Repository: FormidableLabs/victory Branch: main Commit: 75549cf8c67e Files: 981 Total size: 3.2 MB Directory structure: gitextract_8opt94_q/ ├── .babelrc.build.js ├── .babelrc.js ├── .babelrc.native.js ├── .changeset/ │ ├── afraid-donuts-tease.md │ ├── config.json │ ├── cute-foxes-shop.md │ └── silver-crabs-deliver.md ├── .editorconfig ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── feature_request.yml │ │ └── question.yml │ ├── actions/ │ │ ├── bundle-size/ │ │ │ └── action.yml │ │ └── setup/ │ │ └── action.yml │ └── workflows/ │ ├── chromatic.yml │ ├── ci.yml │ ├── issue-stale.yml │ ├── issue-triage.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── .storybook/ │ ├── main.ts │ ├── preview.css │ └── preview.ts ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── chromatic.config.json ├── config/ │ └── webpack/ │ ├── demo/ │ │ ├── webpack.config.dev.js │ │ └── webpack.config.hot.js │ ├── webpack.config.dev.js │ └── webpack.config.js ├── demo/ │ └── rn/ │ ├── .expo-shared/ │ │ └── assets.json │ ├── .gitignore │ ├── App.tsx │ ├── app.json │ ├── babel.config.js │ ├── metro.config.js │ ├── package.json │ ├── src/ │ │ ├── data/ │ │ │ └── index.ts │ │ ├── navigation-config.ts │ │ ├── screens/ │ │ │ ├── area-screen.tsx │ │ │ ├── axis-screen.tsx │ │ │ ├── bar-screen.tsx │ │ │ ├── box-plot-screen.tsx │ │ │ ├── brush-line-screen.tsx │ │ │ ├── chart-screen.tsx │ │ │ ├── components-screen.tsx │ │ │ ├── error-bar-screen.tsx │ │ │ ├── histogram-screen.tsx │ │ │ ├── legends-screen.tsx │ │ │ ├── line-screen.tsx │ │ │ ├── pie-screen.tsx │ │ │ ├── polar-axis-screen.tsx │ │ │ ├── root-navigator.tsx │ │ │ ├── scatter-screen.tsx │ │ │ └── voronoi-screen.tsx │ │ └── styles/ │ │ ├── container-view-styles.ts │ │ └── view-styles.ts │ └── tsconfig.json ├── eslint.config.mjs ├── package-scripts.js ├── package.json ├── packages/ │ ├── victory/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── victory.test.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-area/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── area.test.tsx │ │ │ ├── area.tsx │ │ │ ├── helper-methods.test.tsx │ │ │ ├── helper-methods.tsx │ │ │ ├── index.ts │ │ │ ├── victory-area.test.tsx │ │ │ └── victory-area.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-axis/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helper-methods.tsx │ │ │ ├── index.ts │ │ │ └── victory-axis.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-bar/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── bar-helper-methods.ts │ │ │ ├── bar.test.tsx │ │ │ ├── bar.tsx │ │ │ ├── geometry-helper-methods.test.ts │ │ │ ├── geometry-helper-methods.ts │ │ │ ├── helper-methods.ts │ │ │ ├── index.ts │ │ │ ├── path-helper-methods.ts │ │ │ ├── victory-bar.test.tsx │ │ │ └── victory-bar.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-box-plot/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helper-methods.tsx │ │ │ ├── index.ts │ │ │ ├── victory-box-plot.test.tsx │ │ │ └── victory-box-plot.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-brush-container/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── brush-helpers.test.tsx │ │ │ ├── brush-helpers.ts │ │ │ ├── index.ts │ │ │ └── victory-brush-container.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-brush-line/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── victory-brush-line.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-candlestick/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── candle.test.tsx │ │ │ ├── candle.tsx │ │ │ ├── helper-methods.test.ts │ │ │ ├── helper-methods.ts │ │ │ ├── index.ts │ │ │ ├── victory-candlestick.test.tsx │ │ │ └── victory-candlestick.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-canvas/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── canvas-bar.tsx │ │ │ ├── canvas-curve.tsx │ │ │ ├── canvas-group.tsx │ │ │ ├── canvas-point.tsx │ │ │ ├── hooks/ │ │ │ │ └── use-canvas-context.ts │ │ │ └── index.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-chart/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helper-methods.test.tsx │ │ │ ├── helper-methods.tsx │ │ │ ├── index.ts │ │ │ ├── victory-chart.test.tsx │ │ │ └── victory-chart.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-core/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── exports.test.ts │ │ │ ├── index.ts │ │ │ ├── types/ │ │ │ │ ├── callbacks.ts │ │ │ │ └── prop-types.ts │ │ │ ├── victory-accessible-group/ │ │ │ │ ├── victory-accessible-group.test.tsx │ │ │ │ └── victory-accessible-group.tsx │ │ │ ├── victory-animation/ │ │ │ │ ├── util.test.tsx │ │ │ │ ├── util.ts │ │ │ │ └── victory-animation.tsx │ │ │ ├── victory-clip-container/ │ │ │ │ └── victory-clip-container.tsx │ │ │ ├── victory-container/ │ │ │ │ ├── victory-container.test.tsx │ │ │ │ └── victory-container.tsx │ │ │ ├── victory-label/ │ │ │ │ ├── victory-label.test.tsx │ │ │ │ └── victory-label.tsx │ │ │ ├── victory-portal/ │ │ │ │ ├── portal-context.tsx │ │ │ │ ├── portal-outlet.tsx │ │ │ │ ├── portal.tsx │ │ │ │ └── victory-portal.tsx │ │ │ ├── victory-primitives/ │ │ │ │ ├── arc.tsx │ │ │ │ ├── background.tsx │ │ │ │ ├── border.tsx │ │ │ │ ├── circle.tsx │ │ │ │ ├── clip-path.test.tsx │ │ │ │ ├── clip-path.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── line-segment.tsx │ │ │ │ ├── line.test.tsx │ │ │ │ ├── line.tsx │ │ │ │ ├── path.tsx │ │ │ │ ├── point.test.tsx │ │ │ │ ├── point.tsx │ │ │ │ ├── rect.tsx │ │ │ │ ├── text.tsx │ │ │ │ ├── tspan.tsx │ │ │ │ ├── types.ts │ │ │ │ └── whisker.tsx │ │ │ ├── victory-theme/ │ │ │ │ ├── clean.tsx │ │ │ │ ├── grayscale.tsx │ │ │ │ ├── material.tsx │ │ │ │ ├── types.ts │ │ │ │ └── victory-theme.tsx │ │ │ ├── victory-transition/ │ │ │ │ └── victory-transition.tsx │ │ │ └── victory-util/ │ │ │ ├── add-events.tsx │ │ │ ├── axis.test.tsx │ │ │ ├── axis.tsx │ │ │ ├── collection.test.ts │ │ │ ├── collection.tsx │ │ │ ├── common-props.tsx │ │ │ ├── data.test.tsx │ │ │ ├── data.ts │ │ │ ├── default-transitions.ts │ │ │ ├── domain.test.tsx │ │ │ ├── domain.ts │ │ │ ├── events.test.ts │ │ │ ├── events.ts │ │ │ ├── helpers.test.ts │ │ │ ├── helpers.ts │ │ │ ├── hooks/ │ │ │ │ ├── index.ts │ │ │ │ ├── use-animation-state.ts │ │ │ │ └── use-previous-props.ts │ │ │ ├── immutable-types.d.ts │ │ │ ├── immutable.test.ts │ │ │ ├── immutable.ts │ │ │ ├── index.ts │ │ │ ├── label-helpers.test.tsx │ │ │ ├── label-helpers.ts │ │ │ ├── line-helpers.ts │ │ │ ├── log.ts │ │ │ ├── merge-refs.ts │ │ │ ├── point-path-helpers.test.ts │ │ │ ├── point-path-helpers.ts │ │ │ ├── scale.test.ts │ │ │ ├── scale.ts │ │ │ ├── selection.test.ts │ │ │ ├── selection.ts │ │ │ ├── style.test.ts │ │ │ ├── style.ts │ │ │ ├── textsize.test.ts │ │ │ ├── textsize.ts │ │ │ ├── timer-context.ts │ │ │ ├── timer.ts │ │ │ ├── transitions.test.ts │ │ │ ├── transitions.ts │ │ │ ├── type-helpers.ts │ │ │ ├── user-props.ts │ │ │ ├── wrapper.test.tsx │ │ │ └── wrapper.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-create-container/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── create-container.tsx │ │ │ └── index.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-cursor-container/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── cursor-helpers.tsx │ │ │ ├── index.tsx │ │ │ └── victory-cursor-container.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-errorbar/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── error-bar.test.tsx │ │ │ ├── error-bar.tsx │ │ │ ├── helper-methods.tsx │ │ │ ├── index.ts │ │ │ ├── victory-errorbar.tsx │ │ │ └── victory-errorbars.test.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-group/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helper-methods.tsx │ │ │ ├── index.ts │ │ │ ├── victory-group.test.tsx │ │ │ └── victory-group.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-histogram/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helper-methods.ts │ │ │ ├── index.ts │ │ │ ├── victory-histogram.test.tsx │ │ │ └── victory-histogram.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-legend/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helper-methods.ts │ │ │ ├── index.ts │ │ │ ├── victory-legend.test.tsx │ │ │ └── victory-legend.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-line/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── curve.test.tsx │ │ │ ├── curve.tsx │ │ │ ├── helper-methods.ts │ │ │ ├── index.ts │ │ │ ├── victory-line.test.tsx │ │ │ └── victory-line.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-native/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── components/ │ │ │ │ ├── victory-area.tsx │ │ │ │ ├── victory-axis.tsx │ │ │ │ ├── victory-bar.tsx │ │ │ │ ├── victory-boxplot.tsx │ │ │ │ ├── victory-brush-container.tsx │ │ │ │ ├── victory-brush-line.tsx │ │ │ │ ├── victory-candlestick.tsx │ │ │ │ ├── victory-chart.tsx │ │ │ │ ├── victory-clip-container.tsx │ │ │ │ ├── victory-container.tsx │ │ │ │ ├── victory-cursor-container.tsx │ │ │ │ ├── victory-errorbar.tsx │ │ │ │ ├── victory-group.tsx │ │ │ │ ├── victory-histogram.tsx │ │ │ │ ├── victory-label.tsx │ │ │ │ ├── victory-legend.tsx │ │ │ │ ├── victory-line.tsx │ │ │ │ ├── victory-pie.tsx │ │ │ │ ├── victory-polar-axis.tsx │ │ │ │ ├── victory-portal/ │ │ │ │ │ ├── portal.tsx │ │ │ │ │ └── victory-portal.tsx │ │ │ │ ├── victory-primitives/ │ │ │ │ │ ├── arc.tsx │ │ │ │ │ ├── area.tsx │ │ │ │ │ ├── background.tsx │ │ │ │ │ ├── bar.tsx │ │ │ │ │ ├── border.tsx │ │ │ │ │ ├── candle.tsx │ │ │ │ │ ├── circle.tsx │ │ │ │ │ ├── clip-path.tsx │ │ │ │ │ ├── curve.tsx │ │ │ │ │ ├── error-bar.tsx │ │ │ │ │ ├── flyout.tsx │ │ │ │ │ ├── line-segment.tsx │ │ │ │ │ ├── line.tsx │ │ │ │ │ ├── path.tsx │ │ │ │ │ ├── point.tsx │ │ │ │ │ ├── rect.tsx │ │ │ │ │ ├── slice.tsx │ │ │ │ │ ├── text.tsx │ │ │ │ │ ├── tspan.tsx │ │ │ │ │ ├── types.ts │ │ │ │ │ ├── voronoi.tsx │ │ │ │ │ └── whisker.tsx │ │ │ │ ├── victory-scatter.tsx │ │ │ │ ├── victory-selection-container.tsx │ │ │ │ ├── victory-stack.tsx │ │ │ │ ├── victory-tooltip.tsx │ │ │ │ ├── victory-voronoi-container.tsx │ │ │ │ ├── victory-voronoi.tsx │ │ │ │ └── victory-zoom-container.tsx │ │ │ ├── helpers/ │ │ │ │ ├── create-container.ts │ │ │ │ ├── native-helpers.test.ts │ │ │ │ ├── native-helpers.ts │ │ │ │ ├── native-zoom-helpers.ts │ │ │ │ └── wrap-core-component.tsx │ │ │ └── index.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-pie/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helper-methods.ts │ │ │ ├── index.ts │ │ │ ├── slice.test.tsx │ │ │ ├── slice.tsx │ │ │ ├── victory-pie.test.tsx │ │ │ └── victory-pie.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-polar-axis/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helper-methods.ts │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ └── victory-polar-axis.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-scatter/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helper-methods.test.tsx │ │ │ ├── helper-methods.tsx │ │ │ ├── index.ts │ │ │ ├── victory-scatter.test.tsx │ │ │ └── victory-scatter.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-selection-container/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── selection-helpers.test.tsx │ │ │ ├── selection-helpers.tsx │ │ │ └── victory-selection-container.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-shared-events/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ └── victory-shared-events.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-stack/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helper-methods.tsx │ │ │ ├── index.ts │ │ │ ├── victory-stack.test.tsx │ │ │ └── victory-stack.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-tooltip/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── flyout.test.tsx │ │ │ ├── flyout.tsx │ │ │ ├── index.ts │ │ │ ├── victory-tooltip.test.tsx │ │ │ └── victory-tooltip.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-vendor/ │ │ ├── .babelrc.js │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── scripts/ │ │ │ └── build.js │ │ ├── tests/ │ │ │ ├── d3-array.test.ts │ │ │ ├── d3-ease.test.ts │ │ │ ├── d3-interpolate.test.ts │ │ │ ├── d3-scale.test.ts │ │ │ ├── d3-shape.test.ts │ │ │ ├── d3-time.test.ts │ │ │ └── d3-timer.test.ts │ │ └── tsconfig.json │ ├── victory-voronoi/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── helper-methods.ts │ │ │ ├── index.ts │ │ │ ├── victory-voronoi.test.tsx │ │ │ ├── victory-voronoi.tsx │ │ │ └── voronoi.tsx │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── victory-voronoi-container/ │ │ ├── .npmignore │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── jest.config.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── index.ts │ │ │ ├── victory-voronoi-container.tsx │ │ │ └── voronoi-helpers.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── victory-zoom-container/ │ ├── .npmignore │ ├── CHANGELOG.md │ ├── README.md │ ├── jest.config.ts │ ├── package.json │ ├── src/ │ │ ├── index.ts │ │ ├── victory-zoom-container.tsx │ │ ├── zoom-helpers.test.ts │ │ └── zoom-helpers.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── patches/ │ └── @changesets__cli@2.24.1.patch ├── pnpm-workspace.yaml ├── scripts/ │ ├── changelog.js │ ├── release.ts │ ├── sync-pkgs-wireit-helpers.js │ └── sync-pkgs-wireit.js ├── stories/ │ ├── utils/ │ │ ├── arg-types.tsx │ │ ├── data.ts │ │ └── decorators.tsx │ └── victory-charts/ │ ├── victory-animation/ │ │ ├── config.ts │ │ └── default.stories.tsx │ ├── victory-area/ │ │ ├── config.ts │ │ ├── data-accessors.stories.tsx │ │ ├── default.stories.tsx │ │ ├── disable-inline-styles.stories.tsx │ │ ├── events.stories.tsx │ │ ├── interpolation.stories.tsx │ │ ├── labels.stories.tsx │ │ ├── log-scale.stories.tsx │ │ ├── plotting-functions.stories.tsx │ │ ├── polar-interpolation.stories.tsx │ │ ├── polar.stories.tsx │ │ ├── stacked.stories.tsx │ │ ├── styles.stories.tsx │ │ ├── theme.stories.tsx │ │ ├── time-scale.stories.tsx │ │ └── tooltips.stories.tsx │ ├── victory-axis/ │ │ ├── axis-value.stories.tsx │ │ ├── brush-axis-grid-line-styles.stories.tsx │ │ ├── brush-axis-grid-line-width.stories.tsx │ │ ├── brush-axis-grid-line-with-domain.stories.tsx │ │ ├── brush-axis-grid-line.stories.tsx │ │ ├── brush-axis-with-domain.stories.tsx │ │ ├── brush-axis.stories.tsx │ │ ├── config.ts │ │ ├── default.stories.tsx │ │ ├── fix-label-overlap.stories.tsx │ │ ├── offsets.stories.tsx │ │ ├── orientation.stories.tsx │ │ ├── style.stories.tsx │ │ ├── tick-format.stories.tsx │ │ ├── tick-values.stories.tsx │ │ ├── with-domain.stories.tsx │ │ └── with-multiline-labels.stories.tsx │ ├── victory-bar/ │ │ ├── alignment.stories.tsx │ │ ├── bar-ratio.stories.tsx │ │ ├── bar-width.stories.tsx │ │ ├── config.ts │ │ ├── corner-radius.stories.tsx │ │ ├── data.stories.tsx │ │ ├── default.stories.tsx │ │ ├── disable-inline-styles.stories.tsx │ │ ├── domain.stories.tsx │ │ ├── focus-with-refs.stories.tsx │ │ ├── get-path.stories.tsx │ │ ├── grouped-bars.stories.tsx │ │ ├── labels.stories.tsx │ │ ├── polar-bars.stories.tsx │ │ ├── regressions.stories.tsx │ │ ├── scale.stories.tsx │ │ ├── sorting.stories.tsx │ │ ├── stacked-bars.stories.tsx │ │ ├── style.stories.tsx │ │ ├── theme.stories.tsx │ │ └── tooltips.stories.tsx │ ├── victory-box-plot/ │ │ ├── box-width.stories.tsx │ │ ├── config.ts │ │ ├── data.stories.tsx │ │ ├── default.stories.tsx │ │ ├── disable-inline-styles.stories.tsx │ │ ├── domain.stories.tsx │ │ ├── group.stories.tsx │ │ ├── labels.stories.tsx │ │ ├── style.stories.tsx │ │ ├── theme.stories.tsx │ │ ├── tooltips.stories.tsx │ │ └── whisker-width.stories.tsx │ ├── victory-candlestick/ │ │ ├── candle-colors.stories.tsx │ │ ├── config.ts │ │ ├── data.stories.tsx │ │ ├── default.stories.tsx │ │ ├── disable-inline-styles.stories.tsx │ │ ├── domain.stories.tsx │ │ ├── labels.stories.tsx │ │ ├── scale.stories.tsx │ │ ├── style.stories.tsx │ │ ├── tooltips.stories.tsx │ │ └── wick-stroke-width.stories.tsx │ ├── victory-chart/ │ │ ├── axes.stories.tsx │ │ ├── config.ts │ │ ├── default.stories.tsx │ │ ├── domain-from-data.stories.tsx │ │ ├── domain-padding.stories.tsx │ │ ├── domain.stories.tsx │ │ ├── orientations.stories.tsx │ │ ├── style.stories.tsx │ │ ├── victory-brush-container-default.stories.tsx │ │ ├── victory-brush-container-with-brush-style.stories.tsx │ │ ├── victory-brush-container-with-domain-horizontal.stories.tsx │ │ ├── victory-brush-container-with-domain.stories.tsx │ │ ├── victory-cursor-container-default.stories.tsx │ │ ├── victory-cursor-container-horizontal.stories.tsx │ │ └── victory-zoom-container-default.stories.tsx │ ├── victory-container/ │ │ ├── config.ts │ │ ├── preserve-aspect-ratio.stories.tsx │ │ └── responsive.stories.tsx │ ├── victory-errorbar/ │ │ ├── border-width.stories.tsx │ │ ├── config.ts │ │ ├── data.stories.tsx │ │ ├── default.stories.tsx │ │ ├── disable-inline-styles.stories.tsx │ │ ├── domain.stories.tsx │ │ ├── labels.stories.tsx │ │ ├── style.stories.tsx │ │ └── theme.stories.tsx │ ├── victory-histogram/ │ │ ├── bin-spacing.stories.tsx │ │ ├── config.ts │ │ ├── corner-radius.stories.tsx │ │ ├── data.stories.tsx │ │ ├── data.ts │ │ ├── date-bins.stories.tsx │ │ ├── default.stories.tsx │ │ ├── disable-inline-styles.stories.tsx │ │ ├── domain.stories.tsx │ │ ├── empty-data.stories.tsx │ │ ├── get-path.stories.tsx │ │ ├── labels.stories.tsx │ │ ├── mixed-charts.stories.tsx │ │ ├── numeric-bins.stories.tsx │ │ ├── scale.stories.tsx │ │ ├── stacked.stories.tsx │ │ ├── styles.stories.tsx │ │ └── theme.stories.tsx │ ├── victory-label/ │ │ ├── anchors.stories.tsx │ │ ├── angles.stories.tsx │ │ ├── background-padding.stories.tsx │ │ ├── background-styles.stories.tsx │ │ ├── config.ts │ │ ├── default-rendering.stories.tsx │ │ ├── inline.stories.tsx │ │ ├── line-height.stories.tsx │ │ ├── positioning.stories.tsx │ │ └── styles.stories.tsx │ ├── victory-legend/ │ │ ├── config.ts │ │ ├── default.stories.tsx │ │ ├── line-height.stories.tsx │ │ └── title.stories.tsx │ ├── victory-line/ │ │ ├── config.ts │ │ ├── data-accessors.stories.tsx │ │ ├── default.stories.tsx │ │ ├── disable-inline-styles.stories.tsx │ │ ├── events.stories.tsx │ │ ├── interpolation.stories.tsx │ │ ├── labels.stories.tsx │ │ ├── log-scale.stories.tsx │ │ ├── plotting-functions.stories.tsx │ │ ├── polar-interpolation.stories.tsx │ │ ├── polar.stories.tsx │ │ ├── stacked.stories.tsx │ │ ├── styles.stories.tsx │ │ ├── theme.stories.tsx │ │ ├── time-scale.stories.tsx │ │ └── tooltips.stories.tsx │ ├── victory-pie/ │ │ ├── categories.stories.tsx │ │ ├── config.ts │ │ ├── corner-radius.stories.tsx │ │ ├── data.stories.tsx │ │ ├── default.stories.tsx │ │ ├── disable-inline-styles.stories.tsx │ │ ├── inner-radius.stories.tsx │ │ ├── label-indicator.stories.tsx │ │ ├── label-placement.stories.tsx │ │ ├── label-radius.stories.tsx │ │ ├── labels.stories.tsx │ │ ├── origin.stories.tsx │ │ ├── pad-angle.stories.tsx │ │ ├── radius.stories.tsx │ │ ├── start-and-end-angles.stories.tsx │ │ ├── styles.stories.tsx │ │ ├── theme.stories.tsx │ │ └── tooltips.stories.tsx │ ├── victory-polar-axis/ │ │ ├── axis-angle.stories.tsx │ │ ├── axis-value.stories.tsx │ │ ├── config.ts │ │ ├── default.stories.tsx │ │ ├── domain.stories.tsx │ │ ├── inner-radius.stories.tsx │ │ ├── label-placement.stories.tsx │ │ ├── scale.stories.tsx │ │ ├── start-and-end-angle.stories.tsx │ │ ├── style.stories.tsx │ │ ├── theme.stories.tsx │ │ ├── tick-format.stories.tsx │ │ └── tick-values.stories.tsx │ ├── victory-portal/ │ │ ├── config.ts │ │ └── default.stories.tsx │ ├── victory-scatter/ │ │ ├── bubble-charts.stories.tsx │ │ ├── config.ts │ │ ├── data-accessors.stories.tsx │ │ ├── default-rendering.stories.tsx │ │ ├── disable-inline-styles.stories.tsx │ │ ├── domain.stories.tsx │ │ ├── functional-symbols.stories.tsx │ │ ├── labels.stories.tsx │ │ ├── log-scale.stories.tsx │ │ ├── polar.stories.tsx │ │ ├── stacked.stories.tsx │ │ ├── styles.stories.tsx │ │ ├── symbols.stories.tsx │ │ ├── theme.stories.tsx │ │ ├── time-scale.stories.tsx │ │ └── tooltips.stories.tsx │ └── victory-tooltip/ │ ├── center-offset.stories.tsx │ ├── config.ts │ ├── constrain-to-visible-area.stories.tsx │ ├── corner-radius.stories.tsx │ ├── default.stories.tsx │ ├── disable-inline-styles.stories.tsx │ ├── flyout-height.stories.tsx │ ├── flyout-padding.stories.tsx │ ├── flyout-style.stories.tsx │ ├── flyout-width.stories.tsx │ ├── pointer-length.stories.tsx │ ├── pointer-orientation.stories.tsx │ └── pointer-width.stories.tsx ├── test/ │ ├── helpers/ │ │ ├── index.ts │ │ ├── svg.ts │ │ └── wrappers.tsx │ ├── jest-setup.ts │ ├── jest.config.ts │ └── tsconfig.json ├── tsconfig.base.json ├── tsconfig.json ├── tsconfig.storybook.json ├── vercel.json └── website/ ├── .gitignore ├── README.md ├── babel.config.js ├── docs/ │ ├── api/ │ │ ├── _category_.json │ │ ├── victory-accessible-group.mdx │ │ ├── victory-animation.mdx │ │ ├── victory-area.mdx │ │ ├── victory-axis-common-props.mdx │ │ ├── victory-axis.mdx │ │ ├── victory-bar.mdx │ │ ├── victory-boxplot.mdx │ │ ├── victory-brush-container.mdx │ │ ├── victory-brush-line.mdx │ │ ├── victory-candlestick.mdx │ │ ├── victory-canvas.mdx │ │ ├── victory-chart.mdx │ │ ├── victory-clip-container.mdx │ │ ├── victory-common-props.mdx │ │ ├── victory-common-theme-props.mdx │ │ ├── victory-container-props.mdx │ │ ├── victory-container.mdx │ │ ├── victory-cursor-container.mdx │ │ ├── victory-datatable-props.mdx │ │ ├── victory-error-bar.mdx │ │ ├── victory-group.mdx │ │ ├── victory-histogram.mdx │ │ ├── victory-label.mdx │ │ ├── victory-labelable-props.mdx │ │ ├── victory-legend.mdx │ │ ├── victory-line.mdx │ │ ├── victory-multi-labelable-props.mdx │ │ ├── victory-pie.mdx │ │ ├── victory-polar-axis.mdx │ │ ├── victory-portal.mdx │ │ ├── victory-primitives.mdx │ │ ├── victory-scatter.mdx │ │ ├── victory-selection-container.mdx │ │ ├── victory-shared-events.mdx │ │ ├── victory-single-labelable-props.mdx │ │ ├── victory-stack.mdx │ │ ├── victory-style-interface.mdx │ │ ├── victory-theme.mdx │ │ ├── victory-tooltip.mdx │ │ ├── victory-transition.mdx │ │ ├── victory-voronoi-container.mdx │ │ ├── victory-voronoi.mdx │ │ └── victory-zoom-container.mdx │ ├── charts/ │ │ ├── _category_.json │ │ ├── area.mdx │ │ ├── bar.mdx │ │ ├── box-plot.mdx │ │ ├── candlestick.mdx │ │ ├── error-bar.mdx │ │ ├── histogram.mdx │ │ ├── line.mdx │ │ ├── pie.mdx │ │ ├── scatter.mdx │ │ └── voronoi.mdx │ ├── examples/ │ │ ├── _category_.json │ │ ├── anim-happy-holidays.mdx │ │ ├── area-hover.mdx │ │ ├── area-stream-graph.mdx │ │ ├── axis-parallel-brush.mdx │ │ ├── bar-horizontal-stacked.mdx │ │ ├── bar-linked-brushing.mdx │ │ ├── custom-charts.mdx │ │ ├── histogram-with-slider.mdx │ │ ├── polar-progress-bar.mdx │ │ └── voronoi-tooltips-grouped.mdx │ ├── guides/ │ │ ├── _category_.json │ │ ├── accessibility.mdx │ │ ├── animations.mdx │ │ ├── annotations.mdx │ │ ├── axis.mdx │ │ ├── containers.mdx │ │ ├── custom-components.mdx │ │ ├── data-accessors.mdx │ │ ├── data-selection.mdx │ │ ├── events.mdx │ │ ├── legends.mdx │ │ ├── localization.mdx │ │ ├── pan-and-zoom.mdx │ │ ├── polar-charts.mdx │ │ ├── themes.mdx │ │ ├── tooltips.mdx │ │ └── zoom-large-data.mdx │ └── introduction/ │ ├── _category_.json │ ├── index.mdx │ ├── native.mdx │ └── ssr.mdx ├── docusaurus.config.ts ├── package.json ├── sidebars.ts ├── src/ │ ├── components/ │ │ ├── CalloutBanner.tsx │ │ ├── LearnMoreLink.tsx │ │ ├── SidebarLeadBanner.tsx │ │ ├── badges.tsx │ │ ├── button.tsx │ │ ├── common-props.tsx │ │ ├── link-button.tsx │ │ └── slider.tsx │ ├── css/ │ │ └── custom.css │ ├── hooks/ │ │ ├── useClickOutside.tsx │ │ └── useLocalStorage.tsx │ ├── pages/ │ │ ├── _components/ │ │ │ ├── data/ │ │ │ │ ├── downloads.js │ │ │ │ ├── update-downloads.js │ │ │ │ ├── update-versions.js │ │ │ │ └── versions.js │ │ │ ├── landing-banner.tsx │ │ │ ├── landing-demo.tsx │ │ │ ├── landing-divider.tsx │ │ │ ├── landing-featured-projects.tsx │ │ │ ├── landing-features.tsx │ │ │ ├── landing-hero.tsx │ │ │ ├── landing-showcase.tsx │ │ │ └── theme.ts │ │ ├── index.tsx │ │ └── themes/ │ │ ├── _components/ │ │ │ ├── accordion.tsx │ │ │ ├── alert.tsx │ │ │ ├── base-theme-panel.tsx │ │ │ ├── card.tsx │ │ │ ├── chart-panel.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── code-block.tsx │ │ │ ├── code-panel.tsx │ │ │ ├── color-palette-selector.tsx │ │ │ ├── color-picker-list.tsx │ │ │ ├── color-picker.tsx │ │ │ ├── color-scale-override-selector.tsx │ │ │ ├── control.tsx │ │ │ ├── examples/ │ │ │ │ ├── area.tsx │ │ │ │ ├── axis.tsx │ │ │ │ ├── bar.tsx │ │ │ │ ├── box-plot.tsx │ │ │ │ ├── candlestick.tsx │ │ │ │ ├── errorbar.tsx │ │ │ │ ├── example.ts │ │ │ │ ├── group.tsx │ │ │ │ ├── histogram.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── legend.tsx │ │ │ │ ├── line.tsx │ │ │ │ ├── pie.tsx │ │ │ │ ├── polar-axis-dependent.tsx │ │ │ │ ├── polar-axis.tsx │ │ │ │ ├── scatter.tsx │ │ │ │ ├── stack.tsx │ │ │ │ └── voronoi.tsx │ │ │ ├── export-panel.tsx │ │ │ ├── main.tsx │ │ │ ├── options-panel.tsx │ │ │ ├── panel-header.tsx │ │ │ ├── select.tsx │ │ │ ├── sideNavButton.tsx │ │ │ ├── sidenav.tsx │ │ │ ├── slider.tsx │ │ │ ├── theme-preview/ │ │ │ │ ├── header.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── options.tsx │ │ │ │ ├── preview-color-scale-select.tsx │ │ │ │ └── preview.tsx │ │ │ └── toggle.tsx │ │ ├── _config/ │ │ │ ├── axis.tsx │ │ │ ├── chart.tsx │ │ │ ├── global.tsx │ │ │ ├── index.tsx │ │ │ └── palette.tsx │ │ ├── _const.tsx │ │ ├── _icons/ │ │ │ ├── axis-options-icon.tsx │ │ │ ├── chart-options-icon.tsx │ │ │ ├── export-icon.tsx │ │ │ └── index.tsx │ │ ├── _providers/ │ │ │ ├── alertProvider.tsx │ │ │ ├── previewOptionsProvider.tsx │ │ │ ├── sideNavProvider.tsx │ │ │ └── themeProvider.tsx │ │ ├── _utils.ts │ │ └── index.tsx │ ├── plugins/ │ │ └── victory-typedoc/ │ │ ├── components/ │ │ │ └── api-property.tsx │ │ ├── index.ts │ │ ├── mdast.ts │ │ └── typedoc.ts │ └── theme/ │ ├── DocItem/ │ │ └── index.tsx │ ├── DocSidebar/ │ │ └── index.tsx │ ├── MDXComponents.ts │ ├── Playground/ │ │ ├── index.tsx │ │ └── styles.module.css │ ├── README.md │ ├── ReactLiveScope/ │ │ ├── index.tsx │ │ └── scope-map.ts │ ├── prism-diff-highlight.css │ ├── prism-diff-highlight.ts │ └── prism-include-languages.ts ├── static/ │ ├── .nojekyll │ └── favicon/ │ ├── browserconfig.xml │ └── site.webmanifest ├── tailwind.config.ts ├── tsconfig.json └── vendors.d.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc.build.js ================================================ module.exports = { extends: "./.babelrc.js", ignore: [/^.*(.test.)[j|t]sx?$/], }; ================================================ FILE: .babelrc.js ================================================ module.exports = { presets: [ [ "@babel/preset-env", { loose: true, exclude: [ // only enabled in commonjs targets // see the section on `env` below "@babel/plugin-transform-modules-commonjs", // do not use this plugin with webpack // ref: https://babeljs.io/docs/babel-plugin-proposal-dynamic-import "@babel/plugin-proposal-dynamic-import", ], }, ], "@babel/preset-react", "@babel/preset-typescript", ], ignore: ["**/*.d.ts"], env: { commonjs: { plugins: [ [ "@babel/transform-modules-commonjs", { strict: false, allowTopLevelThis: true, }, ], ], }, }, }; ================================================ FILE: .babelrc.native.js ================================================ module.exports = { presets: ["module:metro-react-native-babel-preset"], plugins: [ "@babel/plugin-transform-export-namespace-from", "@babel/plugin-transform-flow-strip-types", ], }; ================================================ FILE: .changeset/afraid-donuts-tease.md ================================================ --- "victory-errorbar": patch --- add default borderwidth value ================================================ FILE: .changeset/config.json ================================================ { "$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json", "changelog": ["../scripts/changelog.js", { "repo": "FormidableLabs/victory" }], "access": "public", "baseBranch": "main", "fixed": [["victory*"]], "ignore": ["rn-demo", "victory-docs"] } ================================================ FILE: .changeset/cute-foxes-shop.md ================================================ --- "victory-native": patch --- fix native container prop forwarding ================================================ FILE: .changeset/silver-crabs-deliver.md ================================================ --- "victory-candlestick": minor --- Handle undefined labelStyle properties in VictoryCandlestick. Fixes #3039 ================================================ FILE: .editorconfig ================================================ # 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 max_line_length = 80 [*.md] trim_trailing_whitespace = false ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: Bug report description: Create a report to help us improve projects: FormidableLabs/38 labels: ["Type: Bug :bug:"] body: - type: markdown attributes: value: | Thanks for taking the time to report a bug! Please fill out the sections below. - type: checkboxes attributes: label: Is there an existing issue for this? description: Please search to see if an issue already exists for the bug you encountered. options: - label: I have searched the existing issues required: true - type: checkboxes id: terms attributes: label: Code of Conduct description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/FormidableLabs/victory/blob/main/CONTRIBUTING.md#contributor-covenant-code-of-conduct) options: - label: I agree to follow this project's Code of Conduct required: true - type: input attributes: label: Victory version validations: required: true - type: input attributes: label: Code Sandbox link description: | Please include a code sandbox link or a similar reproduction if possible. Issues with sandbox links are typically resolved faster. You can use our preset [here](https://codesandbox.io/s/dj4f7t). - type: textarea attributes: render: markdown label: Bug report description: | A clear and concise description of what the bug is. Please include any screenshots or code snippets that may help us understand the issue. validations: required: true - type: textarea attributes: render: markdown label: Steps to reproduce placeholder: | Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error - type: textarea attributes: render: markdown label: Expected behavior description: A clear and concise description of what you expected to happen. - type: textarea attributes: render: markdown label: Actual behavior description: A clear and concise description of what actually happened. - type: textarea attributes: render: markdown label: Environment description: | examples: - **Device**: Desktop - **OS**: Ubuntu 20.04 - **Browser**: Chrome - **Version**: 22 value: | - Device: - OS: - Node: - npm: ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Victory Issues Board url: https://github.com/FormidableLabs/victory/issues about: Please ask and answer questions here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: Feature request description: Suggest a feature for this project projects: FormidableLabs/38 labels: ["Type: Enhancement :pencil2:"] body: - type: checkboxes attributes: label: Is there an existing issue for this? description: Please search to see if an issue already exists for the feature you are requesting. options: - label: I have searched the existing issues required: true - type: checkboxes id: terms attributes: label: Code of Conduct description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/FormidableLabs/victory/blob/main/CONTRIBUTING.md#contributor-covenant-code-of-conduct) options: - label: I agree to follow this project's Code of Conduct required: true - type: textarea attributes: render: markdown label: Feature Request description: | **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/question.yml ================================================ name: Question description: Ask a question about using Victory. projects: FormidableLabs/38 labels: ["Type: Question :grey_question:"] body: - type: checkboxes attributes: label: Is there an existing issue for this? description: Please search to see if an issue already exists for the question you have. options: - label: I have searched the existing issues required: true - type: checkboxes id: terms attributes: label: Code of Conduct description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/FormidableLabs/victory/blob/main/CONTRIBUTING.md#contributor-covenant-code-of-conduct) options: - label: I agree to follow this project's Code of Conduct required: true - type: textarea attributes: render: markdown label: Question description: | Any background information that might help us answer your questions. Please include any screenshots or code snippets that may help us understand the issue. validations: required: true ================================================ FILE: .github/actions/bundle-size/action.yml ================================================ name: "Bundle size reporter" description: "Post bundle size difference compared to another branch" inputs: branch: description: 'Branch to compare to' required: true default: 'main' paths: description: "Paths to json file bundle size report or folder containing bundles" required: true default: "/" onlyDiff: description: "Report only different sizes" required: false default: "false" filter: description: "Regex filter based on file path" required: false unit: description: "Size unit" required: false default: "KB" # Comment inputs comment: description: "Post comment" required: false default: "true" header: description: "Comment header" required: false default: "Bundle size report" append: description: "Append comment" required: false default: "false" ghToken: description: "Github token" required: false runs: using: "composite" steps: # Checkout branch to compare to [required] - name: Checkout base branch uses: actions/checkout@v4 with: ref: ${{ inputs.branch }} path: br-base # build main under it's checkout location, in ./br-base - name: Build main shell: bash run: cd br-base && pnpm install && pnpm build # Generate the bundle size difference report [required] - name: Generate report id: bundleSize uses: nejcm/bundle-size-reporter-action@v1.4.1 with: paths: ${{ inputs.paths }} onlyDiff: ${{ inputs.onlyDiff }} # Post github action summary - name: Post summary if: ${{ steps.bundleSize.outputs.hasDifferences == 'true' }} # post only in case of changes run: | echo '${{ steps.bundleSize.outputs.summary }}' >> $GITHUB_STEP_SUMMARY shell: bash # Post github action comment - name: Post comment uses: marocchino/sticky-pull-request-comment@v2 if: ${{ steps.bundleSize.outputs.hasDifferences == 'true' }} # post only in case of changes with: number: ${{ github.event.pull_request.number }} header: ${{ inputs.header }} append: ${{ inputs.append }} message: "${{ steps.bundleSize.outputs.summary }}" ================================================ FILE: .github/actions/setup/action.yml ================================================ name: Setup description: Setup Build Step inputs: node-version: default: "18.x" description: "The version of nodejs to use." required: true runs: using: "composite" steps: - uses: pnpm/action-setup@v4 - name: Use Node.js uses: actions/setup-node@v4 with: node-version: ${{ inputs.node-version }} cache: 'pnpm' # Wireit cache - uses: google/wireit@setup-github-actions-caching/v1 - name: Install dependencies shell: bash run: pnpm install ================================================ FILE: .github/workflows/chromatic.yml ================================================ name: Chromatic # Runs chromatic on: # - every push to main (to create a chromatic baseline) # - every pull request where there are changes in stories/ or packages/ and the PR is not in draft mode on: push: branches: - main pull_request: branches: - main paths: - "stories/**" - "packages/**" jobs: chromatic: name: Storybook Publish if: github.event.pull_request.draft == false && github.repository == 'FormidableLabs/victory' runs-on: ubuntu-latest steps: # requires all branches and tags to be fetched for chromatic - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ./.github/actions/setup with: node-version: 18.x - name: Build Victory run: pnpm run build:lib:esm - name: Publish to Chromatic uses: chromaui/action@v1 with: token: ${{ secrets.GITHUB_TOKEN }} projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI # Runs build and test on: # every push to main # every pull request with main branch as the base on: push: branches: - main pull_request: branches: - main jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup - name: Check all package.json's and tsconfig.json's are in sync. run: | pnpm sync git diff --no-ext-diff --quiet --exit-code - name: Build libraries and distributions run: pnpm build - name: Types run: pnpm types:check - name: 📄 Bundle size report uses: ./.github/actions/bundle-size # path to composite action with: paths: "packages/victory/dist/victory.min.js" onlyDiff: "true" header: "Bundle size report" # PR comment header lint: needs: [build] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup - name: Lint run: pnpm lint test: needs: [build] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/setup - name: Test run: pnpm jest ================================================ FILE: .github/workflows/issue-stale.yml ================================================ name: Close inactive issues on: workflow_dispatch: schedule: - cron: "0 0 * * *" jobs: close-issues: runs-on: ubuntu-22.04 permissions: contents: write issues: write pull-requests: write steps: - uses: actions/stale@v9 with: ascending: false operations-per-run: 100 days-before-issue-stale: 90 days-before-issue-close: 7 stale-issue-label: 'Issue: Stale' exempt-issue-labels: 'Issue: Accepted' stale-issue-message: 'This issue is stale because it has been open for 90 days with no activity. If there is no activity in the next 7 days, the issue will be closed.' close-issue-message: 'This issue was closed because it has been inactive for 7 days since being marked as stale. Please open a new issue if you believe you are encountering a related problem.' days-before-pr-stale: -1 days-before-pr-close: -1 enable-statistics: true ================================================ FILE: .github/workflows/issue-triage.yml ================================================ name: Issue Triage on: issues: types: [labeled] jobs: needs-repro: runs-on: ubuntu-22.04 if: "${{ contains(github.event.label.name, 'Status: Needs More Info :hand:') }}" steps: - uses: actions/github-script@v7 with: github-token: ${{ secrets.CHANGESETS_GITHUB_TOKEN }} script: | github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: `Hi there! It looks like your issue requires a minimal reproducible example, but it is invalid or absent. Please prepare such an example and share it with us. **The best way to get attention to your issue is to provide a clean and easy way for a developer to reproduce the issue on their own machine.** Please do not provide your entire project, or a project with more code than is necessary to reproduce the issue. A side benefit of going through the process of narrowing down the minimal amount of code needed to reproduce the issue is that you may get lucky and discover that the bug is due to a mistake in your application code that you can quickly fix on your own. ### Resources - ["How to create a Minimal, Reproducible Example"](https://stackoverflow.com/help/minimal-reproducible-example) - ["Codesandbox Starterkit for Victory"](https://codesandbox.io/p/sandbox/victory-starter-dj4f7t) ### Common concerns #### "I didn't have time to create one" That's understandable, it can take some time to prepare. We ask that you hold off on filing an issue until you are able to fully complete the required fields in the issue template. #### "You can reproduce it by yourself by creating a project and following these steps" This is useful knowledge, but it's still valuable to have the resulting project that is produced from running the steps, where you have verified you can reproduce the issue. `}) ================================================ FILE: .github/workflows/release.yml ================================================ name: Release on: push: branches: - main jobs: release: runs-on: ubuntu-latest permissions: contents: write id-token: write issues: write repository-projects: write deployments: write packages: write pull-requests: write steps: - uses: actions/checkout@v4 with: token: ${{ secrets.CHANGESETS_TOKEN }} - uses: ./.github/actions/setup - name: Build packages run: pnpm run build - name: PR or Publish id: changesets uses: changesets/action@v1 with: version: pnpm run version publish: pnpm run publish env: GITHUB_TOKEN: ${{ secrets.CHANGESETS_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # Note: We run a custom release notes script so we can generate a single aggregate # release notes file for all packages. This can be removed when `changesets` supports # this feature natively. - name: Github Release notes if: steps.changesets.outputs.published == 'true' run: npm run release-notes env: GITHUB_TOKEN: ${{ secrets.CHANGESETS_TOKEN }} ================================================ FILE: .gitignore ================================================ ### SublimeText ### *.sublime-workspace ### IntelliJ / WebStorm ### .idea/ ### OSX ### .AppleDouble .DS_Store .LSOverride Icon # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .tmp .Trashes ### Windows ### # Windows image file caches ehthumbs.db Thumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # App specific .vscode bower_components coverage dist lib es node_modules .pnpm-debug.log* npm-debug.log* lerna-debug.log* yarn-error.log* .cache storybook-static build-storybook.log* artifacts tmp # Wireit + caches .wireit .eslintcache # Ignore all yarn2+ .yarn/* .yarnrc* .pnp.* ================================================ FILE: .npmrc ================================================ auto-install-peers=true link-workspace-packages=deep prefer-workspace-packages=true ================================================ FILE: .prettierignore ================================================ .cache .changeset .docusaurus .expo .storybook .wireit .vscode coverage dist build lib lib-vendor es packages/victory-vendor/d3-* node_modules .pnpm-debug.log* npm-debug.log* lerna-debug.log* yarn-error.log* lerna.json tsconfig.json tsconfig.*.json storybook-static public artifacts ================================================ FILE: .prettierrc.json ================================================ { "arrowParens": "always", "trailingComma": "all", "printWidth": 80, "overrides": [ { "files": "*.mdx", "options": { "printWidth": 40 } } ] } ================================================ FILE: .storybook/main.ts ================================================ import type { StorybookConfig } from "@storybook/react-webpack5"; /* globals __dirname:false */ const path = require("path"); const ROOT = path.resolve(__dirname, ".."); const STORIES = path.resolve(ROOT, "stories"); const getAbsolutePath = (packageName: string): any => path.dirname(require.resolve(path.join(packageName, 'package.json'))); const config: StorybookConfig = { addons: [ getAbsolutePath("@storybook/addon-essentials"), { name: "@storybook/addon-storysource", options: { rule: { test: [/\.stories\.(jsx?|tsx?)$/], include: [STORIES], }, loaderOptions: { prettierConfig: { printWidth: 80, singleQuote: false }, }, }, }, getAbsolutePath("@storybook/addon-webpack5-compiler-swc"), getAbsolutePath("@chromatic-com/storybook"), ], framework: { name: getAbsolutePath("@storybook/react-webpack5"), options: { builder: {}, }, }, stories: ["../stories/**/*.stories.tsx"], typescript: { check: false, checkOptions: { typescript: { configFile: path.resolve(ROOT, "tsconfig.storybook.json"), }, }, }, webpackFinal: async (config) => { if (config.resolve) { config.resolve.alias = { ...config.resolve.alias, "@": path.resolve(__dirname, "../packages"), }; } return config; }, }; export default config; ================================================ FILE: .storybook/preview.css ================================================ .VictoryContainer { border: 1px solid #ccc; } /** Custom styles for the visual style tests */ .fill-purple { fill: rgb(87, 4, 143); } .fill-green { fill: rgb(33, 212, 9); } .stroke-purple { stroke: rgb(87, 4, 143); } .stroke-green { stroke: rgb(33, 212, 9); } ================================================ FILE: .storybook/preview.ts ================================================ import { Preview } from "@storybook/react"; import "./preview.css"; const preview: Preview = { args: { height: 350, themeKey: "grayscale", width: 400, }, parameters: { controls: { sort: 'alpha' }, }, }; export default preview; ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing ## How to Contribute Victory is open to pull requests, issue reports, and questions from the community. Here are some good ways to get help if you need it. - If you have a question, please [open a new Q&A discussion thread](https://github.com/FormidableLabs/victory/discussions/new) - If you think you have found a bug, [open a new issue](https://github.com/FormidableLabs/victory/issues/new) If you are a new contributor looking to learn more about Victory, check out our [good first issues board](https://github.com/FormidableLabs/victory/projects/2). ### Current goals and initiatives There are some parts of Victory that are in need of a little extra attention right now, including state management, transitions and animations, and pan and zoom behavior. These larger goals are being tracked in [milestones](https://github.com/FormidableLabs/victory/milestones). The goal for these milestones is re-work parts of Victory to make them more performant, easier to fix, and more accessible for new contributors. If your issue is related to one of these milestones, we may not be able to push up a fix right away, but we will label it accordingly and address it as a part of this larger scope of work. ## Monorepo! Victory is a monorepo built with [Wireit](https://github.com/google/wireit) and [pnpm](https://pnpm.io/) workspaces. All `victory-*` packages live in the `packages` directory, and each has its own `package.json`. Installing this repo with `pnpm install` will automatically link all interdependent `victory-*` packages. **You must use `pnpm` rather than `npm` or `yarn` when installing and running `package.json` scripts in this project.** ### Requirements - [Node.js](https://nodejs.org/) 18 or higher. - [pnpm](https://pnpm.io/) version specified by [corepack](https://github.com/nodejs/corepack) in the `package.json`. ### Setup Clone this repo: ```sh $ git clone https://github.com/FormidableLabs/victory.git $ cd victory ``` Enable corepack (if not already): ```sh $ corepack enable ``` Use [pnpm](https://pnpm.io/) to install dependencies: ```sh $ pnpm install ``` ## Development ### Dev demo app We have some dev servers available for a sample development environment. > Note: The demo app is deprecated, all development should occur in storybook or the docs ```sh # watch mode / HMR $ pnpm storybook:dev # storybook standalone $ pnpm storybook:start ``` ### Running Docs locally You can run the documentation website locally with the following command. It is linked to the Victory package via PNPM and will reflect changes in packages when they are rebuilt. ```sh $ pnpm start:docs ``` ### Build and checks Our task system mostly takes care of all task dependencies and things you need. When you first clone this repo or a new branch, run: ```sh # Run all checks. Re-run this command for your normal workflow. $ pnpm run check # ... or add in a `--watch` to watch & re-run checks for only what you change! $ pnpm run check --watch # Build libraries and UMD distributions. # Really only needed to double-check the webpack build still works. $ pnpm run build # ... or add in a `--watch` to watch & re-run the parts of the build that changed! $ pnpm run build --watch ``` This will do all the build, seeding the task cache so subsequent tasks are fast, and checks that everything is correctly working. Your Victory workflow could reasonably just be (1) making some changes to files + tests, and then (2) re-running `pnpm run check`! Here are some other useful tasks (with or without a `--watch` flag): ```sh # Quality checks $ pnpm run format $ pnpm run format --watch $ pnpm run lint $ pnpm run lint --watch $ pnpm run types:check $ pnpm run types:check --watch # Tests $ pnpm run jest $ pnpm run jest --watch ``` We also have some helper tasks to fix issues that are fixable. ```sh $ pnpm run format:fix $ pnpm run lint:fix ``` ### Victory Native To develop against `victory-native`, please see the package [README](./packages/victory-native/README.md). ### Tips and tricks #### Scripts can be run from the _root_ AND from inside any _package_ folder For example, when working on a single package like `victory-core`, you can run `pnpm run check --watch` from the `packages/victory-core` directory, and it will only check the core. Example: ```sh $ cd packages/victory-core $ pnpm run check --watch ``` This is especially helpful when you're making changes to any package that is _depended upon_, like `victory-core` or `victory-vendor`, and don't want to run every single script during development. #### My IDE shows outdated TypeScript errors It seems like VS Code and WebStorm both struggle to update their internal cache, whenever the **built types** change. For example, when making changes to `victory-core`, the TypeScript changes won't be picked up by your IDE automatically. Instead of restarting your IDE completely, try restarting the TypeScript Service. #### My computer grinds to a halt! The initial build/check, or one where something that is part of a lot of cache keys changes, can really slow down your computer, especially if you've got an older model. To allow you to do other work on your computer at the same time, consider using the `WIREIT_PARALLEL=` environment variable like: ```sh $ WIREIT_PARALLEL=4 pnpm run check ``` A good rubric is "number of cores" for max speed while still a mostly usable system or one less than that number for a much more usable system. #### Unit of work/caching Wireit is a flexible tool that caches at the task level. So that means that out-of-the box any wireit task will run the entire task again if any of the input `files` change. This leads us to two tips: 1. **Decompose tasks to package level**: Jest could be run over the whole monorepo in one command, but we instead break it out per-package, so that we only re-run Jest tests for packages that have actually changed (or have dependencies that have changed). 2. **Use tool-specific caching**: Tools like eslint and tsc can cache within a subtask run, so we like to leverage this within single tasks to make subtask re-runs faster. #### What happens if a check/subtask fails? The neat thing about wireit caching, is that for any high-level task, all of the sub-tasks that succeeded don't need to be re-run. So, if you're trying to run `pnpm run check` and get a single package lint error, just fix that package lint error and run `pnpm run check` again -- and then repeat until you get a pass! All of the work along the way that _succeeds_ will be cached and won't be run again! #### What should be a package script? What should be a wireit script? If you look at our `package.json:scripts.start` command, you'll notice that we use both a wireit-based script (`pnpm run build:lib:esm`) as well as a normal shell command (`webpack serve ...`). This is a good example of the types of things that should and shouldn't be wireit script tasks. 1. **Wireit tasks**: Tasks that should run on input file **changes** and then not run again should be wireit tasks. E.g. "transpile files", "lint files". 2. **Normal script tasks**: Tasks that should _always_ run regardless of the state of cache task execution. E.g. "start a webpack dev server". #### Cache issues We use tools caching within subtasks wherever we can. However, that can sometimes lead to weird errors. Here are some familiar ones with remedies. *Everything* If you want to make sure globally you're not hitting a cache issue, this command cleans the wireit cache as well as all the tool caches: ```sh $ pnpm run clean:cache ``` Your next `pnpm run build|check` will be a full (long) rebuild from scratch. *Eslint* If you hit something like: ``` /PATH/TO/victory/test/jest-setup.ts 0:0 error Parsing error: Debug Failure. False expression: /PATH/TO/victory/packages/victory-native/node_modules/victory-area/es/index.js linked to nonexistent file /PATH/TO/victory/packages/victory-area/es/index.js ✖ 1 problem (1 error, 0 warnings) ``` Then you've hit an eslint issue that can be fixed with: ```sh $ pnpm run clean:cache:lint ``` ## Authoring tasks Our task system is optimized for fast, easy developer experience, at the acknowledged cost of **extra maintainer burden** when we change task structure, add tasks, etc. If you are editing the scripts in a `package.json` or `package-scripts.js` you'll need to read up on [Wireit](https://github.com/google/wireit) and probably want to talk to an existing Victory maintainer. We use three tools and some custom scripts as follows: - `pnpm` to `run` or `exec` scripts - `wireit` to cache tasks and run dependent tasks. - `nps` to place one off script tasks in the root `/project-scripts.js` in a manner that can be called from within a workspace. - `scripts/sync-pkgs-wireit*.js`: Scripts run with `pnpm run sync` to automate dependency management. This is where most of your work for task management will take place. For our packages, we primarily focus on four types of package.json files: - `package.json`: Define root tasks here and aggregate workspace tasks here. Note that instead of relying on `pnpm -r run` to concurrently run tasks in each workspace, we instead rely on `wireit` alone to have dependencies in aggregate tasks on all subtasks. This is more efficient to have `wireit` command concurrency and the task dependency graph. - `packages/victory*/package.json`: These package scripts are generated by `scripts/sync-pkgs-wireit-helpers.js`. If you want to change them, edit that script and run `pnpm run sync`. - `packages/victory-native/package.json`: A custom package.json that must have implementations or no-ops for all things in `victory-core`. - `packages/victory-vendor/package.json`: A custom package.json that must have implementations or no-ops for all things in `victory-core`. ## Visual Tests Victory relies heavily on visual regression testing with [Storybook](https://storybook.js.org/) and [Chromatic](https://www.chromaticqa.com/). Write visual tests for new features by adding them in the `stories` directory. Run storybooks and check out changes. Storybooks are served from http://localhost:6006/ ```sh $ pnpm run storybook:server ``` This task also watches and rebuilds all Victory source files so you can more easily develop against storybook. [Chromatic](https://www.chromaticqa.com/) provides automated visual testing. All internal PRs will trigger a new Chromatic build, which will be displayed along with CI status. Chromatic builds for Victory may be viewed in more detail here: https://www.chromaticqa.com/builds?appId=5b4acf7c54c0490024d5980b. Chromatic requires a secret app code to run, so PRs from external contributors will not automatically trigger a Chromatic build. For this reason, changes from external contributors will be checked out and opened as separate PRs so Chromatic may be used to verify any changes. Developers with access to the secret app code may also trigger a chromatic build manually with: ```sh $ pnpm run chromatic ``` Note that Chromatic internally runs `npm run build-storybook` around which we have a custom `package.json:scripts.build-storybook` task that is meant to work within Chromatic. ## Release We use [changesets](https://github.com/changesets/changesets) to create package versions and publish them. ### Using changesets Our official release path is to use automation to perform the actual publishing of our packages. The steps are to: 1. A human developer adds a changeset. Ideally this is as a part of a PR that will have a version impact on a package. 2. On merge of a PR our automation system opens a "Version Packages" PR. 3. On merging the "Version Packages" PR, the automation system publishes the packages. Here are more details: ### Add a changeset When you would like to add a changeset (which creates a file indicating the type of change), in your branch/PR issue this command: ```sh $ pnpm run changeset ``` to produce an interactive menu. Navigate the packages with arrow keys and hit `` to select 1+ packages. Hit `` when done. Select semver versions for packages and add appropriate messages. From there, you'll be prompted to enter a summary of the change. Some tips for this summary: 1. Aim for a single line, 1+ sentences as appropriate. 2. Include issue links in GH format (e.g. `#123`). 3. You don't need to reference the current pull request or whatnot, as that will be added later automatically. After this, you'll see a new uncommitted file in `.changesets` like: ```sh $ git status # .... Untracked files: (use "git add ..." to include in what will be committed) .changeset/flimsy-pandas-marry.md ``` Review the file, make any necessary adjustments, and commit it to source. When we eventually do a package release, the changeset notes and version will be incorporated! ### Creating versions On a merge of a feature PR, the changesets GitHub action will open a new PR titled `"Version Packages"`. This PR is automatically kept up to date with additional PRs with changesets. So, if you're not ready to publish yet, just keep merging feature PRs and then merge the version packages PR later. ### Publishing packages On the merge of a version packages PR, the changesets GitHub action will publish the packages to npm. ### The manual version For exceptional circumstances, here is a quick guide to manually publishing from a local computer using changesets. 1. Add a changeset with `pnpm run changeset`. Add changeset file, review file, tweak, and commit. 2. Make a version. Due to our changelog plugin you will need to create a personal GitHub token and pass it to the environment. ```sh $ GITHUB_TOKEN= pnpm run version ``` Review git changes, tweak, and commit. 3. Publish. First, build necessary files: ```sh # Build everything $ pnpm run build ``` Then publish: ```sh # Test things out first $ pnpm -r publish --dry-run # The real publish # This first does a single git tag (if not already present), then publishes $ pnpm run publish --otp= ``` Note that publishing multiple packages via `changeset` to npm with an OTP code can often fail with `429 Too Many Requests` rate limiting error. Take a 5+ minute coffee break, then come back and try again. Then issue the following to also push git tags: ```sh $ git push && git push --tags ``` ================================================ FILE: LICENSE.txt ================================================ The MIT License (MIT) Copyright (c) 2015-2020 Formidable Labs. Copyright (c) 2016-2020 Alexey Svetliakov , snerks , Krzysztof Cebula , Vitaliy Polyanskiy , James Lismore , Stack Builders , Esteban Ibarra , Dominic Lee , Dave Vedder , Alec Flett and potentially other DefinitelyTyped contributors. 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 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. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. ================================================ FILE: README.md ================================================
Victory — Formidable, We build the modern web an ecosystem of composable React components for building interactive data visualizations.

weekly downloads current version build status Maintenance Status

# `Victory` ## Contents - [Getting Started](#getting-started) - [Victory Native](#victory-native) - [API Documentation](http://commerce.nearform.com/open-source/victory/docs) - [Guides](http://commerce.nearform.com/open-source/victory/guides) - [Contributing](#contributing) * See the **docs and examples** on the website: https://commerce.nearform.com/open-source/victory * **Experiment** with all Victory components in this [code sandbox](https://codesandbox.io/s/dj4f7t) ## Getting started 1. Add Victory to your project: ```sh # npm $ npm i --save victory # or yarn $ yarn add victory ``` 2. Add your first Victory component: ```js import React from "react"; import { render } from "react-dom"; import { VictoryPie } from "victory"; const PieChart = () => { return ; }; render(, document.getElementById("app")); ``` 3. `VictoryPie` component will be rendered, and you should see:

pie


## Requirements Projects using Victory should also depend on [React][]. As of `victory@34.0.0` Victory requires React version `16.3.0` or above ## Victory Native Victory Native shares most of its code with Victory, and has a nearly identical API! To learn more, check out the [Victory Native package README](./packages/victory-native/README.md). ## Contributing Please see the [Contributing guide](CONTRIBUTING.md). ## Maintenance Status **Active:** Formidable is actively working on this project, and we expect to continue to work for the foreseeable future. Bug reports, feature requests and pull requests are welcome. [react]: https://facebook.github.io/react/ ================================================ FILE: chromatic.config.json ================================================ { "onlyChanged": true, "projectId": "Project:5b4acf7c54c0490024d5980b", "zip": true } ================================================ FILE: config/webpack/demo/webpack.config.dev.js ================================================ "use strict"; const path = require("path"); const glob = require("glob"); const LodashModuleReplacementPlugin = require("lodash-webpack-plugin"); const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); const ROOT = path.resolve(__dirname, "../../.."); const PKGS = path.join(ROOT, "packages"); const VICTORY_GLOB = path .join(PKGS, "victory*/package.json") .replace(/\\/g, "/"); // Read all the victory packages and alias. const VICTORY_ALIASES = glob.sync(VICTORY_GLOB).reduce((memo, pkgPath) => { const key = path.dirname(path.relative(PKGS, pkgPath)); memo[key] = path.resolve(path.dirname(pkgPath)); return memo; }, {}); const DEMO = path.resolve("demo"); const WDS_PORT = 3000; module.exports = { mode: "development", cache: true, devServer: { port: WDS_PORT, }, output: { path: DEMO, pathinfo: true, filename: "main.js", publicPath: "/assets/", }, devtool: "source-map", stats: { colors: true, reasons: true, }, resolve: { alias: VICTORY_ALIASES, extensions: [".ts", ".tsx", ".js", ".json"], }, module: { rules: [ { // Transform source test: /(\.js|\.tsx?)$/, // Use include specifically of our sources. // Do _not_ use an `exclude` here. include: [DEMO], use: { loader: "babel-loader", options: require("../../../.babelrc.js"), }, }, { test: /\.css$/, use: ["style-loader", "css-loader", "postcss-loader"], }, ], }, plugins: [ new ForkTsCheckerWebpackPlugin({ typescript: { configFile: path.resolve(ROOT, "demo", "ts", "tsconfig.json"), }, }), new LodashModuleReplacementPlugin({ shorthands: true, currying: true, flattening: true, paths: true, placeholders: true, }), ], }; ================================================ FILE: config/webpack/demo/webpack.config.hot.js ================================================ "use strict"; const _ = require("lodash"); const base = require("./webpack.config.dev"); // Clone our own module object. const mod = _.cloneDeep(base.module); const firstLoader = mod.rules[0]; // Update rules array. First loader needs react-hot-loader. firstLoader.rules = [require.resolve("react-hot-loader")] .concat(firstLoader.loader ? [firstLoader.loader] : []) .concat(firstLoader.rules || []); // Remove single loader if any. firstLoader.loader = null; module.exports = _.merge({}, _.omit(base, "entry", "module"), { entry: { app: [require.resolve("webpack/hot/only-dev-server"), "./demo/js/app"], }, module: mod, }); ================================================ FILE: config/webpack/webpack.config.dev.js ================================================ "use strict"; const config = require("./webpack.config"); const LodashModuleReplacementPlugin = require("lodash-webpack-plugin"); // **WARNING**: Mutates base configuration. // We do this because lodash isn't available in `production` mode. config.output.filename = config.output.filename.replace(/\.min\.js$/, ".js"); config.mode = "development"; config.devtool = false; config.plugins = [ new LodashModuleReplacementPlugin({ currying: true, flattening: true, paths: true, placeholders: true, shorthands: true, }), ]; // Export mutated base. module.exports = config; ================================================ FILE: config/webpack/webpack.config.js ================================================ "use strict"; const path = require("path"); const webpack = require("webpack"); const LodashModuleReplacementPlugin = require("lodash-webpack-plugin"); const SRC = path.resolve("src"); // **Little Hacky**: Infer the filename and library name from the package name. // // Assumptions: // - `package.json`'s `name` field is name of dist files. // - PascalCased version of that name is exported class name. const PKG = require(path.resolve("package.json")); const libPath = (PKG.name || "").toLowerCase(); if (!libPath) { throw new Error("Need package.json:name field"); } // PascalCase (with first character capitalized). const libName = libPath .replace(/^\s+|\s+$/g, "") .replace(/(^|[-_ ])+(.)/g, (match, first, second) => { // Second match group is the character we want to change. Throw away first. return second.toUpperCase(); }); module.exports = { cache: true, context: SRC, entry: "./index", externals: { react: { root: "React", commonjs2: "react", commonjs: "react", amd: "react", }, "react-native": "react-native", }, resolve: { extensions: [".ts", ".tsx", ".js", ".jsx"], }, output: { path: path.resolve("dist"), filename: `${libPath}.min.js`, library: libName, libraryTarget: "umd", }, module: { rules: [ { // Transform source test: /(\.js|\.tsx?)$/, // Use include specifically of our sources. // Do _not_ use an `exclude` here. include: [SRC], use: { loader: "babel-loader", options: require("../../.babelrc.js"), }, }, ], }, mode: "production", plugins: [ new LodashModuleReplacementPlugin({ currying: true, flattening: true, paths: true, placeholders: true, shorthands: true, }), new webpack.DefinePlugin({ // Signal production, so that webpack removes non-production code that // is in condtionals like: `if (process.env.NODE_ENV === "production")` "process.env.NODE_ENV": JSON.stringify("production"), }), new webpack.SourceMapDevToolPlugin({ filename: "[file].map", }), ], }; ================================================ FILE: demo/rn/.expo-shared/assets.json ================================================ { "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true } ================================================ FILE: demo/rn/.gitignore ================================================ node_modules/ .expo/ dist/ npm-debug.* *.jks *.p8 *.p12 *.key *.mobileprovision *.orig.* web-build/ # macOS .DS_Store ================================================ FILE: demo/rn/App.tsx ================================================ import React from "react"; import { LogBox } from "react-native"; import { NavigationContainer } from "@react-navigation/native"; import { RootNavigator } from "./src/screens/root-navigator"; import { registerRootComponent } from "expo"; LogBox.ignoreLogs(["Require cycle: ../../packages/victory"]); const App = () => { return ( ); }; registerRootComponent(App); ================================================ FILE: demo/rn/app.json ================================================ { "expo": { "name": "rn-demo", "slug": "rn-demo", "version": "1.0.0", "orientation": "portrait", "icon": "./assets/icon.png", "userInterfaceStyle": "light", "splash": { "image": "./assets/splash.png", "resizeMode": "contain", "backgroundColor": "#ffffff" }, "updates": { "fallbackToCacheTimeout": 0 }, "assetBundlePatterns": ["**/*"], "ios": { "supportsTablet": true }, "android": { "adaptiveIcon": { "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#FFFFFF" } }, "web": { "favicon": "./assets/favicon.png" } } } ================================================ FILE: demo/rn/babel.config.js ================================================ module.exports = function (api) { api.cache(true); return { presets: ["babel-preset-expo"], }; }; ================================================ FILE: demo/rn/metro.config.js ================================================ // https://docs.expo.dev/guides/monorepos/#modify-the-metro-config const { getDefaultConfig } = require("expo/metro-config"); const path = require("path"); // Find the project and workspace directories const projectRoot = __dirname; // Find the monorepo root const monorepoRoot = path.resolve(projectRoot, "../.."); const config = getDefaultConfig(projectRoot); // 1. Watch all files within the monorepo config.watchFolders = [monorepoRoot]; // 2. Let Metro know where to resolve packages and in what order config.resolver.nodeModulesPaths = [ path.resolve(projectRoot, "node_modules"), path.resolve(monorepoRoot, "node_modules"), ]; module.exports = config; ================================================ FILE: demo/rn/package.json ================================================ { "name": "rn-demo", "version": "1.0.0", "sideEffects": false, "main": "App.tsx", "scripts": { "start": "expo start --reset-cache", "android": "expo start --android --reset-cache", "ios": "expo start --ios --reset-cache", "web": "expo start --web --reset-cache", "eject": "expo eject" }, "dependencies": { "@expo/metro-runtime": "~3.1.3", "@react-navigation/native": "^6.1.17", "@react-navigation/native-stack": "^6.9.26", "expo": "~50.0.17", "expo-status-bar": "~1.11.1", "lodash": "^4.17.21", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.73.5", "react-native-gesture-handler": "~2.16.0", "react-native-safe-area-context": "4.10.1", "react-native-screens": "~3.31.1", "react-native-svg": "14.1.0", "react-native-web": "~0.19.6", "victory": "workspace:*", "victory-area": "workspace:*", "victory-axis": "workspace:*", "victory-bar": "workspace:*", "victory-box-plot": "workspace:*", "victory-brush-container": "workspace:*", "victory-brush-line": "workspace:*", "victory-candlestick": "workspace:*", "victory-chart": "workspace:*", "victory-core": "workspace:*", "victory-create-container": "workspace:*", "victory-cursor-container": "workspace:*", "victory-errorbar": "workspace:*", "victory-group": "workspace:*", "victory-histogram": "workspace:*", "victory-legend": "workspace:*", "victory-line": "workspace:*", "victory-native": "workspace:*", "victory-pie": "workspace:*", "victory-polar-axis": "workspace:*", "victory-scatter": "workspace:*", "victory-selection-container": "workspace:*", "victory-shared-events": "workspace:*", "victory-stack": "workspace:*", "victory-tooltip": "workspace:*", "victory-voronoi": "workspace:*", "victory-voronoi-container": "workspace:*", "victory-zoom-container": "workspace:*" }, "devDependencies": { "@babel/core": "^7.20.0", "@babel/runtime": "^7.22.15", "@types/lodash": "^4.14.182", "glob": "^8.0.3", "typescript": "~5.3.3" }, "private": true } ================================================ FILE: demo/rn/src/data/index.ts ================================================ import { range, random } from "lodash"; export const getData = () => range(1, 10).map((i) => ({ x: i, y: random(1, 10) })); export const getBoxPlotData = () => range(5).map((i) => ({ x: i, y: range(20).map(() => random(1, 100)) })); export const generateRandomData = (points = 6) => range(1, points + 1).map((i) => ({ x: i, y: i + random(-1, 2) })); export const getTransitionData = () => { const n = random(4, 10); return range(n).map((i) => { return { x: i, y: random(2, 10), }; }); }; export const getYFunction = () => { const n = random(2, 7); return (data: { x: number }) => Math.exp(-n * data.x) * Math.sin(2 * n * Math.PI * data.x); }; export const getStyles = () => { const colors = ["red", "orange", "magenta", "gold", "blue", "purple"]; return { stroke: colors[random(0, 5)], strokeWidth: random(1, 5), }; }; ================================================ FILE: demo/rn/src/navigation-config.ts ================================================ export type RootStackNavigatorParams = { Components: undefined; Pie: undefined; Chart: undefined; Line: undefined; Area: undefined; Bar: undefined; Histogram: undefined; Scatter: undefined; BoxPlot: undefined; ErrorBar: undefined; Voronoi: undefined; BrushLine: undefined; Legends: undefined; Axis: undefined; PolarAxis: undefined; }; ================================================ FILE: demo/rn/src/screens/area-screen.tsx ================================================ import * as React from "react"; import { ScrollView } from "react-native"; import { VictoryArea, VictoryGroup, VictoryStack } from "victory-native"; import viewStyles from "../styles/view-styles"; import { getData } from "../data"; export const AreaScreen: React.FC = () => { const [data, setData] = React.useState(getData()); React.useEffect(() => { const updateDataHandle = setInterval(() => { setData(getData()); }, 3000); return () => { clearInterval(updateDataHandle); }; }, []); return ( d.yield + d.error} /> ); }; ================================================ FILE: demo/rn/src/screens/axis-screen.tsx ================================================ import * as React from "react"; import { ScrollView } from "react-native"; import { VictoryAxis } from "victory-native"; import Svg from "react-native-svg"; import viewStyles from "../styles/view-styles"; export const AxisScreen: React.FC = () => { return ( x.getFullYear()} /> ); }; ================================================ FILE: demo/rn/src/screens/bar-screen.tsx ================================================ import * as React from "react"; import { Text } from "react-native"; import { ScrollView } from "react-native"; import { VictoryBar, VictoryGroup, VictoryStack } from "victory-native"; import viewStyles from "../styles/view-styles"; export const BarScreen: React.FC = () => { return ( datum.y > 2 ? "red" : "blue", }, }} data={[ { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 }, { x: 4, y: 2 }, { x: 5, y: 1 }, ]} /> ); }; ================================================ FILE: demo/rn/src/screens/box-plot-screen.tsx ================================================ import * as React from "react"; import { ScrollView } from "react-native"; import { VictoryChart, VictoryBoxPlot } from "victory-native"; import viewStyles from "../styles/view-styles"; import { getBoxPlotData } from "../data"; export const BoxPlotScreen: React.FC = () => { const [data, setData] = React.useState(getBoxPlotData()); React.useEffect(() => { const updateDataHandle = setInterval(() => { setData(getBoxPlotData()); }, 3000); return () => { clearInterval(updateDataHandle); }; }, []); return ( ); }; ================================================ FILE: demo/rn/src/screens/brush-line-screen.tsx ================================================ import * as React from "react"; import { Dimensions, ScrollView } from "react-native"; import { VictoryAxis, VictoryBrushLine, VictoryChart, VictoryScatter, VictoryLine, VictoryLabel, VictoryBar, VictoryContainer, } from "victory-native"; import { DomainTuple } from "victory-core"; import _ from "lodash"; import viewStyles from "../styles/view-styles"; import { VictoryBrushLineProps } from "victory-brush-line"; type DataType = { name: string; strength: number; intelligence: number; speed: number; luck: number; }[]; interface DataSet { name: string; data: { x: string; y: number }[]; } const data: DataType = [ { name: "Adrien", strength: 5, intelligence: 30, speed: 500, luck: 3 }, { name: "Brice", strength: 1, intelligence: 13, speed: 550, luck: 2 }, { name: "Casey", strength: 4, intelligence: 15, speed: 80, luck: 1 }, { name: "Drew", strength: 3, intelligence: 25, speed: 600, luck: 5 }, { name: "Erin", strength: 9, intelligence: 50, speed: 350, luck: 4 }, { name: "Francis", strength: 2, intelligence: 40, speed: 200, luck: 2 }, ]; type Attribute = "strength" | "intelligence" | "speed" | "luck"; const attributes: Attribute[] = ["strength", "intelligence", "speed", "luck"]; type Filter = Record; const width = Dimensions.get("window").width; const padding: { [key: string]: number } = { top: 100, left: 50, right: 50, bottom: 50, }; function normalizeData(maximumValues: number[]) { // construct normalized datasets by dividing the value for each attribute by the maximum value return data.map((datum) => ({ name: datum.name, data: attributes.map((attribute, i) => ({ x: attribute, y: datum[attribute] / maximumValues[i], })), })); } function getMaximumValues() { return attributes.map((attribute) => { return data.reduce((memo, datum) => { return datum[attribute] > memo ? datum[attribute] : memo; }, -Infinity); }); } function getAxisOffset(index: number) { const step = (width - padding.left - padding.right) / (attributes.length - 1); return step * index + padding.left; } export const BrushLineScreen: React.FC = () => { const [scrollEnabled, setScrollEnabled] = React.useState(true); const [maximumValues] = React.useState(getMaximumValues()); const [datasets] = React.useState(normalizeData(maximumValues)); const [filters, setFilters] = React.useState({ intelligence: undefined, strength: undefined, luck: undefined, speed: undefined, }); const [isFiltered, setIsFiltered] = React.useState(false); const [activeDatasets, setActiveDatasets] = React.useState([]); const onDomainChange = ( domain: DomainTuple, props: VictoryBrushLineProps | undefined, ) => { const filters = addNewFilters(domain, props); const isFiltered = !_.isEmpty(_.values(filters).filter(Boolean)); const activeDatasets = isFiltered ? getActiveDatasets(filters) : datasets.map((x) => x.name); setFilters(filters); setIsFiltered(isFiltered); setActiveDatasets(activeDatasets); }; const addNewFilters = ( domain: DomainTuple, props: VictoryBrushLineProps | undefined, ): Filter => { if (!domain) return filters; if (typeof domain[0] != "number") return filters; if (typeof domain[1] != "number") return filters; if (!props) return filters; const dm = domain as [number, number]; const currentFilters = filters; const extent = dm && Math.abs(dm[1] - dm[0]); const minVal = 1 / Number.MAX_VALUE; currentFilters[props.name as Attribute] = extent <= minVal ? undefined : dm; return currentFilters; }; const getActiveDatasets = (filters: Filter): string[] => { // Return the names from all datasets that have values within all filters const isActive = (dataset: DataSet): (string | null | undefined)[] => { return Object.keys(filters).reduce((memo: any, name) => { let filter = name as keyof Filter; if (!memo || !Array.isArray(filters[filter])) { return memo; } const point = _.find(dataset.data, (d) => d.x === filter); let tuple = filters[filter]; return ( point && tuple && Math.max(...tuple) >= point.y && Math.min(...tuple) <= point.y ); }, true); }; return datasets .map((dataset) => (isActive(dataset) ? dataset.name : null)) .filter(Boolean) as string[]; }; const isActive = (dataset: DataSet): boolean => { // Determine whether a given dataset is active return !isFiltered ? true : _.includes(activeDatasets, dataset.name); }; const max = maximumValues || []; return ( setScrollEnabled(false)} onTouchEnd={() => setScrollEnabled(true)} /> } domain={{ y: [0, 1.1] }} width={width} padding={padding} > } /> {datasets.map((dataset: DataSet) => ( ))} {attributes.map((attribute, index) => ( setScrollEnabled(false)} onTouchEnd={() => setScrollEnabled(true)} onBrushDomainChange={onDomainChange} /> } offsetX={getAxisOffset(index)} style={{ tickLabels: { fontSize: 15, padding: 15, pointerEvents: "none", }, }} tickValues={[0.2, 0.4, 0.6, 0.8, 1]} tickFormat={(tick) => Math.round(tick * max[index])} /> ))} setScrollEnabled(false)} onTouchEnd={() => setScrollEnabled(true)} brushWidth={20} /> } /> setScrollEnabled(false)} onTouchEnd={() => setScrollEnabled(true)} brushWidth={20} brushDomain={[2, 3]} /> } /> setScrollEnabled(false)} onTouchEnd={() => setScrollEnabled(true)} width={30} /> } /> setScrollEnabled(false)} onTouchEnd={() => setScrollEnabled(true)} brushWidth={10} brushDomain={[0, 10]} /> } /> ); }; ================================================ FILE: demo/rn/src/screens/chart-screen.tsx ================================================ import * as React from "react"; import { ScrollView } from "react-native"; import { Background, VictoryChart, VictoryBar, VictoryGroup, VictoryLine, VictoryScatter, VictoryArea, VictoryStack, VictoryTooltip, VictoryZoomContainer, } from "victory-native"; import viewStyles from "../styles/view-styles"; import { getTransitionData, generateRandomData } from "../data"; export const ChartScreen: React.FC = () => { const [transitionData, setTransitionData] = React.useState(getTransitionData()); React.useEffect(() => { const updateDataHandle = setInterval(() => { setTransitionData(getTransitionData()); }, 3000); return () => { clearInterval(updateDataHandle); }; }, []); return ( } style={{ data: { fill: ({ datum }) => datum.fill }, }} data={[ { x: 1, y: 3, fill: "red", symbol: "plus", size: 6, label: "Red", }, { x: 2, y: 5, fill: "magenta", size: 9, opacity: 0.4, label: "Magenta", }, { x: 3, y: 4, fill: "orange", size: 5, label: "Orange", }, { x: 4, y: 2, fill: "brown", symbol: "square", size: 6, label: "Brown", }, { x: 5, y: 5, fill: "black", symbol: "triangleUp", size: 5, label: "Black", }, ]} /> "Hi"} data={transitionData} style={{ data: { fill: "tomato", width: 12, }, }} animate={{ onExit: { duration: 500, before: () => ({ y: 0, fill: "orange", label: "BYE", }), }, }} /> } style={{ background: { fill: "pink" } }} > } > ); }; ================================================ FILE: demo/rn/src/screens/components-screen.tsx ================================================ import * as React from "react"; import { ListRenderItem, Platform, SectionList, SectionListRenderItem, Text, TouchableOpacity, View, } from "react-native"; import styles from "../styles/container-view-styles"; import { useNavigation } from "@react-navigation/native"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { RootStackNavigatorParams } from "../navigation-config"; export const ComponentsScreen: React.FC = () => { const rootNavigation = useNavigation>(); const renderItem = React.useCallback< SectionListRenderItem >(({ item }) => { return ( { // @ts-ignore rootNavigation.navigate(item.key); }} > {item.title} {/*{Platform.select({ ios: })}*/} ); }, []); const renderSectionHeader = React.useCallback( ({ section }: { section: SectionItem }) => { return ( {section.title} ); }, [], ); return ( ); }; type DataItem = { key: string; title: string }; type SectionItem = { title: string; data: DataItem[]; }; const sections: SectionItem[] = [ { data: [ { key: "Pie", title: "VictoryPie" }, { key: "Chart", title: "VictoryChart" }, { key: "Line", title: "VictoryLine" }, { key: "Area", title: "VictoryArea" }, { key: "Bar", title: "VictoryBar" }, { key: "Histogram", title: "VictoryHistogram" }, { key: "Scatter", title: "VictoryScatter" }, { key: "BoxPlot", title: "VictoryBoxPlot" }, { key: "ErrorBar", title: "VictoryErrorBar" }, { key: "Voronoi", title: "VictoryVoronoi" }, { key: "BrushLine", title: "VictoryBrushLine" }, ], title: "Charts", }, // { // data: [ // { key: "ContainersView", title: "Built–in Containers" }, // { key: "CreateContainerView", title: "Custom Containers" } // ], // title: "Containers" // }, { data: [ { key: "Legends", title: "Legends" }, { key: "Axis", title: "Axis" }, { key: "PolarAxis", title: "VictoryPolarAxis" }, // { key: "ErrorsTooltipsView", title: "Errors & Tooltips" } ], title: "Other", }, ]; ================================================ FILE: demo/rn/src/screens/error-bar-screen.tsx ================================================ import * as React from "react"; import { ScrollView, Text } from "react-native"; import { VictoryChart, VictoryErrorBar, VictoryTheme } from "victory-native"; import viewStyles from "../styles/view-styles"; export const ErrorBarScreen: React.FC = () => { return ( datum.error * datum.x} errorY={(datum) => datum.error * datum.y} /> ); }; const data = [ { x: 15, y: 35000, error: 0.2 }, { x: 20, y: 42000, error: 0.05 }, { x: 25, y: 30000, error: 0.1 }, { x: 30, y: 35000, error: 0.2 }, { x: 35, y: 22000, error: 0.15 }, ]; ================================================ FILE: demo/rn/src/screens/histogram-screen.tsx ================================================ import * as React from "react"; import { ScrollView } from "react-native"; import { VictoryStack, VictoryHistogram } from "victory-native"; import viewStyles from "../styles/view-styles"; const randomDate = (start: Date, end: Date) => { return new Date( start.getTime() + Math.random() * (end.getTime() - start.getTime()), ); }; const getRandomDateData = () => Array.from({ length: 100 }, () => ({ x: randomDate(new Date(2016, 0, 1), new Date(2020, 5, 1)), })); const getRandomData = () => Array.from({ length: 100 }, () => ({ x: Math.floor(Math.random() * 100), })); const numericData = getRandomData(); const numericData2 = getRandomData(); const dateData = getRandomDateData(); const dateData2 = getRandomDateData(); export const HistogramScreen: React.FC = () => { return ( ); }; ================================================ FILE: demo/rn/src/screens/legends-screen.tsx ================================================ import * as React from "react"; import { ScrollView, Dimensions } from "react-native"; import { VictoryLegend } from "victory-native"; import Svg from "react-native-svg"; import viewStyles from "../styles/view-styles"; const legendData = [ { name: "Series 1", symbol: { type: "circle", fill: "green", }, }, { name: "Long Series Name", symbol: { type: "triangleUp", fill: "blue", }, }, { name: "Series 3", symbol: { type: "diamond", fill: "pink", }, }, { name: "Series 4", symbol: { type: "plus", }, }, { name: "Series 5", symbol: { type: "star", fill: "red", }, labels: { fill: "purple", }, }, { name: "Series 6", symbol: { type: "circle", fill: "orange", }, labels: { fill: "blue", }, }, ]; const legendStyle = { border: { stroke: "black" } }; export const LegendsScreen: React.FC = () => { return ( ); }; ================================================ FILE: demo/rn/src/screens/line-screen.tsx ================================================ import * as React from "react"; import { ScrollView } from "react-native"; import { VictoryLine } from "victory-native"; import { getStyles, getYFunction } from "../data"; import viewStyles from "../styles/view-styles"; export const LineScreen: React.FC = () => { const [y, setY] = React.useState(getYFunction); const [styles, setStyles] = React.useState(getStyles()); React.useEffect(() => { const updateDataHandle = setInterval(() => { setY(getYFunction); setStyles(getStyles()); }, 3000); return () => { clearInterval(updateDataHandle); }; }, []); return ( data.yield + data.error} /> Math.sin(2 * Math.PI * data.x)} /> "LINE"} style={{ data: { stroke: "#822722", strokeWidth: 3, }, labels: { fontSize: 12 }, }} /> ); }; ================================================ FILE: demo/rn/src/screens/pie-screen.tsx ================================================ import * as React from "react"; import { ScrollView } from "react-native"; import { VictoryPie } from "victory-native"; import viewStyles from "../styles/view-styles"; import { generateRandomData } from "../data/"; export const PieScreen: React.FC = () => { const [data, setData] = React.useState(generateRandomData()); React.useEffect(() => { const updateDataHandle = setInterval(() => { setData(generateRandomData()); }, 3000); return () => { clearInterval(updateDataHandle); }; }, []); return ( (datum.y > 75 ? "black" : "none"), opacity: ({ datum }) => (datum.y > 75 ? 1 : 0.4), }, }} data={[ { x: "Cat", y: 62 }, { x: "Dog", y: 91 }, { x: "Fish", y: 55 }, { x: "Bird", y: 55 }, ]} /> ); }; ================================================ FILE: demo/rn/src/screens/polar-axis-screen.tsx ================================================ import * as React from "react"; import { ScrollView } from "react-native"; import viewStyles from "../styles/view-styles"; import { VictoryBar, VictoryChart, VictoryPolarAxis, VictoryTheme, } from "victory-native"; export const PolarAxisScreen: React.FC = () => { return ( {["cat", "dog", "bird", "dog", "frog", "fish"].map((d, i) => { return ( ); })} ); }; ================================================ FILE: demo/rn/src/screens/root-navigator.tsx ================================================ import * as React from "react"; import { RootStackNavigatorParams } from "../navigation-config"; import { createNativeStackNavigator } from "@react-navigation/native-stack"; import { BarScreen } from "./bar-screen"; import { ComponentsScreen } from "./components-screen"; import { PieScreen } from "./pie-screen"; import { ChartScreen } from "./chart-screen"; import { LineScreen } from "./line-screen"; import { AreaScreen } from "./area-screen"; import { HistogramScreen } from "./histogram-screen"; import { LegendsScreen } from "./legends-screen"; import { AxisScreen } from "./axis-screen"; import { ScatterScreen } from "./scatter-screen"; import { BoxPlotScreen } from "./box-plot-screen"; import { ErrorBarScreen } from "./error-bar-screen"; import { PolarAxisScreen } from "./polar-axis-screen"; import { VoronoiScreen } from "./voronoi-screen"; import { BrushLineScreen } from "./brush-line-screen"; export const RootNavigator: React.FC = () => { return ( {/* Other */} ); }; const RootStack = createNativeStackNavigator(); ================================================ FILE: demo/rn/src/screens/scatter-screen.tsx ================================================ import * as React from "react"; import { ScrollView, View } from "react-native"; import { VictoryScatter } from "victory-native"; import viewStyles from "../styles/view-styles"; import { generateRandomData } from "../data"; export const ScatterScreen: React.FC = () => { const [data, setData] = React.useState(generateRandomData()); React.useEffect(() => { const updateDataHandle = setInterval(() => { setData(generateRandomData()); }, 3000); return () => { clearInterval(updateDataHandle); }; }, []); return ( Math.sin(2 * Math.PI * d.x)} /> (datum.y > 0 ? "red" : "blue"), }, }} symbol={({ datum }) => (datum.y > 0 ? "triangleUp" : "triangleDown")} y={(d) => Math.sin(2 * Math.PI * d.x)} samples={25} /> ); }; ================================================ FILE: demo/rn/src/screens/voronoi-screen.tsx ================================================ import * as React from "react"; import { ScrollView } from "react-native"; import viewStyles from "../styles/view-styles"; import { VictoryTheme, VictoryVoronoi, VictoryChart } from "victory-native"; export const VoronoiScreen: React.FC = () => { return ( ); }; ================================================ FILE: demo/rn/src/styles/container-view-styles.ts ================================================ import { Platform, StyleSheet } from "react-native"; export default StyleSheet.create({ container: { flex: 1, backgroundColor: "#fff", }, sectionHeader: { paddingHorizontal: 15, paddingVertical: 10, backgroundColor: "#eee", borderBottomColor: "#ccc", borderBottomWidth: StyleSheet.hairlineWidth, }, sectionHeaderText: { fontWeight: "bold", }, item: { backgroundColor: "#fff", borderBottomColor: "#ccc", borderBottomWidth: StyleSheet.hairlineWidth, justifyContent: "space-between", alignItems: "center", flexDirection: "row", ...Platform.select({ ios: { marginLeft: 15, paddingRight: 15, paddingVertical: 15 }, android: { padding: 15 }, }), }, itemText: { fontSize: 16, }, }); ================================================ FILE: demo/rn/src/styles/view-styles.ts ================================================ import { Platform, StyleSheet } from "react-native"; export default StyleSheet.create({ container: { flex: 1, }, monospace: { fontFamily: Platform.OS === "ios" ? "Menlo" : "monospace", }, contentContainer: { alignItems: "center", }, header: { fontWeight: "600", padding: 15, fontSize: 18, }, }); ================================================ FILE: demo/rn/tsconfig.json ================================================ { "extends": "expo/tsconfig.base", "compilerOptions": { "strict": true } } ================================================ FILE: eslint.config.mjs ================================================ import eslint from "@eslint/js"; import prettier from "eslint-plugin-prettier/recommended"; import pluginJest from "eslint-plugin-jest"; import pluginPromise from "eslint-plugin-promise"; import react from "eslint-plugin-react"; import reactHooks from "eslint-plugin-react-hooks"; import tseslint from "typescript-eslint"; import globals from "globals"; export default tseslint.config( // enforce formatting prettier, // global ignores { ignores: [ "**/*.d.ts", "**/.wireit/", "**/.docusaurus/", "**/artifacts/", "**/coverage/", "**/demo/rn/", "**/dist/", "**/build/", "**/es/", "**/lib/", "**/lib-vendor/", "**/public/", "**/storybook-static/", "**/tmp/", "website/src/theme/", "website/src/plugins/", ], }, // Typescript and React Rules { files: ["**/*.{js,jsx,mjs,cjs,ts,tsx}"], extends: [ eslint.configs.recommended, ...tseslint.configs.recommended, react.configs.flat.recommended, pluginPromise.configs["flat/recommended"], ], plugins: { react, "react-hooks": reactHooks, }, settings: { react: { version: "detect", }, }, languageOptions: { parserOptions: { ecmaFeatures: { jsx: true, }, }, globals: { ...globals.browser, ...globals.node, }, }, rules: { // eslint overrides eqeqeq: "error", "max-depth": ["error", 4], "max-nested-callbacks": ["error", 3], "max-params": ["error", 3], "no-console": "error", "no-magic-numbers": [ "error", { ignore: [-1, 0, 0.5, 1, 2, 90, 180, 270, 360] }, ], "no-nested-ternary": "error", "no-prototype-builtins": "off", "no-param-reassign": "error", "no-restricted-imports": [ "error", { paths: [ { name: "lodash", message: "Be sure to import specific lodash functions, not the entire library!", }, ], patterns: [ { group: ["victory*/src", "victory*/src/**"], message: "Be sure to import directly from Victory packages, not from /src folders!", }, ], }, ], "no-shadow": "error", "no-undef": "error", "no-useless-escape": "off", "prefer-arrow-callback": "error", // react overrides "react/display-name": "off", "react/prop-types": "off", "react/no-multi-comp": "error", ...reactHooks.configs.recommended.rules, // @typescript-eslint overrides "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-unsafe-declaration-merging": "off", }, }, // Overrides for JS files { files: ["**/*.js"], rules: { "@typescript-eslint/no-require-imports": "off", }, }, // Overrides for Test files { files: ["**/*.test.*", "**/test/**/*", "**/jest-native-setup.tsx"], plugins: { jest: pluginJest }, languageOptions: { globals: pluginJest.environments.globals.globals, }, rules: { "react/sort-comp": "off", "no-magic-numbers": "off", "max-statements": "off", "import/no-unresolved": "off", "no-undef": "off", "max-nested-callbacks": "off", }, }, // Overrides for Storybook { files: ["**/stories/**/*.ts", "**/stories/**/*.stories.tsx"], rules: { "no-magic-numbers": "off", }, }, ); ================================================ FILE: package-scripts.js ================================================ /** * We generally use `nps` for scripts that we: * 1. define at the root of the monorepo * 2. that are meant to execute _within_ a workspace * * ... or ... * * - That could use a little JS magic that we don't want to write a full * node script for 😂 * * For more cases, if you have an actual root task, define it in root * `package.json:scripts`. */ // For publishing, use the core package's version. const coreVersion = require("./packages/victory-core/package.json").version; if (!coreVersion) { throw new Error("Unable to read core version"); } const coreTag = `v${coreVersion}`; module.exports = { scripts: { // Root tasks. // Try to find an existing tag (from previous attempts, etc.), and if not, create one. "git:tag": `git show-ref ${coreTag} || git tag -a ${coreTag} -m \"Version ${coreVersion}\"`, // Build. // - Libraries "build:lib:esm": "cross-env BABEL_ENV=es babel src --out-dir es --config-file ../../.babelrc.build.js --extensions .tsx,.ts,.jsx,.js", "build:lib:cjs": "cross-env BABEL_ENV=commonjs babel src --out-dir lib --config-file ../../.babelrc.build.js --extensions .tsx,.ts,.jsx,.js", // - UMD distributions // TODO(2375): Add / verify caching // https://github.com/FormidableLabs/victory/issues/2375 "build:dist:dev": "webpack --bail --config ../../config/webpack/webpack.config.dev.js", "build:dist:min": "webpack --bail --config ../../config/webpack/webpack.config.js", // - TypeScript // TODO(2375): Can we cache / incremental? // https://github.com/FormidableLabs/victory/issues/2375 // Check for errors (includes test files): "types:pkg:check": "tsc --pretty --noEmit", // To create types, we must do the following: // 1. Copy all *.d.ts files to the es folder // 2. Compile all *.ts files to the es folder // 3. Copy all output from the es folder to the lib folder "types:pkg:create": "nps types:pkg:copy types:pkg:compile types:pkg:cjs-copy", "types:pkg:copy": 'cpx "src/**/*.d.ts" es', "types:pkg:compile": "tsc --pretty -p ./tsconfig.build.json --emitDeclarationOnly --rootDir src --outDir es || nps types:warning", "types:warning": 'echo "Warning: found TypeScript errors during build. Continuing anyway!"', "types:pkg:cjs-copy": 'cpx "es/**/*{.d.ts,.d.ts.map}" lib', }, }; ================================================ FILE: package.json ================================================ { "name": "victory-monorepo", "version": "0.0.0", "description": "Data viz for React", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/formidablelabs/victory.git" }, "private": true, "author": "Formidable", "license": "MIT", "bugs": { "url": "https://github.com/formidablelabs/victory/issues" }, "homepage": "https://commerce.nearform.com/open-source/victory", "packageManager": "pnpm@9.13.0", "dependencies": { "clsx": "^2.1.1" }, "devDependencies": { "@babel/cli": "7.23.9", "@babel/core": "7.23.9", "@babel/plugin-transform-export-namespace-from": "7.23.4", "@babel/plugin-transform-modules-commonjs": "7.23.3", "@babel/preset-env": "7.23.9", "@babel/preset-react": "7.23.3", "@babel/preset-typescript": "7.23.3", "@changesets/cli": "^2.24.1", "@chromatic-com/storybook": "^3.2.2", "@eslint/js": "^9.14.0", "@storybook/addon-essentials": "^8.4.1", "@storybook/addon-storysource": "^8.4.1", "@storybook/addon-webpack5-compiler-swc": "1.0.5", "@storybook/react": "^8.4.1", "@storybook/react-webpack5": "^8.4.1", "@svitejs/changesets-changelog-github-compact": "^0.1.1", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.0.1", "@types/fs-extra": "^11.0.3", "@types/jest": "^29.5.12", "@types/lodash": "^4.14.149", "@types/node": "^18.6.1", "@types/prop-types": "^15.7.5", "@types/react": "^18.0.15", "@types/react-dom": "^18.0.6", "babel-jest": "29.7.0", "babel-loader": "9.1.3", "babel-plugin-module-resolver": "5.0.0", "babel-preset-react-native": "4.0.1", "chromatic": "^11.16.3", "concurrently": "^9.0.1", "cpx2": "^4.2.0", "cross-env": "^7.0.3", "eslint": "^9.14.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jest": "^28.9.0", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-promise": "^7.1.0", "eslint-plugin-react": "^7.37.2", "eslint-plugin-react-hooks": "^5.0.0", "fork-ts-checker-webpack-plugin": "^8.0.0", "fs-extra": "^10.0.0", "glob": "8.0.3", "globals": "^15.12.0", "immutable": "^3.8.2", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "lodash": "^4.17.19", "lodash-webpack-plugin": "^0.11.6", "mdast-util-to-string": "^1.0.6", "metro-react-native-babel-preset": "0.77.0", "nps": "^5.9.0", "octokit": "^3.1.1", "prettier": "^3.3.3", "prop-types": "^15.8.1", "react": "^18.1.0", "react-dom": "^18.1.0", "react-hot-loader": "4.13.0", "react-icons": "^5.3.0", "react-test-renderer": "^18.1.0", "remark-parse": "^7.0.1", "remark-stringify": "^7.0.3", "rimraf": "^3.0.2", "seedrandom": "^3.0.5", "storybook": "^8.4.1", "ts-jest": "^29.2.5", "ts-loader": "^9.3.0", "ts-node": "^10.9.1", "typescript": "^5.7.2", "typescript-eslint": "^8.13.0", "unified": "^8.3.2", "victory-vendor": "*", "victory-voronoi": "*", "webpack": "^5.74.0", "webpack-cli": "^4.9.2", "webpack-dev-server": "^4.9.0", "wireit": "^0.7.1" }, "pnpm": { "overrides": { "@types/eslint": "8.4.3" }, "patchedDependencies": { "@changesets/cli@2.24.1": "patches/@changesets__cli@2.24.1.patch" } }, "browserslist": [ "> 0.5%", "last 2 versions", "Firefox ESR", "not dead" ], "scripts": { "version": "pnpm changeset version && pnpm install --no-frozen-lockfile", "publish": "nps git:tag && pnpm changeset publish --no-git-tag", "changeset": "changeset", "start": "webpack serve --config ./config/webpack/demo/webpack.config.dev.js --static demo/ts --entry ./demo/ts/app", "start:docs": "wireit", "check": "wireit", "check:debug": "cross-env WIREIT_PARALLEL=1 pnpm check", "clean:build": "rimraf coverage \"packages/*/{dist,es,lib}\"", "clean:cache": "wireit", "clean:cache:lint": "rimraf .eslintcache \"{demo,packages}/*/.eslintcache\"", "clean:cache:wireit": "rimraf .wireit \"{demo,packages}/*/.wireit\"", "clean:cache:modules": "rimraf node_modules/.cache \"{demo,packages}/*/node_modules/.cache\"", "build": "wireit", "build:lib:esm": "wireit", "build:typescript": "wireit", "build:docs": "wireit", "format": "wireit", "format:fix": "wireit", "lint": "wireit", "lint:fix": "wireit", "lint:root": "wireit", "lint:root:fix": "wireit", "lint:pkgs": "wireit", "lint:pkgs:fix": "wireit", "jest": "wireit", "jest:root": "wireit", "jest:pkgs": "wireit", "types:check": "wireit", "types:create": "wireit", "build-storybook": "storybook build", "storybook:typecheck": "tsc -p tsconfig.storybook.json", "storybook:build": "wireit", "storybook:dev": "concurrently --raw \"pnpm:build:lib:esm --watch\" \"pnpx storybook dev -p 6006\"", "storybook:start": "storybook dev -p 6006 --no-open", "sync": "wireit", "sync:pkgs": "node ./scripts/sync-pkgs-wireit.js", "sync:tsconfig": "pnpm -r --filter !victory-core --filter !victory-vendor --filter !victory-native exec -- cpx ../victory-core/tsconfig.* .", "release-notes": "ts-node ./scripts/release.ts" }, "wireit": { "clean:cache": { "dependencies": [ "clean:cache:wireit", "clean:cache:lint", "clean:cache:modules" ] }, "check": { "dependencies": [ "format", "lint", "jest", "types:check" ] }, "build": { "dependencies": [ "./packages/victory-native:build", "./packages/victory-vendor:build", "./packages/victory:build", "./packages/victory-area:build", "./packages/victory-axis:build", "./packages/victory-bar:build", "./packages/victory-box-plot:build", "./packages/victory-brush-container:build", "./packages/victory-brush-line:build", "./packages/victory-candlestick:build", "./packages/victory-canvas:build", "./packages/victory-chart:build", "./packages/victory-core:build", "./packages/victory-create-container:build", "./packages/victory-cursor-container:build", "./packages/victory-errorbar:build", "./packages/victory-group:build", "./packages/victory-histogram:build", "./packages/victory-legend:build", "./packages/victory-line:build", "./packages/victory-pie:build", "./packages/victory-polar-axis:build", "./packages/victory-scatter:build", "./packages/victory-selection-container:build", "./packages/victory-shared-events:build", "./packages/victory-stack:build", "./packages/victory-tooltip:build", "./packages/victory-voronoi:build", "./packages/victory-voronoi-container:build", "./packages/victory-zoom-container:build" ] }, "build:lib:esm": { "dependencies": [ "./packages/victory-native:build:lib:esm", "./packages/victory-vendor:build:lib:esm", "./packages/victory:build:lib:esm", "./packages/victory-area:build:lib:esm", "./packages/victory-axis:build:lib:esm", "./packages/victory-bar:build:lib:esm", "./packages/victory-box-plot:build:lib:esm", "./packages/victory-brush-container:build:lib:esm", "./packages/victory-brush-line:build:lib:esm", "./packages/victory-candlestick:build:lib:esm", "./packages/victory-canvas:build:lib:esm", "./packages/victory-chart:build:lib:esm", "./packages/victory-core:build:lib:esm", "./packages/victory-create-container:build:lib:esm", "./packages/victory-cursor-container:build:lib:esm", "./packages/victory-errorbar:build:lib:esm", "./packages/victory-group:build:lib:esm", "./packages/victory-histogram:build:lib:esm", "./packages/victory-legend:build:lib:esm", "./packages/victory-line:build:lib:esm", "./packages/victory-pie:build:lib:esm", "./packages/victory-polar-axis:build:lib:esm", "./packages/victory-scatter:build:lib:esm", "./packages/victory-selection-container:build:lib:esm", "./packages/victory-shared-events:build:lib:esm", "./packages/victory-stack:build:lib:esm", "./packages/victory-tooltip:build:lib:esm", "./packages/victory-voronoi:build:lib:esm", "./packages/victory-voronoi-container:build:lib:esm", "./packages/victory-zoom-container:build:lib:esm" ] }, "build:typescript": { "dependencies": [ "build:lib:esm", "types:create" ] }, "build:docs": { "command": "pnpm run --filter victory-docs build", "dependencies": [ "build:lib:esm" ] }, "format": { "command": "prettier --config .prettierrc.json --ignore-path .prettierignore --check \"./**/*.{js,jsx,json,ts,tsx}\"" }, "format:fix": { "command": "prettier --config .prettierrc.json --ignore-path .prettierignore --write \"./**/*.{js,jsx,json,ts,tsx}\"" }, "lint": { "dependencies": [ "lint:root", "lint:pkgs" ] }, "lint:fix": { "dependencies": [ "lint:root:fix", "lint:pkgs:fix" ] }, "lint:root": { "command": "eslint --color *.js scripts config stories test website", "files": [ "*.js", "scripts", "config", "stories", "test", "website", "!**/node_modules/**" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:root:fix": { "command": "eslint --color --fix *.js scripts config stories test website", "files": [ "*.js", "scripts", "config", "stories", "test", "website", "!**/node_modules/**" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:pkgs": { "command": "eslint --color packages", "dependencies": [ "build" ] }, "lint:pkgs:fix": { "command": "eslint --color --fix packages", "dependencies": [ "build" ] }, "jest": { "dependencies": [ "jest:pkgs" ] }, "jest:pkgs": { "dependencies": [ "./packages/victory-native:jest", "./packages/victory-vendor:jest", "./packages/victory:jest", "./packages/victory-area:jest", "./packages/victory-axis:jest", "./packages/victory-bar:jest", "./packages/victory-box-plot:jest", "./packages/victory-brush-container:jest", "./packages/victory-brush-line:jest", "./packages/victory-candlestick:jest", "./packages/victory-canvas:jest", "./packages/victory-chart:jest", "./packages/victory-core:jest", "./packages/victory-create-container:jest", "./packages/victory-cursor-container:jest", "./packages/victory-errorbar:jest", "./packages/victory-group:jest", "./packages/victory-histogram:jest", "./packages/victory-legend:jest", "./packages/victory-line:jest", "./packages/victory-pie:jest", "./packages/victory-polar-axis:jest", "./packages/victory-scatter:jest", "./packages/victory-selection-container:jest", "./packages/victory-shared-events:jest", "./packages/victory-stack:jest", "./packages/victory-tooltip:jest", "./packages/victory-voronoi:jest", "./packages/victory-voronoi-container:jest", "./packages/victory-zoom-container:jest" ] }, "storybook:build": { "command": "storybook build", "files": [ ".storybook", "stories", "packages/victory*/src/**/*.stories.*" ], "output": [ "storybook-static" ], "dependencies": [ "build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "sync": { "dependencies": [ "sync:pkgs", "sync:tsconfig" ] }, "types:check": { "dependencies": [ "./packages/victory-native:types:check", "./packages/victory-vendor:types:check", "./packages/victory:types:check", "./packages/victory-area:types:check", "./packages/victory-axis:types:check", "./packages/victory-bar:types:check", "./packages/victory-box-plot:types:check", "./packages/victory-brush-container:types:check", "./packages/victory-brush-line:types:check", "./packages/victory-candlestick:types:check", "./packages/victory-canvas:types:check", "./packages/victory-chart:types:check", "./packages/victory-core:types:check", "./packages/victory-create-container:types:check", "./packages/victory-cursor-container:types:check", "./packages/victory-errorbar:types:check", "./packages/victory-group:types:check", "./packages/victory-histogram:types:check", "./packages/victory-legend:types:check", "./packages/victory-line:types:check", "./packages/victory-pie:types:check", "./packages/victory-polar-axis:types:check", "./packages/victory-scatter:types:check", "./packages/victory-selection-container:types:check", "./packages/victory-shared-events:types:check", "./packages/victory-stack:types:check", "./packages/victory-tooltip:types:check", "./packages/victory-voronoi:types:check", "./packages/victory-voronoi-container:types:check", "./packages/victory-zoom-container:types:check" ] }, "types:create": { "dependencies": [ "./packages/victory-native:types:create", "./packages/victory-vendor:types:create", "./packages/victory:types:create", "./packages/victory-area:types:create", "./packages/victory-axis:types:create", "./packages/victory-bar:types:create", "./packages/victory-box-plot:types:create", "./packages/victory-brush-container:types:create", "./packages/victory-brush-line:types:create", "./packages/victory-candlestick:types:create", "./packages/victory-canvas:types:create", "./packages/victory-chart:types:create", "./packages/victory-core:types:create", "./packages/victory-create-container:types:create", "./packages/victory-cursor-container:types:create", "./packages/victory-errorbar:types:create", "./packages/victory-group:types:create", "./packages/victory-histogram:types:create", "./packages/victory-legend:types:create", "./packages/victory-line:types:create", "./packages/victory-pie:types:create", "./packages/victory-polar-axis:types:create", "./packages/victory-scatter:types:create", "./packages/victory-selection-container:types:create", "./packages/victory-shared-events:types:create", "./packages/victory-stack:types:create", "./packages/victory-tooltip:types:create", "./packages/victory-voronoi:types:create", "./packages/victory-voronoi-container:types:create", "./packages/victory-zoom-container:types:create" ] }, "start:docs": { "command": "pnpm run --filter victory-docs start", "dependencies": [ "build:lib:esm" ] }, "engines": { "node": ">=18.0.0" } } } ================================================ FILE: packages/victory/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory/CHANGELOG.md ================================================ # victory ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Refactor containers and portal to function components ([#2799](https://github.com/FormidableLabs/victory/pull/2799)) * Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ### Patch Changes - Update victory package.json source to the correct index file ([#2765](https://github.com/FormidableLabs/victory/pull/2765)) ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ### Patch Changes - Fix Date equality in axis ([#2642](https://github.com/FormidableLabs/victory/pull/2642)) ## 36.6.12 ### Patch Changes - Add aria-hidden flag to svg for textsize util to fix accessibility issue ([#2661](https://github.com/FormidableLabs/victory/pull/2661)) ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) * Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies [[`c5b4f660c`](https://github.com/FormidableLabs/victory/commit/c5b4f660cb91d8d1979d216b846a44f0c5030ec1), [`1da86d186`](https://github.com/FormidableLabs/victory/commit/1da86d18615bf75db35cfc55cc8e3d5c0b5772b6)]: - victory-zoom-container@36.6.8 - victory-stack@36.6.8 - victory-area@36.6.8 - victory-axis@36.6.8 - victory-bar@36.6.8 - victory-box-plot@36.6.8 - victory-brush-container@36.6.8 - victory-brush-line@36.6.8 - victory-candlestick@36.6.8 - victory-canvas@36.6.8 - victory-chart@36.6.8 - victory-core@36.6.8 - victory-create-container@36.6.8 - victory-cursor-container@36.6.8 - victory-errorbar@36.6.8 - victory-group@36.6.8 - victory-histogram@36.6.8 - victory-legend@36.6.8 - victory-line@36.6.8 - victory-pie@36.6.8 - victory-polar-axis@36.6.8 - victory-scatter@36.6.8 - victory-selection-container@36.6.8 - victory-shared-events@36.6.8 - victory-tooltip@36.6.8 - victory-voronoi@36.6.8 - victory-voronoi-container@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies [[`3f476632f`](https://github.com/FormidableLabs/victory/commit/3f476632fcd41c31cb69fcfae74d1bbaaa78103d)]: - victory-line@36.6.7 - victory-area@36.6.7 - victory-axis@36.6.7 - victory-bar@36.6.7 - victory-box-plot@36.6.7 - victory-brush-container@36.6.7 - victory-brush-line@36.6.7 - victory-candlestick@36.6.7 - victory-canvas@36.6.7 - victory-chart@36.6.7 - victory-core@36.6.7 - victory-create-container@36.6.7 - victory-cursor-container@36.6.7 - victory-errorbar@36.6.7 - victory-group@36.6.7 - victory-histogram@36.6.7 - victory-legend@36.6.7 - victory-pie@36.6.7 - victory-polar-axis@36.6.7 - victory-scatter@36.6.7 - victory-selection-container@36.6.7 - victory-shared-events@36.6.7 - victory-stack@36.6.7 - victory-tooltip@36.6.7 - victory-voronoi@36.6.7 - victory-voronoi-container@36.6.7 - victory-zoom-container@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies [[`6c6764f73`](https://github.com/FormidableLabs/victory/commit/6c6764f734b9655170c75798b7fe1c6d9e63f2a5)]: - victory-brush-container@36.6.6 - victory-cursor-container@36.6.6 - victory-selection-container@36.6.6 - victory-voronoi-container@36.6.6 - victory-zoom-container@36.6.6 - victory-area@36.6.6 - victory-axis@36.6.6 - victory-bar@36.6.6 - victory-box-plot@36.6.6 - victory-brush-line@36.6.6 - victory-candlestick@36.6.6 - victory-canvas@36.6.6 - victory-chart@36.6.6 - victory-core@36.6.6 - victory-create-container@36.6.6 - victory-errorbar@36.6.6 - victory-group@36.6.6 - victory-histogram@36.6.6 - victory-legend@36.6.6 - victory-line@36.6.6 - victory-pie@36.6.6 - victory-polar-axis@36.6.6 - victory-scatter@36.6.6 - victory-shared-events@36.6.6 - victory-stack@36.6.6 - victory-tooltip@36.6.6 - victory-voronoi@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416), [`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-zoom-container@36.6.5 - victory-core@36.6.5 - victory-area@36.6.5 - victory-axis@36.6.5 - victory-bar@36.6.5 - victory-box-plot@36.6.5 - victory-brush-container@36.6.5 - victory-brush-line@36.6.5 - victory-candlestick@36.6.5 - victory-canvas@36.6.5 - victory-chart@36.6.5 - victory-create-container@36.6.5 - victory-cursor-container@36.6.5 - victory-errorbar@36.6.5 - victory-group@36.6.5 - victory-histogram@36.6.5 - victory-legend@36.6.5 - victory-line@36.6.5 - victory-pie@36.6.5 - victory-polar-axis@36.6.5 - victory-scatter@36.6.5 - victory-selection-container@36.6.5 - victory-shared-events@36.6.5 - victory-stack@36.6.5 - victory-tooltip@36.6.5 - victory-voronoi@36.6.5 - victory-voronoi-container@36.6.5 ## 36.6.4 ### Patch Changes - Added explicit `any` type defs (fixes [#2358](https://github.com/FormidableLabs/victory/issues/2358)) ([`57ed0fe30`](https://github.com/FormidableLabs/victory/commit/57ed0fe304dbc8753da1126a02d44de8004e96aa)) * Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) * Updated dependencies [[`57ed0fe30`](https://github.com/FormidableLabs/victory/commit/57ed0fe304dbc8753da1126a02d44de8004e96aa), [`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-canvas@36.6.4 - victory-create-container@36.6.4 - victory-selection-container@36.6.4 - victory-voronoi-container@36.6.4 - victory-zoom-container@36.6.4 - victory-core@36.6.4 - victory-area@36.6.4 - victory-axis@36.6.4 - victory-bar@36.6.4 - victory-box-plot@36.6.4 - victory-brush-container@36.6.4 - victory-brush-line@36.6.4 - victory-candlestick@36.6.4 - victory-chart@36.6.4 - victory-cursor-container@36.6.4 - victory-errorbar@36.6.4 - victory-group@36.6.4 - victory-histogram@36.6.4 - victory-legend@36.6.4 - victory-line@36.6.4 - victory-pie@36.6.4 - victory-polar-axis@36.6.4 - victory-scatter@36.6.4 - victory-shared-events@36.6.4 - victory-stack@36.6.4 - victory-tooltip@36.6.4 - victory-voronoi@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-area@36.6.3 - victory-axis@36.6.3 - victory-bar@36.6.3 - victory-box-plot@36.6.3 - victory-brush-container@36.6.3 - victory-brush-line@36.6.3 - victory-candlestick@36.6.3 - victory-canvas@36.6.3 - victory-chart@36.6.3 - victory-core@36.6.3 - victory-create-container@36.6.3 - victory-cursor-container@36.6.3 - victory-errorbar@36.6.3 - victory-group@36.6.3 - victory-histogram@36.6.3 - victory-legend@36.6.3 - victory-line@36.6.3 - victory-pie@36.6.3 - victory-polar-axis@36.6.3 - victory-scatter@36.6.3 - victory-selection-container@36.6.3 - victory-shared-events@36.6.3 - victory-stack@36.6.3 - victory-tooltip@36.6.3 - victory-voronoi@36.6.3 - victory-voronoi-container@36.6.3 - victory-zoom-container@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies [[`877c16923`](https://github.com/FormidableLabs/victory/commit/877c169230c24ab02f570a21dca10a2dce0dcf4e)]: - victory-cursor-container@36.6.2 - victory-area@36.6.2 - victory-axis@36.6.2 - victory-bar@36.6.2 - victory-box-plot@36.6.2 - victory-brush-container@36.6.2 - victory-brush-line@36.6.2 - victory-candlestick@36.6.2 - victory-canvas@36.6.2 - victory-chart@36.6.2 - victory-core@36.6.2 - victory-create-container@36.6.2 - victory-errorbar@36.6.2 - victory-group@36.6.2 - victory-histogram@36.6.2 - victory-legend@36.6.2 - victory-line@36.6.2 - victory-pie@36.6.2 - victory-polar-axis@36.6.2 - victory-scatter@36.6.2 - victory-selection-container@36.6.2 - victory-shared-events@36.6.2 - victory-stack@36.6.2 - victory-tooltip@36.6.2 - victory-voronoi@36.6.2 - victory-voronoi-container@36.6.2 - victory-zoom-container@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`f7d50fa38`](https://github.com/FormidableLabs/victory/commit/f7d50fa381998c068a8b91af9818a6bc726b0dcd), [`f7d50fa38`](https://github.com/FormidableLabs/victory/commit/f7d50fa381998c068a8b91af9818a6bc726b0dcd), [`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-group@36.6.1 - victory-stack@36.6.1 - victory-core@36.6.1 - victory-axis@36.6.1 - victory-errorbar@36.6.1 - victory-scatter@36.6.1 - victory-cursor-container@36.6.1 - victory-area@36.6.1 - victory-bar@36.6.1 - victory-box-plot@36.6.1 - victory-brush-container@36.6.1 - victory-brush-line@36.6.1 - victory-candlestick@36.6.1 - victory-canvas@36.6.1 - victory-chart@36.6.1 - victory-create-container@36.6.1 - victory-histogram@36.6.1 - victory-legend@36.6.1 - victory-line@36.6.1 - victory-pie@36.6.1 - victory-polar-axis@36.6.1 - victory-selection-container@36.6.1 - victory-shared-events@36.6.1 - victory-tooltip@36.6.1 - victory-voronoi@36.6.1 - victory-voronoi-container@36.6.1 - victory-zoom-container@36.6.1 ## 36.6.0 ### Patch Changes - Updated dependencies [[`0c827edb4`](https://github.com/FormidableLabs/victory/commit/0c827edb4daf6fa61ee2a561df27ddf89ccc649f), [`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`f7bf134e5`](https://github.com/FormidableLabs/victory/commit/f7bf134e58bc1660c28f83f0eede3b19048d2656), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-box-plot@36.6.0 - victory-core@36.6.0 - victory-errorbar@36.6.0 - victory-brush-container@36.6.0 - victory-brush-line@36.6.0 - victory-area@36.6.0 - victory-axis@36.6.0 - victory-bar@36.6.0 - victory-candlestick@36.6.0 - victory-canvas@36.6.0 - victory-create-container@36.6.0 - victory-cursor-container@36.6.0 - victory-legend@36.6.0 - victory-line@36.6.0 - victory-pie@36.6.0 - victory-polar-axis@36.6.0 - victory-scatter@36.6.0 - victory-selection-container@36.6.0 - victory-shared-events@36.6.0 - victory-stack@36.6.0 - victory-tooltip@36.6.0 - victory-voronoi@36.6.0 - victory-voronoi-container@36.6.0 - victory-zoom-container@36.6.0 - victory-chart@36.6.0 - victory-group@36.6.0 - victory-histogram@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory/README.md ================================================ ## [Victory Documentation](https://commerce.nearform.com/open-source/victory) ## Getting started 1. Add Victory to your project: ```sh $ npm install victory --save ``` 2. Add your first Victory component: ```js import React, { Component } from "react"; import { render } from "react-dom"; import { VictoryPie } from "victory"; class PieChart extends Component { render() { return ; } } render(, document.getElementById("app")); ``` 3. `VictoryPie` component will be rendered, and you should see:

pie

For detailed documentation and examples please see [Victory Documentation](https://commerce.nearform.com/open-source/victory) ## Requirements Projects using Victory should also depend on [React][] and [prop-types][]. ## Victory Native Want to use `Victory` with React Native? Check out [victory-native-xl](https://github.com/FormidableLabs/victory-native-xl) If you would like to use this version of `Victory` with React Native, you can install the legacy version using the `legacy` npm tag. See the [available versions in npm](https://www.npmjs.com/package/victory-native?activeTab=versions). ## [Contributing](CONTRIBUTING.md) [react]: https://facebook.github.io/react/ [prop-types]: https://github.com/reactjs/prop-types ================================================ FILE: packages/victory/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory/package.json ================================================ { "name": "victory", "version": "37.3.6", "description": "Data viz for React", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "source": "src/index.ts", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "victory-area": "37.3.6", "victory-axis": "37.3.6", "victory-bar": "37.3.6", "victory-box-plot": "37.3.6", "victory-brush-container": "37.3.6", "victory-brush-line": "37.3.6", "victory-candlestick": "37.3.6", "victory-canvas": "37.3.6", "victory-chart": "37.3.6", "victory-core": "37.3.6", "victory-create-container": "37.3.6", "victory-cursor-container": "37.3.6", "victory-errorbar": "37.3.6", "victory-group": "37.3.6", "victory-histogram": "37.3.6", "victory-legend": "37.3.6", "victory-line": "37.3.6", "victory-pie": "37.3.6", "victory-polar-axis": "37.3.6", "victory-scatter": "37.3.6", "victory-selection-container": "37.3.6", "victory-shared-events": "37.3.6", "victory-stack": "37.3.6", "victory-tooltip": "37.3.6", "victory-voronoi": "37.3.6", "victory-voronoi-container": "37.3.6", "victory-zoom-container": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-area:build:lib:esm", "../victory-axis:build:lib:esm", "../victory-bar:build:lib:esm", "../victory-box-plot:build:lib:esm", "../victory-brush-container:build:lib:esm", "../victory-brush-line:build:lib:esm", "../victory-candlestick:build:lib:esm", "../victory-canvas:build:lib:esm", "../victory-chart:build:lib:esm", "../victory-core:build:lib:esm", "../victory-create-container:build:lib:esm", "../victory-cursor-container:build:lib:esm", "../victory-errorbar:build:lib:esm", "../victory-group:build:lib:esm", "../victory-histogram:build:lib:esm", "../victory-legend:build:lib:esm", "../victory-line:build:lib:esm", "../victory-pie:build:lib:esm", "../victory-polar-axis:build:lib:esm", "../victory-scatter:build:lib:esm", "../victory-selection-container:build:lib:esm", "../victory-shared-events:build:lib:esm", "../victory-stack:build:lib:esm", "../victory-tooltip:build:lib:esm", "../victory-voronoi:build:lib:esm", "../victory-voronoi-container:build:lib:esm", "../victory-zoom-container:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-area:build:lib:cjs", "../victory-axis:build:lib:cjs", "../victory-bar:build:lib:cjs", "../victory-box-plot:build:lib:cjs", "../victory-brush-container:build:lib:cjs", "../victory-brush-line:build:lib:cjs", "../victory-candlestick:build:lib:cjs", "../victory-canvas:build:lib:cjs", "../victory-chart:build:lib:cjs", "../victory-core:build:lib:cjs", "../victory-create-container:build:lib:cjs", "../victory-cursor-container:build:lib:cjs", "../victory-errorbar:build:lib:cjs", "../victory-group:build:lib:cjs", "../victory-histogram:build:lib:cjs", "../victory-legend:build:lib:cjs", "../victory-line:build:lib:cjs", "../victory-pie:build:lib:cjs", "../victory-polar-axis:build:lib:cjs", "../victory-scatter:build:lib:cjs", "../victory-selection-container:build:lib:cjs", "../victory-shared-events:build:lib:cjs", "../victory-stack:build:lib:cjs", "../victory-tooltip:build:lib:cjs", "../victory-voronoi:build:lib:cjs", "../victory-voronoi-container:build:lib:cjs", "../victory-zoom-container:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-area:build:lib:esm", "../victory-axis:build:lib:esm", "../victory-bar:build:lib:esm", "../victory-box-plot:build:lib:esm", "../victory-brush-container:build:lib:esm", "../victory-brush-line:build:lib:esm", "../victory-candlestick:build:lib:esm", "../victory-canvas:build:lib:esm", "../victory-chart:build:lib:esm", "../victory-core:build:lib:esm", "../victory-create-container:build:lib:esm", "../victory-cursor-container:build:lib:esm", "../victory-errorbar:build:lib:esm", "../victory-group:build:lib:esm", "../victory-histogram:build:lib:esm", "../victory-legend:build:lib:esm", "../victory-line:build:lib:esm", "../victory-pie:build:lib:esm", "../victory-polar-axis:build:lib:esm", "../victory-scatter:build:lib:esm", "../victory-selection-container:build:lib:esm", "../victory-shared-events:build:lib:esm", "../victory-stack:build:lib:esm", "../victory-tooltip:build:lib:esm", "../victory-voronoi:build:lib:esm", "../victory-voronoi-container:build:lib:esm", "../victory-zoom-container:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-area:build:lib:esm", "../victory-axis:build:lib:esm", "../victory-bar:build:lib:esm", "../victory-box-plot:build:lib:esm", "../victory-brush-container:build:lib:esm", "../victory-brush-line:build:lib:esm", "../victory-candlestick:build:lib:esm", "../victory-canvas:build:lib:esm", "../victory-chart:build:lib:esm", "../victory-core:build:lib:esm", "../victory-create-container:build:lib:esm", "../victory-cursor-container:build:lib:esm", "../victory-errorbar:build:lib:esm", "../victory-group:build:lib:esm", "../victory-histogram:build:lib:esm", "../victory-legend:build:lib:esm", "../victory-line:build:lib:esm", "../victory-pie:build:lib:esm", "../victory-polar-axis:build:lib:esm", "../victory-scatter:build:lib:esm", "../victory-selection-container:build:lib:esm", "../victory-shared-events:build:lib:esm", "../victory-stack:build:lib:esm", "../victory-tooltip:build:lib:esm", "../victory-voronoi:build:lib:esm", "../victory-voronoi-container:build:lib:esm", "../victory-zoom-container:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-area:types:create", "../victory-axis:types:create", "../victory-bar:types:create", "../victory-box-plot:types:create", "../victory-brush-container:types:create", "../victory-brush-line:types:create", "../victory-candlestick:types:create", "../victory-canvas:types:create", "../victory-chart:types:create", "../victory-core:types:create", "../victory-create-container:types:create", "../victory-cursor-container:types:create", "../victory-errorbar:types:create", "../victory-group:types:create", "../victory-histogram:types:create", "../victory-legend:types:create", "../victory-line:types:create", "../victory-pie:types:create", "../victory-polar-axis:types:create", "../victory-scatter:types:create", "../victory-selection-container:types:create", "../victory-shared-events:types:create", "../victory-stack:types:create", "../victory-tooltip:types:create", "../victory-voronoi:types:create", "../victory-voronoi-container:types:create", "../victory-zoom-container:types:create", "../victory-vendor:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-area:types:create", "../victory-axis:types:create", "../victory-bar:types:create", "../victory-box-plot:types:create", "../victory-brush-container:types:create", "../victory-brush-line:types:create", "../victory-candlestick:types:create", "../victory-canvas:types:create", "../victory-chart:types:create", "../victory-core:types:create", "../victory-create-container:types:create", "../victory-cursor-container:types:create", "../victory-errorbar:types:create", "../victory-group:types:create", "../victory-histogram:types:create", "../victory-legend:types:create", "../victory-line:types:create", "../victory-pie:types:create", "../victory-polar-axis:types:create", "../victory-scatter:types:create", "../victory-selection-container:types:create", "../victory-shared-events:types:create", "../victory-stack:types:create", "../victory-tooltip:types:create", "../victory-voronoi:types:create", "../victory-voronoi-container:types:create", "../victory-zoom-container:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-area:types:create", "../victory-axis:types:create", "../victory-bar:types:create", "../victory-box-plot:types:create", "../victory-brush-container:types:create", "../victory-brush-line:types:create", "../victory-candlestick:types:create", "../victory-canvas:types:create", "../victory-chart:types:create", "../victory-core:types:create", "../victory-create-container:types:create", "../victory-cursor-container:types:create", "../victory-errorbar:types:create", "../victory-group:types:create", "../victory-histogram:types:create", "../victory-legend:types:create", "../victory-line:types:create", "../victory-pie:types:create", "../victory-polar-axis:types:create", "../victory-scatter:types:create", "../victory-selection-container:types:create", "../victory-shared-events:types:create", "../victory-stack:types:create", "../victory-tooltip:types:create", "../victory-voronoi:types:create", "../victory-voronoi-container:types:create", "../victory-zoom-container:types:create", "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-area:types:create", "../victory-axis:types:create", "../victory-bar:types:create", "../victory-box-plot:types:create", "../victory-brush-container:types:create", "../victory-brush-line:types:create", "../victory-candlestick:types:create", "../victory-canvas:types:create", "../victory-chart:types:create", "../victory-core:types:create", "../victory-create-container:types:create", "../victory-cursor-container:types:create", "../victory-errorbar:types:create", "../victory-group:types:create", "../victory-histogram:types:create", "../victory-legend:types:create", "../victory-line:types:create", "../victory-pie:types:create", "../victory-polar-axis:types:create", "../victory-scatter:types:create", "../victory-selection-container:types:create", "../victory-shared-events:types:create", "../victory-stack:types:create", "../victory-tooltip:types:create", "../victory-voronoi:types:create", "../victory-voronoi-container:types:create", "../victory-zoom-container:types:create", "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-area:build", "../victory-axis:build", "../victory-bar:build", "../victory-box-plot:build", "../victory-brush-container:build", "../victory-brush-line:build", "../victory-candlestick:build", "../victory-canvas:build", "../victory-chart:build", "../victory-core:build", "../victory-create-container:build", "../victory-cursor-container:build", "../victory-errorbar:build", "../victory-group:build", "../victory-histogram:build", "../victory-legend:build", "../victory-line:build", "../victory-pie:build", "../victory-polar-axis:build", "../victory-scatter:build", "../victory-selection-container:build", "../victory-shared-events:build", "../victory-stack:build", "../victory-tooltip:build", "../victory-voronoi:build", "../victory-voronoi-container:build", "../victory-zoom-container:build", "../victory-vendor:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory/src/index.ts ================================================ export * from "victory-area"; export * from "victory-axis"; export * from "victory-bar"; export * from "victory-box-plot"; export * from "victory-brush-container"; export * from "victory-brush-line"; export * from "victory-candlestick"; export * from "victory-canvas"; export * from "victory-chart"; export * from "victory-core"; export * from "victory-create-container"; export * from "victory-cursor-container"; export * from "victory-errorbar"; export * from "victory-group"; export * from "victory-histogram"; export * from "victory-legend"; export * from "victory-line"; export * from "victory-pie"; export * from "victory-polar-axis"; export * from "victory-scatter"; export * from "victory-selection-container"; export * from "victory-shared-events"; export * from "victory-stack"; export * from "victory-tooltip"; export * from "victory-voronoi"; export * from "victory-voronoi-container"; export * from "victory-zoom-container"; ================================================ FILE: packages/victory/src/victory.test.ts ================================================ /* eslint-disable @typescript-eslint/no-unused-vars */ import * as Victory from "./index"; import { Arc, ArcProps, Area, AreaProps, Axis, Background, BackgroundProps, Bar, BarProps, Border, BorderProps, Box, BoxProps, BrushHelpers, Candle, CandleProps, CanvasBar, CanvasBarProps, CanvasCurve, CanvasCurveProps, CanvasGroup, CanvasGroupProps, CanvasPoint, CanvasPointProps, Circle, ClipPath, ClipPathProps, Collection, ContainerType, CursorHelpers, Curve, CurveProps, Data, DefaultTransitions, Domain, ErrorBar, ErrorBarProps, Events, Flyout, FlyoutProps, Helpers, LabelHelpers, Line, LineSegment, LineSegmentProps, Log, Path, Point, PointProps, Portal, RawZoomHelpers, Rect, Scale, Selection, SelectionHelpers, Slice, SliceProps, Style, TSpan, Text, TextProps, TextSize, Transitions, UserProps, VictoryAccessibleGroup, VictoryAccessibleGroupProps, VictoryAnimation, VictoryAnimationProps, VictoryArea, VictoryAreaProps, VictoryAxis, VictoryAxisProps, VictoryBar, VictoryBarProps, VictoryBoxPlot, VictoryBoxPlotProps, VictoryBrushContainer, VictoryBrushContainerProps, VictoryBrushLine, VictoryBrushLineProps, VictoryCandlestick, VictoryCandlestickProps, VictoryChart, VictoryChartProps, VictoryClipContainer, VictoryClipContainerProps, VictoryContainer, VictoryContainerProps, VictoryCursorContainer, VictoryCursorContainerProps, VictoryErrorBar, VictoryErrorBarProps, VictoryGroup, VictoryGroupProps, VictoryHistogram, VictoryHistogramProps, VictoryLabel, VictoryLabelProps, VictoryLegend, VictoryLegendProps, VictoryLine, VictoryLineProps, VictoryPie, VictoryPieProps, VictoryPolarAxis, VictoryPolarAxisProps, VictoryPortal, VictoryPortalProps, VictoryScatter, VictoryScatterProps, VictorySelectionContainer, VictorySelectionContainerProps, VictorySharedEvents, VictorySharedEventsProps, VictoryStack, VictoryStackProps, VictoryTheme, VictoryThemeDefinition, VictoryTooltip, VictoryTooltipProps, VictoryTransition, VictoryVoronoi, VictoryVoronoiContainer, VictoryVoronoiContainerProps, VictoryVoronoiProps, VictoryZoomContainer, VictoryZoomContainerProps, Voronoi, VoronoiHelpers, VoronoiProps, Whisker, WhiskerProps, Wrapper, ZoomHelpers, addEvents, createContainer, makeCreateContainerFunction, useCanvasContext, useVictoryBrushContainer, useVictoryCursorContainer, useVictorySelectionContainer, useVictoryVoronoiContainer, useVictoryZoomContainer, VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS, VICTORY_CURSOR_CONTAINER_DEFAULT_PROPS, VICTORY_SELECTION_CONTAINER_DEFAULT_PROPS, VICTORY_VORONOI_CONTAINER_DEFAULT_PROPS, VICTORY_ZOOM_CONTAINER_DEFAULT_PROPS, mergeRefs, useVictoryContainer, } from "./index"; describe("victory", () => { it("ensure it has named exports", () => { expect(Area).toBeInstanceOf(Function); }); xit("ensure all components have valid types", () => { /* * See https://github.com/FormidableLabs/victory/issues/2411 * It's easy for some of our Components to accidentally get typed as 'any'. * This seems to be due to our use of mixins, especially `addEvents`. * * This test is designed to catch those errors, so that we can be sure * that all of our components are exported with the proper types. */ let shouldNotBeTypedAsAny: { SHOULD_NOT_BE_TYPED_AS_ANY: true }; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryAccessibleGroup; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryAccessibleGroupProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryAnimation; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryAnimationProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryArea; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryAreaProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryAxis; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryAxisProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryBar; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryBarProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryBoxPlot; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryBoxPlotProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryBrushContainer; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryBrushContainerProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryBrushLine; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryBrushLineProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryCandlestick; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryCandlestickProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryChart; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryChartProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryClipContainer; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryClipContainerProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryContainer; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryContainerProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryCursorContainer; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryCursorContainerProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryErrorBar; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryErrorBarProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryGroup; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryGroupProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryHistogram; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryHistogramProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryLabel; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryLabelProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryLegend; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryLegendProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryLine; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryLineProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryPie; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryPieProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryPolarAxis; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryPolarAxisProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryPortal; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryPortalProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryScatter; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryScatterProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictorySelectionContainer; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictorySelectionContainerProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictorySharedEvents; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictorySharedEventsProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryStack; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryStackProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryTheme; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryThemeDefinition; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryTooltip; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryTooltipProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryTransition; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryVoronoi; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryVoronoiContainer; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryVoronoiContainerProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryVoronoiProps; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryZoomContainer; // @ts-expect-error This will fail if the component is typed as 'any': shouldNotBeTypedAsAny = VictoryZoomContainerProps; }); it("ensure everything is exported correctly", () => { expect(Object.keys(Victory).sort()).toMatchInlineSnapshot(` [ "Arc", "Area", "Axis", "Background", "Bar", "Border", "Box", "BrushHelpers", "Candle", "CanvasBar", "CanvasCurve", "CanvasGroup", "CanvasPoint", "Circle", "ClipPath", "Collection", "CursorHelpers", "Curve", "Data", "DefaultTransitions", "Domain", "ErrorBar", "Events", "Flyout", "Helpers", "Hooks", "Immutable", "LabelHelpers", "Line", "LineHelpers", "LineSegment", "Log", "Path", "Point", "PointPathHelpers", "Portal", "PortalContext", "PortalOutlet", "PortalProvider", "RawZoomHelpers", "Rect", "Scale", "Selection", "SelectionHelpers", "Slice", "Style", "TSpan", "Text", "TextSize", "Timer", "TimerContext", "Transitions", "UserProps", "VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS", "VICTORY_CURSOR_CONTAINER_DEFAULT_PROPS", "VICTORY_SELECTION_CONTAINER_DEFAULT_PROPS", "VICTORY_VORONOI_CONTAINER_DEFAULT_PROPS", "VICTORY_ZOOM_CONTAINER_DEFAULT_PROPS", "VictoryAccessibleGroup", "VictoryAnimation", "VictoryArea", "VictoryAxis", "VictoryBar", "VictoryBoxPlot", "VictoryBrushContainer", "VictoryBrushLine", "VictoryCandlestick", "VictoryChart", "VictoryClipContainer", "VictoryContainer", "VictoryCursorContainer", "VictoryErrorBar", "VictoryGroup", "VictoryHistogram", "VictoryLabel", "VictoryLegend", "VictoryLine", "VictoryPie", "VictoryPolarAxis", "VictoryPortal", "VictoryScatter", "VictorySelectionContainer", "VictorySharedEvents", "VictoryStack", "VictoryTheme", "VictoryTooltip", "VictoryTransition", "VictoryVoronoi", "VictoryVoronoiContainer", "VictoryZoomContainer", "Voronoi", "VoronoiHelpers", "Whisker", "Wrapper", "ZoomHelpers", "addEvents", "createContainer", "getBarPath", "getBarPosition", "getBarWidth", "getCornerRadius", "getCustomBarPath", "getHorizontalBarPath", "getPolarBarPath", "getStyle", "getVerticalBarPath", "getVerticalPolarBarPath", "makeCreateContainerFunction", "mergeRefs", "useCanvasContext", "usePortalContext", "useVictoryBrushContainer", "useVictoryContainer", "useVictoryCursorContainer", "useVictorySelectionContainer", "useVictoryVoronoiContainer", "useVictoryZoomContainer", ] `); }); }); ================================================ FILE: packages/victory/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-area/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-area/CHANGELOG.md ================================================ # victory-area ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Upgrade typescript to 5.7.2 ([#2997](https://github.com/FormidableLabs/victory/pull/2997)) * Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ### Patch Changes - Remove duplicate types from interfaces ([#2940](https://github.com/FormidableLabs/victory/pull/2940)) ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash isNil and isNan with native code ([#2800](https://github.com/FormidableLabs/victory/pull/2800)) * Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ### Patch Changes - Migrate victory-native to TypeScript ([#2739](https://github.com/FormidableLabs/victory/pull/2739)) ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) * Replace instances of lodash.range with equivalent native code ([#2760](https://github.com/FormidableLabs/victory/pull/2760)) ## 36.8.4 ## 36.8.3 ## 36.8.2 ## 36.8.1 ## 36.8.0 ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 - victory-vendor@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 - victory-vendor@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 - victory-vendor@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416), [`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 - victory-vendor@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 - victory-vendor@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 - victory-vendor@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 - victory-vendor@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 - victory-vendor@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 - victory-vendor@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-area/README.md ================================================ # VictoryArea `victory-area@^30.0.0` exports `VictoryArea` and `Area` components To view documentation for `VictoryArea` please see https://commerce.nearform.com/open-source/victory/docs/victory-area To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-area.md ================================================ FILE: packages/victory-area/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-area/package.json ================================================ { "name": "victory-area", "version": "37.3.6", "description": "Area Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6", "victory-vendor": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "devDependencies": { "victory-chart": "*" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs", "../victory-vendor:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-chart:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-area/src/area.test.tsx ================================================ import React from "react"; import { render } from "@testing-library/react"; import { VictoryContainer } from "victory-core"; import * as d3Scale from "victory-vendor/d3-scale"; import { Area } from "./area"; describe("victory-primitives/area", () => { const baseProps = { data: [ { _x1: 1, x1: 1, _y1: 4, y1: 4, _y0: 0, eventKey: 0 }, { _x1: 2, x1: 2, _y1: 5, y1: 5, _y0: 0, eventKey: 1 }, { _x1: 3, x1: 3, _y1: 7, y1: 7, _y0: 0, eventKey: 2 }, { _x1: 4, x1: 4, _y1: 10, y1: 10, _y0: 0, eventKey: 3 }, { _x1: 5, x1: 5, _y1: 15, y1: 15, _y0: 0, eventKey: 4 }, ], scale: { x: d3Scale.scaleLinear(), y: d3Scale.scaleLinear(), }, interpolation: "basis", style: { stroke: "tomato", }, }; it("should render a single area and no line when no line style is given", () => { const props = Object.assign({}, baseProps, { style: { stroke: "none", }, }); const { container } = render( , ); expect(container.querySelectorAll("path")).toHaveLength(1); }); it("should render an area and line when a line style is given", () => { const { container } = render( , ); // multiple paths should be grouped expect(container.querySelectorAll("path")).toHaveLength(2); }); }); ================================================ FILE: packages/victory-area/src/area.tsx ================================================ /* eslint no-magic-numbers: ["error", { "ignore": [-1, 0, 1, 2] }]*/ import React from "react"; import defaults from "lodash/defaults"; import * as d3Shape from "victory-vendor/d3-shape"; import { Helpers, Path, UserProps, VictoryCommonPrimitiveProps, LineHelpers, VictoryCommonThemeProps, } from "victory-core"; const defined = (d) => { const y = d._y1 !== undefined ? d._y1 : d._y; return y !== null && y !== undefined && d._y0 !== null; }; const getXAccessor = (scale) => { return (d) => scale.x(d._x1 !== undefined ? d._x1 : d._x); }; const getYAccessor = (scale) => { return (d) => scale.y(d._y1 !== undefined ? d._y1 : d._y); }; const getY0Accessor = (scale) => { return (d) => scale.y(d._y0); }; const getAngleAccessor = (scale) => { return (d) => { const x = scale.x(d._x1 !== undefined ? d._x1 : d._x); return -1 * x + Math.PI / 2; }; }; const getCartesianArea = (props: AreaProps) => { const { horizontal, scale } = props; const interpolationFunction = LineHelpers.getInterpolationFunction(props); return horizontal ? d3Shape .area() .defined(defined) .curve(interpolationFunction) .x0(getY0Accessor(scale)) .x1(getYAccessor(scale)) .y(getXAccessor(scale)) : d3Shape .area() .defined(defined) .curve(interpolationFunction) .x(getXAccessor(scale)) .y1(getYAccessor(scale)) .y0(getY0Accessor(scale)); }; const getAreaFunction = (props: AreaProps) => { const { polar, scale } = props; const interpolationFunction = LineHelpers.getInterpolationFunction(props); return polar ? d3Shape .radialArea() .defined(defined) .curve(interpolationFunction) .angle(getAngleAccessor(scale)) .outerRadius(getYAccessor(scale)) .innerRadius(getY0Accessor(scale)) : getCartesianArea(props); }; const evaluateProps = (props: AreaProps) => { /** * Potential evaluated props are: * `ariaLabel` * `desc` * `id` * `style` * `tabIndex` */ const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const desc = Helpers.evaluateProp(props.desc, props); const id = Helpers.evaluateProp(props.id, props); const style = Helpers.evaluateStyle( Object.assign({ fill: "black" }, props.style), props, ); const tabIndex = Helpers.evaluateProp(props.tabIndex, props); return Object.assign({}, props, { ariaLabel, desc, id, style, tabIndex }); }; const defaultProps = { groupComponent: , pathComponent: , role: "presentation", shapeRendering: "auto", }; /** * The area primitive used by VictoryArea */ export const Area: React.FC = (initialProps) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); const { ariaLabel, role, shapeRendering, className, polar, origin, data, pathComponent, events, groupComponent, clipPath, id, style, desc, tabIndex, } = props; const userProps = UserProps.getSafeUserProps(props); const defaultTransform = polar && origin ? `translate(${origin.x}, ${origin.y})` : undefined; const transform = props.transform || defaultTransform; const renderLine = style.stroke && style.stroke !== "none" && style.stroke !== "transparent"; const areaFunction = getAreaFunction(props); const lineFunction = renderLine && LineHelpers.getLineFunction(props); const areaStroke = style.stroke ? "none" : style.fill; const sharedProps = { "aria-label": ariaLabel, className, role, shapeRendering, transform, ...events, clipPath, tabIndex, }; const area = React.cloneElement( pathComponent!, Object.assign( { key: `${id}-area`, style: Object.assign({}, style, { stroke: areaStroke }), d: areaFunction(data), desc, tabIndex, }, sharedProps, userProps, ), ); const line = renderLine ? React.cloneElement( pathComponent!, Object.assign( { key: `${id}-area-stroke`, style: Object.assign({}, style, { fill: "none" }), d: lineFunction(data), }, sharedProps, ), ) : null; return renderLine ? React.cloneElement(groupComponent!, userProps, [area, line]) : area; }; export interface AreaProps extends VictoryCommonPrimitiveProps { horizontal?: VictoryCommonThemeProps["horizontal"]; groupComponent?: React.ReactElement; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type interpolation?: string | Function; pathComponent?: React.ReactElement; } ================================================ FILE: packages/victory-area/src/helper-methods.test.tsx ================================================ import * as d3Scale from "victory-vendor/d3-scale"; import { getDataWithBaseline } from "./helper-methods"; describe("victory-area/helper-methods", () => { describe("getDataWithBaseline", () => { const data = [ { _x: 1, _y: 1 }, { _x: 2, _y: 1 }, ]; const stackedData = [ { _x: 1, _x0: 0, _x1: 1, _y: 1, _y0: 1, _y1: 2 }, { _x: 2, _x0: 0, _x1: 2, _y: 1, _y0: 1, _y1: 2 }, ]; const defaultDomain = { x: [0, 10], y: [0, 10] }; const nonZeroDomain = { x: [0, 10], y: [1, 10] }; const negativeDomain = { x: [0, 10], y: [-1, 10] }; const scale = (domain) => { return { x: d3Scale.scaleLinear().domain(domain.x), y: d3Scale.scaleLinear().domain(domain.y), }; }; it("should return the minimum if yOffset is not present", () => { const props = { data }; const result = getDataWithBaseline(props, scale(defaultDomain)); const expectedResult = [ { _y0: 0, _y1: 1, _y: 1, _x: 1, _x0: 0, _x1: 1 }, { _y0: 0, _y1: 1, _y: 1, _x: 2, _x0: 0, _x1: 2 }, ]; expect(result).toEqual(expectedResult); }); it("should return the domain minimum when it is greater than zero", () => { const props = { data }; const result = getDataWithBaseline(props, scale(nonZeroDomain)); const expectedResult = [ { _y0: 1, _y1: 1, _y: 1, _x: 1, _x0: 0, _x1: 1 }, { _y0: 1, _y1: 1, _y: 1, _x: 2, _x0: 0, _x1: 2 }, ]; expect(result).toEqual(expectedResult); }); it("should return zero when the domain minimum is negative", () => { const props = { data }; const result = getDataWithBaseline(props, scale(negativeDomain)); const expectedResult = [ { _y0: 0, _y1: 1, _y: 1, _x: 1, _x0: 0, _x1: 1 }, { _y0: 0, _y1: 1, _y: 1, _x: 2, _x0: 0, _x1: 2 }, ]; expect(result).toEqual(expectedResult); }); it("should return yOffset if present", () => { const props = { data: stackedData }; const result = getDataWithBaseline(props, scale(defaultDomain)); const expectedResult = stackedData; expect(result).toEqual(expectedResult); }); }); }); ================================================ FILE: packages/victory-area/src/helper-methods.tsx ================================================ import { Helpers, LabelHelpers, Data, Domain, Scale, Collection, } from "victory-core"; export const getDataWithBaseline = (props, scale) => { let data = Data.getData(props); if (data.length < 2) { data = []; } const getDefaultMin = (axis) => { const defaultZero = Scale.getType(scale[axis]) === "log" ? 1 / Number.MAX_SAFE_INTEGER : 0; const domain = scale[axis].domain(); const minY = Collection.getMinValue(domain); const maxY = Collection.getMaxValue(domain); let defaultMin: typeof minY = defaultZero; if (minY.valueOf() < 0 && maxY.valueOf() <= 0) { defaultMin = maxY; } else if (minY.valueOf() >= 0 && maxY.valueOf() > 0) { defaultMin = minY; } return Collection.containsDates(domain) ? new Date(defaultMin) : defaultMin; }; return data.map((datum) => { const _y1 = datum._y1 !== undefined ? datum._y1 : datum._y; const _y0 = datum._y0 !== undefined ? datum._y0 : getDefaultMin("y"); const _x1 = datum._x1 !== undefined ? datum._x1 : datum._x; const _x0 = datum._x0 !== undefined ? datum._x0 : getDefaultMin("x"); return Object.assign({}, datum, { _y0, _y1, _x0, _x1 }); }); }; const getCalculatedValues = (props) => { const { polar } = props; const defaultStyles = Helpers.getDefaultStyles(props, "area"); const style = Helpers.getStyles(props.style, defaultStyles); const range = { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; const domain = { x: Domain.getDomainWithZero(props, "x"), y: Domain.getDomainWithZero(props, "y"), }; const scale = { x: Scale.getBaseScale(props, "x") .domain(domain.x) .range(props.horizontal ? range.y : range.x), y: Scale.getBaseScale(props, "y") .domain(domain.y) .range(props.horizontal ? range.x : range.y), }; const origin = polar ? props.origin || Helpers.getPolarOrigin(props) : undefined; const data = getDataWithBaseline(props, scale); return { style, data, scale, domain, origin }; }; export const getBaseProps = (initialProps, fallbackProps) => { const modifiedProps = Helpers.modifyProps( initialProps, fallbackProps, "area", ); const props = Object.assign( {}, modifiedProps, getCalculatedValues(modifiedProps), ); const { data, domain, events, groupComponent, height, horizontal, interpolation, origin, padding, polar, scale, sharedEvents, standalone, style, theme, width, labels, name, disableInlineStyles, } = props; const initialChildProps = { parent: { style: style.parent, width, height, scale, data, domain, standalone, theme, polar, origin, padding, name, horizontal, }, all: { data: { horizontal, polar, origin, scale, data, interpolation, groupComponent, style: disableInlineStyles ? {} : style.data, disableInlineStyles, }, }, }; return data.reduce((childProps, datum, index) => { const text = LabelHelpers.getText(props, datum, index); if ( (text !== undefined && text !== null) || (labels && (events || sharedEvents)) ) { const eventKey = !Helpers.isNil(datum.eventKey) ? datum.eventKey : index; childProps[eventKey] = { labels: LabelHelpers.getProps(props, index) }; } return childProps; }, initialChildProps); }; ================================================ FILE: packages/victory-area/src/index.ts ================================================ export * from "./victory-area"; export * from "./area"; ================================================ FILE: packages/victory-area/src/victory-area.test.tsx ================================================ import { fireEvent, render, screen } from "@testing-library/react"; import React from "react"; import { VictoryChart } from "victory-chart"; import { Helpers } from "victory-core"; import { curveCatmullRom } from "victory-vendor/d3-shape"; import { calculateD3Path } from "../../../test/helpers/svg"; import { Area } from "./area"; import { VictoryArea, VictoryAreaProps } from "./victory-area"; describe("components/victory-area", () => { describe("default component rendering", () => { it("attaches safe user props to the container component", () => { render( , ); const container = screen.getByTestId("victory-area"); expect(screen.getByLabelText("Chart")).toBeDefined(); expect(container).not.toHaveAttribute("unsafe-prop"); expect(container.nodeName).toEqual("svg"); }); it("attaches safe user props to the group component if the component is rendered inside a VictoryChart", () => { render( , { wrapper: VictoryChart as any }, ); const container = screen.getByTestId("victory-area"); expect(screen.getByLabelText("Chart")).toBeDefined(); expect(container).not.toHaveAttribute("unsafe-prop"); expect(container.nodeName).toEqual("g"); }); it("renders an svg with the correct viewbox", () => { const { container } = render(); const viewBoxValue = `0 0 ${450} ${300}`; expect(container.querySelector("svg")!.getAttribute("viewBox")).toEqual( viewBoxValue, ); }); }); describe("component rendering with data", () => { it("renders the correct d3 path", () => { const props: VictoryAreaProps = { width: 400, height: 300, padding: 50, scale: "linear", interpolation: "linear", data: [ { x: 0, y: 0, y0: 0 }, { x: 2, y: 3, y0: 0 }, { x: 4, y: 1, y0: 0 }, ], }; const { container } = render(); expect(container.querySelector("path")!.getAttribute("d")).toEqual( calculateD3Path(props, "area"), ); }); it("renders the correct d3 path with custom interpolation string property", () => { const props: VictoryAreaProps = { interpolation: "catmullRom", width: 400, height: 300, padding: 50, scale: "linear", data: [ { x: 0, y: 0, y0: 0 }, { x: 2, y: 3, y0: 0 }, { x: 4, y: 1, y0: 0 }, ], }; const { container } = render(); expect(container.querySelector("path")!.getAttribute("d")).toEqual( calculateD3Path(props, "area"), ); }); it("renders the correct d3 path with custom interpolation function", () => { const props: VictoryAreaProps = { interpolation: curveCatmullRom, width: 400, height: 300, padding: 50, scale: "linear", data: [ { x: 0, y: 0, y0: 0 }, { x: 2, y: 3, y0: 0 }, { x: 4, y: 1, y0: 0 }, ], }; const { container } = render(); expect(container.querySelector("path")!.getAttribute("d")).toEqual( calculateD3Path(props, "area"), ); }); it("sorts data according to sortKey prop", () => { const props: VictoryAreaProps = { scale: "linear", interpolation: "linear", sortKey: "x", data: Helpers.range(5) .map((i) => ({ x: i, y: i, y0: 0 })) .reverse(), }; render( JSON.stringify(data)} /> } />, ); const area = screen.getByTestId("area"); const data = JSON.parse(area.getAttribute("data-json")!); const xValues = data.map((d) => d._x); expect(xValues).toEqual([0, 1, 2, 3, 4]); }); it("sorts data according to sortOrder prop", () => { const props: VictoryAreaProps = { scale: "linear", interpolation: "linear", sortKey: "x", sortOrder: "descending", data: Helpers.range(5) .map((i) => ({ x: i, y: i, y0: 0 })) .reverse(), }; render( JSON.stringify(data)} /> } />, ); const area = screen.getByTestId("area"); const data = JSON.parse(area.getAttribute("data-json")!); const xValues = data.map((d) => d._x); expect(xValues).toEqual([4, 3, 2, 1, 0]); }); }); describe("event handling", () => { it("attaches an event to the parent svg", () => { const clickHandler = jest.fn(); render( , ); const svg = screen.getByTestId("container"); fireEvent.click(svg); expect(clickHandler).toHaveBeenCalled(); }); it("attaches an event to data", () => { const clickHandler = jest.fn(); render( } events={[ { target: "data", eventHandlers: { onClick: clickHandler }, }, ]} />, ); const dataComponent = screen.getByTestId("data"); fireEvent.click(dataComponent); expect(clickHandler).toHaveBeenCalled(); }); it("attaches an event to a label", () => { const clickHandler = jest.fn(); render( datum.x} events={[ { target: "labels", eventHandlers: { onClick: clickHandler }, }, ]} />, ); fireEvent.click(screen.getByText("1")); expect(clickHandler).toHaveBeenCalled(); }); }); describe("accessibility", () => { it("adds an aria role to the path area", () => { const { container } = render(); container.querySelectorAll("path").forEach((p) => { expect(p.getAttribute("role")).toBe("presentation"); }); }); it("adds aria-label and tabIndex to Area primitive", () => { const ariaTestData = [ { x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 5 }, { x: 4, y: 4 }, { x: 5, y: 7 }, ]; const { container } = render( `data point 1's x value is ${data![0].x}` } tabIndex={4} /> } />, ); container.querySelectorAll("path").forEach((p, i) => { expect(p.getAttribute("aria-label")).toEqual( `data point 1's x value is ${ariaTestData[i].x}`, ); expect(parseInt(p.getAttribute("tabindex")!)).toEqual(4); }); }); }); }); ================================================ FILE: packages/victory-area/src/victory-area.tsx ================================================ import React from "react"; import { getBaseProps } from "./helper-methods"; import { Area } from "./area"; import { Helpers, VictoryLabel, VictoryContainer, DefaultTransitions, VictoryClipContainer, addEvents, VictoryTheme, Data, Domain, UserProps, EventPropTypeInterface, InterpolationPropType, StringOrNumberOrCallback, VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, VictoryStyleInterface, EventsMixinClass, } from "victory-core"; const fallbackProps = { width: 450, height: 300, padding: 50, interpolation: "linear", }; const options = { components: [ { name: "parent", index: "parent" }, { name: "data", index: "all" }, { name: "labels" }, ], }; export type VictoryAreaTTargetType = "data" | "labels" | "parent"; export interface VictoryAreaProps extends VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps { eventKey?: string[] | number[] | StringOrNumberOrCallback; events?: EventPropTypeInterface[]; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type interpolation?: InterpolationPropType | Function; style?: VictoryStyleInterface; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryAreaBase extends EventsMixinClass {} /** * Draw area charts with React. VictoryArea is a composable component, so it doesn't include axes. * Add VictoryArea as a child of VictoryChart for a complete chart. */ class VictoryAreaBase extends React.Component { static animationWhitelist = [ "data", "domain", "height", "padding", "style", "width", ]; static defaultProps: VictoryAreaProps = { containerComponent: , dataComponent: , groupComponent: , labelComponent: , samples: 50, sortKey: "x", sortOrder: "ascending", standalone: true, theme: VictoryTheme.grayscale, }; static displayName = "VictoryArea"; static role = "area"; static continuous = true; static defaultTransitions = DefaultTransitions.continuousTransitions(); static defaultPolarTransitions = DefaultTransitions.continuousPolarTransitions(); static getDomain = Domain.getDomainWithZero; static getData = Data.getData; static getBaseProps(props) { return getBaseProps(props, fallbackProps); } static expectedComponents = [ "dataComponent", "labelComponent", "groupComponent", "containerComponent", ]; // Overridden in native versions shouldAnimate() { return !!this.props.animate; } render() { const { animationWhitelist, role } = VictoryAreaBase; const props = Helpers.modifyProps(this.props, fallbackProps, role); if (this.shouldAnimate()) { return this.animateComponent(props, animationWhitelist); } const children = this.renderContinuousData(props); const component = props.standalone ? this.renderContainer(props.containerComponent, children) : children; return UserProps.withSafeUserProps(component, props); } } export const VictoryArea = addEvents(VictoryAreaBase, options); ================================================ FILE: packages/victory-area/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-area/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-axis/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-axis/CHANGELOG.md ================================================ # victory-axis ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) * Improve types in victory-core helpers ([#2999](https://github.com/FormidableLabs/victory/pull/2999)) ## 37.3.2 ## 37.3.1 ### Patch Changes - Remove duplicate types from interfaces ([#2940](https://github.com/FormidableLabs/victory/pull/2940)) ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ### Patch Changes - Fix Date equality in axis ([#2642](https://github.com/FormidableLabs/victory/pull/2642)) ## 36.6.13 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - - Removed Template Literal Types to increase TS compatibility (fixes [#2418](https://github.com/FormidableLabs/victory/issues/2418)) ([#2420](https://github.com/FormidableLabs/victory/pull/2420)) - Improved type for `VictoryLabelProps["textAnchor"]` (fixes [#2361](https://github.com/FormidableLabs/victory/issues/2361)) - Fixed exported types for `VictoryAxis`, `VictoryBoxPlot`, `VictoryErrorBar`, and `VictoryScatter` (fixes [#2411](https://github.com/FormidableLabs/victory/issues/2411)) - Migrate `victory-cursor-container` to TS (fixes [#2402](https://github.com/FormidableLabs/victory/issues/2402)) - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-axis/README.md ================================================ # VictoryAxis `victory-axis@^30.0.0` exports `VictoryAxis` component To view documentation for `VictoryAxis` please see https://commerce.nearform.com/open-source/victory/docs/victory-axis To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-axis.md ================================================ FILE: packages/victory-axis/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-axis/package.json ================================================ { "name": "victory-axis", "version": "37.3.6", "description": "Axis Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-axis/src/helper-methods.tsx ================================================ import defaults from "lodash/defaults"; import { Helpers, Scale, Axis } from "victory-core"; import { VictoryAxisProps } from "./victory-axis"; const orientationSign = { top: -1, left: -1, right: 1, bottom: 1, }; const exists = (val) => val !== null && val !== undefined; const getCurrentAxis = (props, axis) => { const { orientation, horizontal } = props; if (orientation) { const dimensions = { top: "x", bottom: "x", left: "y", right: "y" }; return dimensions[orientation]; } const otherAxis = axis === "x" ? "y" : "x"; return horizontal ? otherAxis : axis; }; const getScale = (props) => { const axis = Axis.getAxis(props); const currentAxis = getCurrentAxis(props, axis); const scale = Scale.getBaseScale(props, axis); const propsDomain = props.domain && props.domain[axis]; const domain = propsDomain || Axis.getDomain(props) || scale.domain(); scale.range(Helpers.getRange(props, currentAxis)); scale.domain(domain); return scale; }; const getStyleObject = (props) => { const { theme, dependentAxis } = props; const generalAxisStyle = theme && theme.axis && theme.axis.style; const axisType = dependentAxis ? "dependentAxis" : "independentAxis"; const specificAxisStyle = theme && theme[axisType] && theme[axisType].style; const mergeStyles = () => { const styleNamespaces = [ "axis", "axisLabel", "grid", "parent", "tickLabels", "ticks", ]; return styleNamespaces.reduce((memo, curr) => { memo[curr] = defaults( {}, specificAxisStyle[curr], generalAxisStyle[curr], ); return memo; }, {}); }; return generalAxisStyle && specificAxisStyle ? mergeStyles() : specificAxisStyle || generalAxisStyle; }; export const getStyles = ( props, styleObject: VictoryAxisProps["style"] = {}, ) => { const style = props.style || {}; const parentStyleProps = { height: "100%", width: "100%" }; return { parent: defaults(style.parent, styleObject.parent, parentStyleProps), axis: defaults({}, style.axis, styleObject.axis), axisLabel: defaults({}, style.axisLabel, styleObject.axisLabel), grid: defaults({}, style.grid, styleObject.grid), ticks: defaults({}, style.ticks, styleObject.ticks), tickLabels: defaults({}, style.tickLabels, styleObject.tickLabels), }; }; const getTickProps = (layout, style, datum) => { const { position, transform } = layout; return { x1: transform.x, y1: transform.y, x2: transform.x + position.x2, y2: transform.y + position.y2, style, datum, }; }; // eslint-disable-next-line max-params const getTickLabelProps = (layout, style, anchors, datum, text) => { const { position, transform } = layout; return { style, x: transform.x + position.x, y: transform.y + position.y, verticalAnchor: anchors.verticalAnchor, textAnchor: anchors.textAnchor, angle: style.angle, text, datum, }; }; const getGridProps = (layout, style, datum) => { const { edge, transform } = layout; return { x1: transform.x, y1: transform.y, x2: edge.x + transform.x, y2: edge.y + transform.y, style, datum, }; }; const getAxisProps = (modifiedProps, calculatedValues, globalTransform) => { const { style, padding, isVertical } = calculatedValues; const { width, height } = modifiedProps; return { style: style.axis, x1: isVertical ? globalTransform.x : padding.left + globalTransform.x, x2: isVertical ? globalTransform.x : width - padding.right + globalTransform.x, y1: isVertical ? padding.top + globalTransform.y : globalTransform.y, y2: isVertical ? height - padding.bottom + globalTransform.y : globalTransform.y, }; }; const getEvaluatedStyles = (style, props) => { return { tickStyle: Helpers.evaluateStyle(style.ticks, props), labelStyle: Helpers.evaluateStyle(style.tickLabels, props), gridStyle: Helpers.evaluateStyle(style.grid, props), }; }; const getAxisLabelProps = (props, calculatedValues, globalTransform) => { const { style, orientation, padding, labelPadding, isVertical } = calculatedValues; const sign = orientationSign[orientation]; const hPadding = padding.left + padding.right; const vPadding = padding.top + padding.bottom; const verticalAnchor = sign < 0 ? "end" : "start"; const labelStyle = style.axisLabel; const angle = isVertical ? -90 : 0; // eslint-disable-line no-magic-numbers const x = isVertical ? globalTransform.x + sign * labelPadding : (props.width - hPadding) / 2 + padding.left + globalTransform.x; const y = isVertical ? (props.height - vPadding) / 2 + padding.top + globalTransform.y : sign * labelPadding + globalTransform.y; return { x, y, verticalAnchor: labelStyle.verticalAnchor || verticalAnchor, textAnchor: labelStyle.textAnchor || "middle", angle: labelStyle.angle === undefined ? angle : labelStyle.angle, style: labelStyle, text: props.label, }; }; const getAnchors = (orientation, isVertical) => { const anchorOrientation = { top: "end", left: "end", right: "start", bottom: "start", }; const anchor = anchorOrientation[orientation]; return { textAnchor: isVertical ? anchor : "middle", verticalAnchor: isVertical ? "middle" : anchor, }; }; const getLabelPadding = (props, style) => { const labelStyle = style.axisLabel || {}; if (labelStyle.padding !== undefined && labelStyle.padding !== null) { return labelStyle.padding; } const isVertical = Axis.isVertical(props); // TODO: magic numbers /* eslint-disable no-magic-numbers*/ const fontSize = labelStyle.fontSize || 14; return props.label ? fontSize * (isVertical ? 2.3 : 1.6) : 0; /* eslint-enable no-magic-numbers*/ }; const getDefaultOrientations = (axis, originSign, horizontal) => { const sign = originSign || "positive"; const orientations = { positive: { x: "bottom", y: "left" }, negative: { x: "top", y: "right" }, }; const horizontalOrientations = { positive: { x: "left", y: "bottom" }, negative: { x: "right", y: "top" }, }; return horizontal ? horizontalOrientations[sign][axis] : orientations[sign][axis]; }; const getStandaloneOffset = (props, calculatedValues) => { const { style, scale, orientation, padding, axis, ticks, stringTicks, isVertical, labelPadding, } = calculatedValues; const { polar, horizontal } = props; const sharedProps = { scale: { [axis]: scale }, polar, horizontal, ticks, stringTicks, }; const xPadding = orientation === "right" ? padding.right : padding.left; const yPadding = orientation === "top" ? padding.top : padding.bottom; const offsetX = props.offsetX !== null && props.offsetX !== undefined ? props.offsetX : xPadding; const offsetY = props.offsetY !== null && props.offsetY !== undefined ? props.offsetY : yPadding; const fontSize = style.axisLabel.fontSize || 14; // eslint-disable-line no-magic-numbers const tickSizes = ticks.map((data, index) => { const tick = stringTicks ? props.tickValues[data - 1] : data; const tickStyle = Helpers.evaluateStyle( style.ticks, Object.assign({}, sharedProps, { tick, index }), ); return tickStyle.size || 0; }); const totalPadding = fontSize + 2 * Math.max(...tickSizes) + labelPadding; const minimumPadding = 1.2 * fontSize; // eslint-disable-line no-magic-numbers const x = isVertical ? totalPadding : minimumPadding; const y = isVertical ? minimumPadding : totalPadding; return { x: offsetX !== null && offsetX !== undefined ? offsetX : x, y: offsetY !== null && offsetY !== undefined ? offsetY : y, }; }; const isEqual = (a, b) => { if (a instanceof Date && b instanceof Date) { return a.getTime() === b.getTime(); } return a === b; }; const getOffset = (props, calculatedValues) => { const { scale, origin, orientation, orientations, domain, padding } = calculatedValues; const { top, bottom, left, right } = padding; const calculatedOrientation = { x: orientation === "bottom" || orientation === "top" ? orientation : orientations.x, y: orientation === "left" || orientation === "right" ? orientation : orientations.y, }; // make the axes line up, and cross when appropriate const orientationOffset = { x: calculatedOrientation.y === "left" ? left : right, y: calculatedOrientation.x === "bottom" ? bottom : top, }; const originOffset = { x: calculatedOrientation.y === "left" ? 0 : props.width, y: calculatedOrientation.x === "bottom" ? props.height : 0, }; const originPosition = { x: isEqual(origin.x, domain.x[0]) || isEqual(origin.x, domain.x[1]) ? 0 : scale.x(origin.x), y: isEqual(origin.y, domain.y[0]) || isEqual(origin.y, domain.y[1]) ? 0 : scale.y(origin.y), }; const x = originPosition.x ? Math.abs(originOffset.x - originPosition.x) : orientationOffset.x; const y = originPosition.y ? Math.abs(originOffset.y - originPosition.y) : orientationOffset.y; const offsetX = exists(props.offsetX) ? props.offsetX : x; const offsetY = exists(props.offsetY) ? props.offsetY : y; return { x: offsetX, y: offsetY, }; }; const getHorizontalOffset = (props, calculatedValues) => { const { scale, origin, orientation, orientations, domain, padding } = calculatedValues; const { top, bottom, left, right } = padding; const calculatedOrientation = { y: orientation === "bottom" || orientation === "top" ? orientation : orientations.x, x: orientation === "left" || orientation === "right" ? orientation : orientations.y, }; // make the axes line up, and cross when appropriate const orientationOffset = { x: calculatedOrientation.y === "bottom" ? bottom : top, y: calculatedOrientation.x === "left" ? left : right, }; const originOffset = { y: calculatedOrientation.x === "left" ? 0 : props.width, x: calculatedOrientation.y === "bottom" ? props.height : 0, }; const originPosition = { x: isEqual(origin.x, domain.x[0]) || isEqual(origin.x, domain.x[1]) ? 0 : scale.x(origin.x), y: isEqual(origin.y, domain.y[0]) || isEqual(origin.y, domain.y[1]) ? 0 : scale.y(origin.y), }; const y = originPosition.x ? Math.abs(originOffset.x - originPosition.x) : orientationOffset.x; const x = originPosition.y ? Math.abs(originOffset.y - originPosition.y) : orientationOffset.y; const offsetX = exists(props.offsetX) ? props.offsetX : x; const offsetY = exists(props.offsetY) ? props.offsetY : y; return { x: offsetX, y: offsetY, }; }; const getTransform = (props, calculatedValues, offset) => { const { orientation, axis } = calculatedValues; const axisValue = Axis.getAxisValue(props, axis); return { top: { x: 0, y: axisValue !== undefined ? axisValue : offset.y, }, bottom: { x: 0, y: axisValue !== undefined ? axisValue : props.height - offset.y, }, left: { x: axisValue !== undefined ? axisValue : offset.x, y: 0, }, right: { x: axisValue !== undefined ? axisValue : props.width - offset.x, y: 0, }, }[orientation]; }; const getTickPosition = (style, orientation, isVertical) => { const { tickStyle, labelStyle } = style; const size = tickStyle.size || 0; const tickPadding = tickStyle.padding || 0; const labelPadding = labelStyle.padding || 0; const tickSpacing = size + tickPadding + labelPadding; const sign = orientationSign[orientation]; return { x: isVertical ? sign * tickSpacing : 0, x2: isVertical ? sign * size : 0, y: isVertical ? 0 : sign * tickSpacing, y2: isVertical ? 0 : sign * size, }; }; const getTickTransform = (tick, globalTransform, isVertical) => { return { x: isVertical ? globalTransform.x : tick + globalTransform.x, y: isVertical ? tick + globalTransform.y : globalTransform.y, }; }; const getGridEdge = (props, calculatedValues) => { const { orientation, padding, isVertical } = calculatedValues; const sign = -orientationSign[orientation]; const x = isVertical ? sign * (props.width - (padding.left + padding.right)) : 0; const y = isVertical ? 0 : sign * (props.height - (padding.top + padding.bottom)); return { x, y }; }; const getGridOffset = (calculatedValues, offset) => { const { padding, orientation, crossAxis } = calculatedValues; const xPadding = orientation === "right" ? padding.right : padding.left; const yPadding = orientation === "top" ? padding.top : padding.bottom; return { x: crossAxis ? offset.x - xPadding : 0, y: crossAxis ? offset.y - yPadding : 0, }; }; const getLayoutProps = (modifiedProps, calculatedValues) => { let offset; if (calculatedValues.domain.x && calculatedValues.domain.y) { offset = modifiedProps.horizontal ? getHorizontalOffset(modifiedProps, calculatedValues) : getOffset(modifiedProps, calculatedValues); } else { offset = getStandaloneOffset(modifiedProps, calculatedValues); } return { globalTransform: getTransform(modifiedProps, calculatedValues, offset), gridOffset: getGridOffset(calculatedValues, offset), gridEdge: getGridEdge(modifiedProps, calculatedValues), }; }; const getOrientation = (props) => { if (props.orientation) { return props.orientation; } const defaultOrientations = { dependent: props.horizontal ? "bottom" : "left", independent: props.horizontal ? "left" : "bottom", }; return props.dependentAxis ? defaultOrientations.dependent : defaultOrientations.independent; }; const getCalculatedValues = (props) => { const defaultStyles = getStyleObject(props); const style = getStyles(props, defaultStyles); const padding = Helpers.getPadding(props.padding); const labelPadding = getLabelPadding(props, style); const stringTicks = Axis.stringTicks(props) ? props.tickValues : undefined; const axis = Axis.getAxis(props); const axisDomain = Axis.getDomain(props); const axisScale = getScale(props); const xAxisDomain = axis === "x" ? axisDomain : undefined; const yAxisDomain = axis === "y" ? axisDomain : undefined; const xAxisScale = axis === "x" ? axisScale : undefined; const yAxisScale = axis === "y" ? axisScale : undefined; const crossAxis = !(props.crossAxis === false || props.standalone === true); const ticks = Axis.getTicks(props, axisScale, crossAxis); const tickFormat = Axis.getTickFormat(props, axisScale); const range = { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; // use full domain if passed in from parent, // otherwise use the just the one axis available const domain = { x: props.domain && props.domain.x ? props.domain.x : xAxisDomain, y: props.domain && props.domain.y ? props.domain.y : yAxisDomain, }; // use full scale if passed in from parent, // otherwise use the just the one axis available const scale = { x: props.domain && props.domain.x ? Scale.getBaseScale(props, "x") .domain(props.domain.x) .range(props.horizontal ? range.y : range.x) : xAxisScale, y: props.domain && props.domain.y ? Scale.getBaseScale(props, "y") .domain(props.domain.y) .range(props.horizontal ? range.x : range.y) : yAxisScale, }; const origin = domain.x && domain.y ? Axis.getOrigin(domain) : undefined; const originSign = origin ? { x: Axis.getOriginSign(origin.x, domain.x), y: Axis.getOriginSign(origin.y, domain.y), } : undefined; const orientations = originSign ? { x: getDefaultOrientations("x", originSign.y, props.horizontal), y: getDefaultOrientations("y", originSign.x, props.horizontal), } : undefined; const orientation = orientations ? props.orientation || orientations[axis] : getOrientation(props); const isVertical = Axis.isVertical(Object.assign({}, props, { orientation })); const anchors = getAnchors(orientation, isVertical); return { anchors, axis, crossAxis, domain, isVertical, labelPadding, orientation, orientations, origin, padding, scale, stringTicks, style, tickFormat, ticks, }; }; export const getBaseProps = (initialProps, fallbackProps) => { const props = Axis.modifyProps(initialProps, fallbackProps); const calculatedValues = getCalculatedValues(props); const { axis, style, orientation, isVertical, scale, ticks, tickFormat, anchors, domain, stringTicks, } = calculatedValues; const otherAxis = axis === "x" ? "y" : "x"; const { width, height, standalone, theme, polar, padding, horizontal } = props; const { globalTransform, gridOffset, gridEdge } = getLayoutProps( props, calculatedValues, ); const sharedProps = { scale: { [axis]: scale[axis] }, polar, horizontal, ticks, stringTicks, }; const axisProps = getAxisProps(props, calculatedValues, globalTransform); const axisLabelProps = getAxisLabelProps( props, calculatedValues, globalTransform, ); const initialChildProps = { parent: Object.assign( { style: style.parent, ticks, standalone, theme, width, height, padding, domain, }, sharedProps, ), }; const gridProps = { dimension: otherAxis, range: { [otherAxis]: Helpers.getRange(props, otherAxis) }, scale: props.scale && props.scale[otherAxis] ? { [otherAxis]: props.scale[otherAxis] } : undefined, }; return (ticks as number[]).reduce((childProps, tickValue, index) => { const tick = stringTicks ? stringTicks[index] : tickValue; const text = tickFormat(tickValue, index, ticks); const styles = getEvaluatedStyles( style, Object.assign({}, sharedProps, { tick, tickValue, index, text }), ); const tickLayout = { position: getTickPosition(styles, orientation, isVertical), transform: getTickTransform( scale[axis]?.(tickValue), globalTransform, isVertical, ), }; const gridLayout = { edge: gridEdge, transform: { x: isVertical ? -gridOffset.x + globalTransform.x : scale[axis]?.(tickValue) + globalTransform.x, y: isVertical ? scale[axis]?.(tickValue) + globalTransform.y : gridOffset.y + globalTransform.y, }, }; childProps[index] = { axis: Object.assign({ dimension: axis }, sharedProps, axisProps), axisLabel: Object.assign({}, sharedProps, axisLabelProps), ticks: Object.assign( {}, sharedProps, getTickProps(tickLayout, styles.tickStyle, tickValue), ), tickLabels: Object.assign( {}, sharedProps, getTickLabelProps( tickLayout, styles.labelStyle, anchors, tickValue, text, ), ), grid: Object.assign( {}, sharedProps, gridProps, getGridProps(gridLayout, styles.gridStyle, tickValue), ), }; return childProps; }, initialChildProps); }; ================================================ FILE: packages/victory-axis/src/index.ts ================================================ export * from "./victory-axis"; ================================================ FILE: packages/victory-axis/src/victory-axis.tsx ================================================ import React from "react"; import isEmpty from "lodash/isEmpty"; import { VictoryLabel, VictoryContainer, VictoryTheme, LineSegment, TextSize, addEvents, Axis, UserProps, EventPropTypeInterface, OrientationTypes, VictoryAxisCommonProps, VictoryCommonProps, VictorySingleLabelableProps, EventsMixinClass, } from "victory-core"; import { getBaseProps, getStyles } from "./helper-methods"; const fallbackProps = { width: 450, height: 300, padding: 50, }; const options = { components: [ { name: "axis", index: 0 }, { name: "axisLabel", index: 0 }, { name: "grid" }, { name: "parent", index: "parent" }, { name: "ticks" }, { name: "tickLabels" }, ], }; export type VictoryAxisTTargetType = | "axis" | "axisLabel" | "grid" | "ticks" | "tickLabels" | "parent"; export interface VictoryAxisProps extends VictoryAxisCommonProps, VictoryCommonProps, VictorySingleLabelableProps { crossAxis?: boolean; events?: EventPropTypeInterface[]; fixLabelOverlap?: boolean; offsetX?: number; offsetY?: number; orientation?: OrientationTypes; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryAxisBase extends EventsMixinClass {} class VictoryAxisBase extends React.Component { static animationWhitelist: Array = [ "style", "domain", "range", "tickCount", "tickValues", "offsetX", "offsetY", "padding", "width", "height", ]; static displayName = "VictoryAxis"; static role = "axis"; static defaultTransitions = { onExit: { duration: 500, }, onEnter: { duration: 500, }, }; static defaultProps = { axisComponent: , axisLabelComponent: , tickLabelComponent: , tickComponent: , gridComponent: , standalone: true, theme: VictoryTheme.grayscale, containerComponent: , groupComponent: , fixLabelOverlap: false, }; static getDomain = Axis.getDomain; static getAxis = Axis.getAxis; static getStyles(props) { return getStyles(props); } static getBaseProps(props) { return getBaseProps(props, fallbackProps); } static expectedComponents: Array = [ "axisComponent", "axisLabelComponent", "groupComponent", "containerComponent", "tickComponent", "tickLabelComponent", "gridComponent", ]; renderLine(props) { const { axisComponent } = props; const axisProps = this.getComponentProps(axisComponent, "axis", 0); return React.cloneElement(axisComponent, axisProps); } renderLabel(props) { const { axisLabelComponent, label } = props; if (!label) { return null; } const axisLabelProps = this.getComponentProps( axisLabelComponent, "axisLabel", 0, ); return React.cloneElement(axisLabelComponent, axisLabelProps); } renderGridAndTicks(props) { const { tickComponent, tickLabelComponent, gridComponent, name } = props; const shouldRender = (componentProps) => { const { style = {}, events = {} } = componentProps; const visible = style.stroke !== "transparent" && style.stroke !== "none" && style.strokeWidth !== 0; return visible || !isEmpty(events); }; return this.dataKeys.map((key, index) => { const tickProps = this.getComponentProps(tickComponent, "ticks", index); const BaseTickComponent = React.cloneElement(tickComponent, tickProps); const TickComponent = shouldRender(BaseTickComponent.props) ? BaseTickComponent : undefined; const gridProps = this.getComponentProps(gridComponent, "grid", index); const BaseGridComponent = React.cloneElement(gridComponent, gridProps); const GridComponent = shouldRender(BaseGridComponent.props) ? BaseGridComponent : undefined; const tickLabelProps = this.getComponentProps( tickLabelComponent, "tickLabels", index, ); const TickLabel = React.cloneElement(tickLabelComponent, tickLabelProps); const children = [GridComponent, TickComponent, TickLabel].filter( Boolean, ); return React.cloneElement( props.groupComponent, { key: `${name}-tick-group-${key}` }, children, ); }); } fixLabelOverlap(gridAndTicks, props) { const isVertical = Axis.isVertical(props); const size = isVertical ? props.height : props.width; const isVictoryLabel = (child) => child.type && child.type.role === "label"; const labels = gridAndTicks .map((gridAndTick) => gridAndTick.props.children) .reduce((accumulator, childArr) => accumulator.concat(childArr), []) .filter(isVictoryLabel) .map((child) => child.props); const paddingToObject = (padding) => typeof padding === "object" ? Object.assign({}, { top: 0, right: 0, bottom: 0, left: 0 }, padding) : { top: padding, right: padding, bottom: padding, left: padding }; const labelsSumSize = labels.reduce((sum, label) => { const padding = paddingToObject(label.style.padding); const labelSize = TextSize.approximateTextSize(label.text, { angle: label.angle, fontSize: label.style.fontSize, letterSpacing: label.style.letterSpacing, fontFamily: label.style.fontFamily, }); return ( sum + (isVertical ? labelSize.height + padding.top + padding.bottom : labelSize.width + padding.right + padding.left) ); }, 0); const availiableLabelCount = Math.floor( (size * gridAndTicks.length) / labelsSumSize, ); const divider = Math.ceil(gridAndTicks.length / availiableLabelCount) || 1; const getLabelCoord = (gridAndTick) => gridAndTick.props.children .filter(isVictoryLabel) .reduce( (prev, child) => (isVertical ? child.props.y : child.props.x) || 0, 0, ); const sorted = gridAndTicks.sort( (a, b) => isVertical ? getLabelCoord(b) - getLabelCoord(a) // ordinary axis has top-bottom orientation : getLabelCoord(a) - getLabelCoord(b), // ordinary axis has left-right orientation ); return sorted.filter((gridAndTick, index) => index % divider === 0); } // Overridden in native versions shouldAnimate() { return !!this.props.animate; } render(): React.ReactElement { const { animationWhitelist } = VictoryAxis; const props = Axis.modifyProps(this.props, fallbackProps); const userProps = UserProps.getSafeUserProps(this.props); if (this.shouldAnimate()) { return this.animateComponent(props, animationWhitelist); } const gridAndTicks = this.renderGridAndTicks(props); const modifiedGridAndTicks = props.fixLabelOverlap ? this.fixLabelOverlap(gridAndTicks, props) : gridAndTicks; const children = [ this.renderLine(props), this.renderLabel(props), ...modifiedGridAndTicks, ]; const container = React.cloneElement(props.containerComponent, userProps); return props.standalone ? this.renderContainer(container, children) : React.cloneElement(props.groupComponent, userProps, children); } } export const VictoryAxis = addEvents(VictoryAxisBase, options); ================================================ FILE: packages/victory-axis/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-axis/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-bar/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-bar/CHANGELOG.md ================================================ # victory-bar ## 37.3.6 ## 37.3.5 ### Patch Changes - Fix "props.groupComponent is undefined" error ([#3014](https://github.com/FormidableLabs/victory/pull/3014)) ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) * Zoomed bar graph items will no longer be culled from the view when more than 50% of width or height is outside of the clipping parent. Instead they will be clipped once 100% outside" ([#2970](https://github.com/FormidableLabs/victory/pull/2970)) ## 37.3.2 ## 37.3.1 ### Patch Changes - Remove duplicate types from interfaces ([#2940](https://github.com/FormidableLabs/victory/pull/2940)) ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash isNil and isNan with native code ([#2800](https://github.com/FormidableLabs/victory/pull/2800)) * Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) * Replace instances of lodash.range with equivalent native code ([#2760](https://github.com/FormidableLabs/victory/pull/2760)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Fix incorrect typescript props ([#2745](https://github.com/FormidableLabs/victory/pull/2745)) * Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ### Patch Changes - Migrate victory-bar to TypeScript ([#2709](https://github.com/FormidableLabs/victory/pull/2709)) ## 36.8.1 ## 36.8.0 ### Minor Changes - Remove v37 experimental code ([#2697](https://github.com/FormidableLabs/victory/pull/2697)) ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) ## 36.7.0 ### Minor Changes - added ref forwarding for path and bar components ([#2673](https://github.com/FormidableLabs/victory/pull/2673)) ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 - victory-vendor@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 - victory-vendor@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 - victory-vendor@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416), [`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 - victory-vendor@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 - victory-vendor@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 - victory-vendor@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 - victory-vendor@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 - victory-vendor@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 - victory-vendor@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-bar/README.md ================================================ # VictoryBar `victory-bar@^30.0.0` exports `VictoryBar` and `Bar` components To view documentation for `VictoryBar` please see https://commerce.nearform.com/open-source/victory/docs/victory-bar To suggest an addition or correction to documentation for `VictoryBar` please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-bar.md ================================================ FILE: packages/victory-bar/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-bar/package.json ================================================ { "name": "victory-bar", "version": "37.3.6", "description": "Bar Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6", "victory-vendor": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "devDependencies": { "victory-chart": "*" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs", "../victory-vendor:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-chart:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-bar/src/bar-helper-methods.ts ================================================ import isPlainObject from "lodash/isPlainObject"; import { Helpers, VictoryStyleObject } from "victory-core"; import { BarProps } from "./bar"; import { VictoryBarCornerRadiusObject, VictoryBarCornerRadiusKey, } from "./victory-bar"; const DEFAULT_BAR_WIDTH = 8; export const getBarWidth = ( barWidth: BarProps["barWidth"], props: BarProps, ) => { const { scale, data, style } = props; if (barWidth) { return Helpers.evaluateProp(barWidth, props); } else if (style.width) { return style.width; } const range = scale.x.range(); const extent = Math.abs(range[1] - range[0]); const bars = data.length + 2; const barRatio = props.barRatio || 0.5; const defaultWidth = barRatio * (data.length < 2 ? DEFAULT_BAR_WIDTH : extent / bars); return Math.max(1, defaultWidth); }; const getCornerRadiusFromObject = ( cornerRadius: VictoryBarCornerRadiusObject, props: BarProps, ) => { const realCornerRadius: VictoryBarCornerRadiusObject = { topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0, }; const updateCornerRadius = ( corner: VictoryBarCornerRadiusKey, fallback: "top" | "bottom", ) => { if (!Helpers.isNil(cornerRadius[corner])) { realCornerRadius[corner] = Helpers.evaluateProp( cornerRadius[corner], props, ); } else if (!Helpers.isNil(cornerRadius[fallback])) { realCornerRadius[corner] = Helpers.evaluateProp( cornerRadius[fallback], props, ); } }; updateCornerRadius("topLeft", "top"); updateCornerRadius("topRight", "top"); updateCornerRadius("bottomLeft", "bottom"); updateCornerRadius("bottomRight", "bottom"); return realCornerRadius; }; function isCornerRadiusObject( cornerRadius: BarProps["cornerRadius"], ): cornerRadius is VictoryBarCornerRadiusObject { return isPlainObject(cornerRadius); } export const getCornerRadius = ( cornerRadius: BarProps["cornerRadius"], props: BarProps, ) => { const realCornerRadius: VictoryBarCornerRadiusObject = { topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0, }; if (!cornerRadius) { return realCornerRadius; } if (isCornerRadiusObject(cornerRadius)) { return getCornerRadiusFromObject(cornerRadius, props); } realCornerRadius.topLeft = Helpers.evaluateProp(cornerRadius, props); realCornerRadius.topRight = Helpers.evaluateProp(cornerRadius, props); return realCornerRadius; }; export const getStyle = (style: VictoryStyleObject = {}, props: BarProps) => { if (props.disableInlineStyles) { return {}; } const stroke = style.fill || "black"; const baseStyle = { fill: "black", stroke }; return Helpers.evaluateStyle(Object.assign(baseStyle, style), props); }; ================================================ FILE: packages/victory-bar/src/bar.test.tsx ================================================ import React from "react"; import { render } from "@testing-library/react"; import * as d3Scale from "victory-vendor/d3-scale"; import { VictoryContainer } from "victory-core"; import { getBarShape } from "../../../test/helpers"; import { Bar } from "./bar"; describe("victory-primitives/bar", () => { const baseProps = { data: [ { _x: 2, x: 2, _y: 4, y: 4, eventKey: 0 }, { _x: 3, x: 3, _y: 5, y: 5, eventKey: 1 }, ], datum: { _x: 2, x: 2, _y: 4, y: 4, eventKey: 0 }, x: 2, x0: 0, y: 10, y0: 0, scale: { x: d3Scale.scaleLinear(), y: d3Scale.scaleLinear(), }, }; const renderBarSvg = (props = {}) => { const combinedProps = { ...baseProps, ...props }; const { container } = render( , ); return container.querySelector("path"); }; it("should render a vertical bar", () => { const bar = renderBarSvg(); const barShape = getBarShape(bar); expect(Math.round(barShape.height)).toEqual(10); }); it("should render a horizontal bar", () => { const props = { horizontal: true }; const bar = renderBarSvg(props); const barShape = getBarShape(bar); expect(Math.round(barShape.width)).toEqual(2); }); it("should render a default bar width when one is not provided", () => { const props = { width: 10, padding: 1, data: Array(4), }; const bar = renderBarSvg(props); const barShape = getBarShape(bar); expect(Math.floor(barShape.width)).toEqual(2); }); it("should allow override of width by passing a style", () => { const props = { style: { width: 3 } }; const bar = renderBarSvg(props); const barShape = getBarShape(bar); expect(Math.floor(barShape.width)).toEqual(3); }); it("should allow modification of width by passing barRatio", () => { const props = { data: [{ _x: 2, x: 2, _y: 4, y: 4, eventKey: 0 }], barRatio: 3, }; const bar = renderBarSvg(props); const barShape = getBarShape(bar); expect(Math.floor(barShape.width)).toEqual(24); }); }); ================================================ FILE: packages/victory-bar/src/bar.tsx ================================================ import React, { forwardRef } from "react"; import defaults from "lodash/defaults"; import { Helpers, NumberOrCallback, Path, VictoryCommonPrimitiveProps, } from "victory-core"; import { getStyle, getBarWidth, getCornerRadius } from "./bar-helper-methods"; import { getPolarBarPath, getBarPath } from "./path-helper-methods"; import { VictoryBarAlignmentType, VictoryBarCornerRadiusObject, } from "./victory-bar"; export interface BarProps extends VictoryCommonPrimitiveProps { alignment?: VictoryBarAlignmentType; barOffset?: number[]; barRatio?: number; barWidth?: NumberOrCallback; cornerRadius?: NumberOrCallback | VictoryBarCornerRadiusObject; datum?: any; getPath?: (props: BarProps) => string; horizontal?: boolean; pathComponent?: React.ReactElement; width?: number; x?: number; y?: number; y0?: number; } const evaluateProps = (props: BarProps) => { /** * Potential evaluated props of following must be evaluated in this order: * 1) `style` * 2) `barWidth` * 3) `cornerRadius` * * Everything else does not have to be evaluated in a particular order: * `ariaLabel` * `desc` * `id` * `tabIndex` */ const style = getStyle(props.style, props); const barWidth = getBarWidth( props.barWidth, Object.assign({}, props, { style }), ); const cornerRadius = getCornerRadius( props.cornerRadius, Object.assign({}, props, { style, barWidth }), ); const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const desc = Helpers.evaluateProp(props.desc, props); const id = Helpers.evaluateProp(props.id, props); const tabIndex = Helpers.evaluateProp(props.tabIndex, props); return Object.assign({}, props, { ariaLabel, style, barWidth, cornerRadius, desc, id, tabIndex, }); }; const defaultProps: Partial = { pathComponent: , role: "presentation", shapeRendering: "auto", }; export const Bar = forwardRef( // eslint-disable-next-line prefer-arrow-callback function Bar(initialProps, ref) { const props = evaluateProps(defaults({}, initialProps, defaultProps)); const { polar, origin, style, barWidth, cornerRadius } = props; const path = polar ? getPolarBarPath(props, cornerRadius) : getBarPath(props, barWidth, cornerRadius); const defaultTransform = polar && origin ? `translate(${origin.x}, ${origin.y})` : undefined; if (!props.pathComponent) { return null; } return React.cloneElement(props.pathComponent, { ...props.events, "aria-label": props.ariaLabel, style, d: path, className: props.className, clipPath: props.clipPath, desc: props.desc, index: props.index, role: props.role, shapeRendering: props.shapeRendering, transform: props.transform || defaultTransform, tabIndex: props.tabIndex, ref, }); }, ); ================================================ FILE: packages/victory-bar/src/geometry-helper-methods.test.ts ================================================ import { circle, point } from "./geometry-helper-methods"; describe("point", () => { describe("calculates distances", () => { const expectDistanceToBeClose = (p0, p1, answerRoot = 2) => { const answer = Math.sqrt(answerRoot); expect(p0.distance(p1)).toBeCloseTo(answer); }; it("between positive coordinates", () => { const p0 = point(1, 2); const p1 = point(2, 3); expectDistanceToBeClose(p0, p1); }); it("between negative coordinates", () => { const p0 = point(-1, -2); const p1 = point(-2, -3); expectDistanceToBeClose(p0, p1); }); it("between zero, zero coordinates", () => { const p0 = point(0, 0); const p1 = point(0, 0); expectDistanceToBeClose(p0, p1, 0); }); }); it("can add correctly", () => { const p0 = point(1, 1); const p1 = point(2, 3); const { x, y } = p0.add(p1); expect(x).toEqual(3); expect(y).toEqual(4); }); it("can subtract correctly", () => { const p0 = point(1, 1); const p1 = point(2, 3); const { x, y } = p1.subtract(p0); expect(x).toEqual(1); expect(y).toEqual(2); }); it("can scalar multiply correctly", () => { const p = point(3, 4); const { x, y } = p.scalarMult(-2); expect(x).toEqual(-6); expect(y).toEqual(-8); }); it("can scalar divide correctly", () => { const p = point(3, 4); const { x, y } = p.scalarDivide(2); const answerX = 1.5; const answerY = 2; expect(x).toBeCloseTo(answerX); expect(y).toBeCloseTo(answerY); }); it("throws when dividing by zero", () => { const p = point(3, 4); expect(() => p.scalarDivide(0)).toThrow(); }); it("can check for equality correctly", () => { const p0 = point(3, 4); const p1 = point(3, 4); expect(p0.equals(p1)).toBeTruthy(); }); }); describe("circle", () => { describe("calculates circle-circle intersection correctly", () => { it("handles separate circles", () => { const c0 = circle(point(0, 0), 1); const c1 = circle(point(0, 10), 1); expect(c0.intersection(c1)).toEqual([]); }); it("handles one circle containing another", () => { const c0 = circle(point(0, 0), 2); const c1 = circle(point(0, 0), 10); expect(c0.intersection(c1)).toEqual([]); }); it("handles same circle case", () => { const c0 = circle(point(0, 0), 1); const c1 = circle(point(0, 0), 1); expect(c0.intersection(c1)).toEqual([]); }); it("handles circles of zero radius", () => { const c0 = circle(point(0, 0), 0); const c1 = circle(point(0, 0), 0); expect(c0.intersection(c1)).toEqual([]); const c2 = circle(point(0, 0), 0); const c3 = circle(point(0, 1), 0); expect(c2.intersection(c3)).toEqual([]); }); it("handles circles meeting at exactly one point", () => { const c0 = circle(point(0, 0), 1); const c1 = circle(point(0, 2), 1); const [{ x: x0, y: y0 }, { x: x1, y: y1 }] = c0.intersection(c1); expect(x0).toEqual(0); expect(x1).toEqual(0); expect(y0).toEqual(1); expect(y1).toEqual(1); }); it("handles circles meeting at two points", () => { const c0 = circle(point(2, 3), 3); const c1 = circle(point(1, -1), 4); const [{ x: x0, y: y0 }, { x: x1, y: y1 }] = c0.intersection(c1); expect(x0).toBeCloseTo(-0.96); expect(y0).toBeCloseTo(2.49); expect(x1).toBeCloseTo(4.37); expect(y1).toBeCloseTo(1.16); }); it("the left-most point is the 0th element, the right-most is the 1st", () => { const c0 = circle(point(2, 3), 3); const c1 = circle(point(1, -1), 4); const [{ x: x0 }, { x: x1 }] = c0.intersection(c1); expect(x0 <= x1).toBeTruthy(); }); }); }); ================================================ FILE: packages/victory-bar/src/geometry-helper-methods.ts ================================================ /** * A point in the 2d plane */ export const point = (x: number, y: number) => ({ x, y, distance(p1: { x: number; y: number }) { return Math.sqrt(Math.pow(this.x - p1.x, 2) + Math.pow(this.y - p1.y, 2)); }, // vector addition in 2d plane add(p1: { x: number; y: number }) { return point(this.x + p1.x, this.y + p1.y); }, // vector subtraction in 2d // returns p0 - p1 subtract(p1: { x: number; y: number }) { return point(this.x - p1.x, this.y - p1.y); }, // multiply a 2d point by a scalar scalarMult(n: number) { return point(this.x * n, this.y * n); }, scalarDivide(n: number) { if (n === 0) { throw new Error("Division by 0 error"); } return point(this.x / n, this.y / n); }, equals(p1: { x: number; y: number }) { return this.x === p1.x && this.y === p1.y; }, }); type Center = ReturnType; /** * A circle in the 2d plane */ export const circle = (center: Center, radius: number) => ({ center, radius, hasIntersection(circle1: { center: Center; radius: number }) { const P0 = this.center; const P1 = circle1.center; const r0 = this.radius; const r1 = circle1.radius; const d = P0.distance(P1); if (d > r0 + r1) { return false; // separate circles } if (d < Math.abs(r0 - r1)) { return false; // one circle contains another } return true; }, equals(circle1: { center: Center; radius: number }) { const P0 = this.center; const P1 = circle1.center; const r0 = this.radius; const r1 = circle1.radius; return r0 === r1 && P0.equals(P1); }, // Source: http://paulbourke.net/geometry/circlesphere/ // "Intersection of two circles" by Paul Bourke // Left-most point is returned as 0th element of array // Right-most point is returned as 1st elemennt of array intersection(circle1: { center: Center; radius: number }) { const P0 = this.center; const P1 = circle1.center; const r0 = this.radius; const r1 = circle1.radius; const d = P0.distance(P1); if (!this.hasIntersection(circle1) || this.equals(circle1)) { return []; } const a = (Math.pow(r0, 2) - Math.pow(r1, 2) + Math.pow(d, 2)) / (2 * d); const h = Math.sqrt(Math.pow(r0, 2) - Math.pow(a, 2)); const P2 = P0.add(P1.subtract(P0).scalarMult(a).scalarDivide(d)); const { x: x0, y: y0 } = P0; const { x: x1, y: y1 } = P1; const { x: x2, y: y2 } = P2; const P3s = [ point(x2 - (h * (y1 - y0)) / d, y2 + (h * (x1 - x0)) / d), point(x2 + (h * (y1 - y0)) / d, y2 - (h * (x1 - x0)) / d), ]; P3s.sort((Point1, Point2) => Point1.x - Point2.x); return P3s; }, solveX(y: number) { const sqrt = Math.sqrt( Math.pow(this.radius, 2) - Math.pow(y - this.center.y, 2), ); return [this.center.x - sqrt, this.center.x + sqrt]; }, solveY(x: number) { const sqrt = Math.sqrt( Math.pow(this.radius, 2) - Math.pow(x - this.center.x, 2), ); return [this.center.y - sqrt, this.center.y + sqrt]; }, }); ================================================ FILE: packages/victory-bar/src/helper-methods.ts ================================================ import { Collection, Data, Domain, Helpers, LabelHelpers, Scale, VictoryClipContainer, } from "victory-core"; export const getBarPosition = (props, datum) => { const getDefaultMin = (axis) => { const defaultZero = Scale.getType(props.scale[axis]) === "log" ? 1 / Number.MAX_SAFE_INTEGER : 0; let defaultMin = defaultZero; const minY = Collection.getMinValue(props.domain[axis]) as number; const maxY = Collection.getMaxValue(props.domain[axis]) as number; if (minY < 0 && maxY <= 0) { defaultMin = maxY; } else if (minY >= 0 && maxY > 0) { defaultMin = minY; } return datum[`_${axis}`] instanceof Date ? new Date(defaultMin) : defaultMin; }; const _y0 = datum._y0 !== undefined ? datum._y0 : getDefaultMin("y"); const _x0 = datum._x0 !== undefined ? datum._x0 : getDefaultMin("x"); return Helpers.scalePoint(props, Object.assign({}, datum, { _y0, _x0 })); }; const getCalculatedValues = (props) => { const { polar } = props; const defaultStyles = Helpers.getDefaultStyles(props, "bar"); const style = !props.disableInlineStyles ? Helpers.getStyles(props.style, defaultStyles) : {}; const range = props.range || { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; const domain = { x: Domain.getDomainWithZero(props, "x"), y: Domain.getDomainWithZero(props, "y"), }; const scale = { x: Scale.getBaseScale(props, "x") .domain(domain.x) .range(props.horizontal ? range.y : range.x), y: Scale.getBaseScale(props, "y") .domain(domain.y) .range(props.horizontal ? range.x : range.y), }; const origin = polar ? props.origin || Helpers.getPolarOrigin(props) : undefined; let data = Data.getData(props); data = Data.formatDataFromDomain(data, domain, 0); // when inside a zoom container, reset the _x and _y properties of each datum to the original // x and y property values so they will not be clipped. See https://github.com/FormidableLabs/victory/pull/2970 if (props.groupComponent?.type === VictoryClipContainer) { data = data.map((datum) => ({ ...datum, _x: datum.x, _y: datum.y })); } return { style, data, scale, domain, origin }; }; export const getBaseProps = (initialProps, fallbackProps) => { const modifiedProps = Helpers.modifyProps(initialProps, fallbackProps, "bar"); const props = Object.assign( {}, modifiedProps, getCalculatedValues(modifiedProps), ); const { alignment, barRatio, cornerRadius, data, disableInlineStyles, domain, events, height, horizontal, origin, padding, polar, scale, sharedEvents, standalone, style, theme, width, labels, name, barWidth, getPath, } = props; const initialChildProps = { parent: { horizontal, domain, scale, width, height, data, standalone, name, theme, polar, origin, padding, style: style.parent, }, }; return data.reduce((childProps, datum, index) => { const eventKey = !Helpers.isNil(datum.eventKey) ? datum.eventKey : index; const { x, y, y0, x0 } = getBarPosition(props, datum); const dataProps = { alignment, barRatio, barWidth, cornerRadius, data, datum, disableInlineStyles, getPath, horizontal, index, polar, origin, scale, style: style.data, width, height, x, y, y0, x0, }; childProps[eventKey] = { data: dataProps, }; const text = LabelHelpers.getText(props, datum, index); if ( (text !== undefined && text !== null) || (labels && (events || sharedEvents)) ) { childProps[eventKey].labels = LabelHelpers.getProps(props, index); } return childProps; }, initialChildProps); }; ================================================ FILE: packages/victory-bar/src/index.ts ================================================ export * from "./victory-bar"; export * from "./bar"; export { getBarPosition } from "./helper-methods"; export { getVerticalBarPath, getHorizontalBarPath, getVerticalPolarBarPath, getCustomBarPath, getPolarBarPath, getBarPath, } from "./path-helper-methods"; export { getBarWidth, getStyle, getCornerRadius } from "./bar-helper-methods"; ================================================ FILE: packages/victory-bar/src/path-helper-methods.ts ================================================ import * as d3Shape from "victory-vendor/d3-shape"; import { BarProps } from "./bar"; import { circle, point } from "./geometry-helper-methods"; const getPosition = (props, width) => { const { x, x0, y, y0, horizontal } = props; const alignment = props.alignment || "middle"; const size = alignment === "middle" ? width / 2 : width; const sign = horizontal ? -1 : 1; if (horizontal) { return { x0, x1: x, y0: alignment === "start" ? y : y - sign * size, y1: alignment === "end" ? y : y + sign * size, }; } return { x0: alignment === "start" ? x : x - sign * size, x1: alignment === "end" ? x : x + sign * size, y0, y1: y, }; }; const getAngle = (props, index: number) => { const { data, scale } = props; const x = data[index]._x1 === undefined ? "_x" : "_x1"; return scale.x(data[index][x]); }; const getAngularWidth = (props, width) => { const { scale } = props; const range = scale.y.range(); const r = Math.max(...range); const angularRange = Math.abs(scale.x.range()[1] - scale.x.range()[0]); return (width / (2 * Math.PI * r)) * angularRange; }; const transformAngle = (angle: number) => { return -1 * angle + Math.PI / 2; }; export const getCustomBarPath = ( props: BarProps, width: number, ): string | undefined => { const { getPath } = props; if (typeof getPath === "function") { const propsWithCalculatedValues = { ...props, ...getPosition(props, width), }; return getPath(propsWithCalculatedValues); } }; const getStartAngle = (props, index: number) => { const { data, scale, alignment } = props; const currentAngle = getAngle(props, index); const angularRange = Math.abs(scale.x.range()[1] - scale.x.range()[0]); const previousAngle = index === 0 ? getAngle(props, data.length - 1) - Math.PI * 2 : getAngle(props, index - 1); if (index === 0 && angularRange < 2 * Math.PI) { return scale.x.range()[0]; } else if (alignment === "start" || alignment === "end") { return alignment === "start" ? previousAngle : currentAngle; } return (currentAngle + previousAngle) / 2; }; const getEndAngle = (props, index: number) => { const { data, scale, alignment } = props; const currentAngle = getAngle(props, index); const angularRange = Math.abs(scale.x.range()[1] - scale.x.range()[0]); const lastAngle = scale.x.range()[1] === 2 * Math.PI ? getAngle(props, 0) + Math.PI * 2 : scale.x.range()[1]; const nextAngle = index === data.length - 1 ? getAngle(props, 0) + Math.PI * 2 : getAngle(props, index + 1); if (index === data.length - 1 && angularRange < 2 * Math.PI) { return lastAngle; } else if (alignment === "start" || alignment === "end") { return alignment === "start" ? currentAngle : nextAngle; } return (currentAngle + nextAngle) / 2; }; const mapPointsToPath = (coords, cornerRadius, direction) => { const topLeftPath = `${cornerRadius.topLeft} ${cornerRadius.topLeft} ${direction}`; const topRightPath = `${cornerRadius.topRight} ${cornerRadius.topRight} ${direction}`; const bottomLeftPath = `${cornerRadius.bottomLeft} ${cornerRadius.bottomLeft} ${direction}`; const bottomRightPath = `${cornerRadius.bottomRight} ${cornerRadius.bottomRight} ${direction}`; const commands = [ "M", `A ${bottomLeftPath},`, "L", `A ${topLeftPath},`, "L", `A ${topRightPath},`, "L", `A ${bottomRightPath},`, ]; const path = commands.reduce( (acc, command, i) => `${acc}${command} ${coords[i].x}, ${coords[i].y} \n`, "", ); return `${path} z`; }; const getVerticalBarPoints = (position, sign, cr) => { const { x0, x1, y0, y1 } = position; const getHalfPoints = (side) => { const isLeft = side === "Left"; const signL = isLeft ? 1 : -1; const x = isLeft ? x0 : x1; let bottomPoint = { x: x + signL * cr[`bottom${side}`], y: y0 }; let bottomMiddlePoint = { x, y: y0 - sign * cr[`bottom${side}`] }; let topMiddlePoint = { x, y: y1 + sign * cr[`top${side}`] }; let topPoint = { x: x + signL * cr[`top${side}`], y: y1 }; const hasIntersection = sign === 1 ? y0 - cr[`bottom${side}`] < y1 + cr[`top${side}`] : y0 + cr[`bottom${side}`] > y1 - cr[`top${side}`]; if (hasIntersection) { const topCenter = point( x + signL * cr[`top${side}`], y1 + sign * cr[`top${side}`], ); const topCircle = circle(topCenter, cr[`top${side}`]); const bottomCenter = point( x + signL * cr[`bottom${side}`], y0 - sign * cr[`bottom${side}`], ); const bottomCircle = circle(bottomCenter, cr[`bottom${side}`]); const circleIntersection = topCircle.intersection(bottomCircle); const hasArcIntersection = circleIntersection.length > 0; if (hasArcIntersection) { const arcIntersection = circleIntersection[isLeft ? 0 : 1]; bottomMiddlePoint = { x: arcIntersection.x, y: arcIntersection.y }; topMiddlePoint = { x: arcIntersection.x, y: arcIntersection.y }; } else { const hasBottomLineTopArcIntersection = cr[`top${side}`] > cr[`bottom${side}`]; if (hasBottomLineTopArcIntersection) { const newX = topCircle.solveX(y0)[isLeft ? 0 : 1]; bottomPoint = { x: newX, y: y0 }; bottomMiddlePoint = { x: newX, y: y0 }; topMiddlePoint = { x: newX, y: y0 }; } else { const newX = bottomCircle.solveX(y1)[isLeft ? 0 : 1]; bottomMiddlePoint = { x: newX, y: y1 }; topMiddlePoint = { x: newX, y: y1 }; topPoint = { x: newX, y: y1 }; } } } const points = [bottomPoint, bottomMiddlePoint, topMiddlePoint, topPoint]; return isLeft ? points : points.reverse(); }; return getHalfPoints("Left").concat(getHalfPoints("Right")); }; const getHorizontalBarPoints = (position, sign, cr) => { const { y0, y1 } = position; const x0 = position.x0 < position.x1 ? position.x0 : position.x1; const x1 = position.x0 < position.x1 ? position.x1 : position.x0; const getHalfPoints = (side) => { const isTop = side === "top"; const signL = isTop ? -1 : 1; const y = isTop ? y1 : y0; let leftPoint = { x: x0, y: y - signL * cr[`${side}Left`] }; let leftMiddlePoint = { x: x0 + cr[`${side}Left`], y }; let rightMiddlePoint = { x: x1 - cr[`${side}Right`], y }; let rightPoint = { x: x1, y: y - signL * cr[`${side}Right`] }; const hasIntersection = leftMiddlePoint.x > rightMiddlePoint.x; if (hasIntersection) { const leftCenter = point( x0 + cr[`${side}Left`], y - signL * cr[`${side}Left`], ); const leftCircle = circle(leftCenter, cr[`${side}Left`]); const rightCenter = point( x1 - cr[`${side}Right`], y - signL * cr[`${side}Right`], ); const rightCircle = circle(rightCenter, cr[`${side}Right`]); const circleIntersection = leftCircle.intersection(rightCircle); const hasArcIntersection = circleIntersection.length > 0; if (hasArcIntersection) { const arcIntersection = circleIntersection[sign > 0 ? 1 : 0]; leftMiddlePoint = { x: arcIntersection.x, y: arcIntersection.y }; rightMiddlePoint = { x: arcIntersection.x, y: arcIntersection.y }; } else { const hasLeftLineRightArcIntersection = cr[`${side}Right`] > cr[`${side}Left`]; if (hasLeftLineRightArcIntersection) { const newY = rightCircle.solveY(x0)[isTop ? 0 : 1]; leftPoint = { x: x0, y: newY }; leftMiddlePoint = { x: x0, y: newY }; rightMiddlePoint = { x: x0, y: newY }; } else { const newY = leftCircle.solveY(x1)[isTop ? 0 : 1]; rightPoint = { x: x1, y: newY }; rightMiddlePoint = { x: x1, y: newY }; leftMiddlePoint = { x: x1, y: newY }; } } } return [leftPoint, leftMiddlePoint, rightMiddlePoint, rightPoint]; }; const topPoints = getHalfPoints("top"); const bottomPoints = getHalfPoints("bottom"); return [ bottomPoints[1], bottomPoints[0], ...topPoints, // eslint-disable-next-line no-magic-numbers bottomPoints[3], bottomPoints[2], ]; }; export const getVerticalBarPath = (props, width, cornerRadius) => { const position = getPosition(props, width); const sign = position.y0 > position.y1 ? 1 : -1; const direction = sign > 0 ? "0 0 1" : "0 0 0"; const points = getVerticalBarPoints(position, sign, cornerRadius); return mapPointsToPath(points, cornerRadius, direction); }; export const getHorizontalBarPath = (props, width, cornerRadius) => { const position = getPosition(props, width); const sign = position.x0 < position.x1 ? 1 : -1; const direction = "0 0 1"; const cr = { topRight: sign > 0 ? cornerRadius.topLeft : cornerRadius.bottomLeft, bottomRight: sign > 0 ? cornerRadius.topRight : cornerRadius.bottomRight, bottomLeft: sign > 0 ? cornerRadius.bottomRight : cornerRadius.topRight, topLeft: sign > 0 ? cornerRadius.bottomLeft : cornerRadius.topLeft, }; const points = getHorizontalBarPoints(position, sign, cr); return mapPointsToPath(points, cr, direction); }; export const getVerticalPolarBarPath = (props: BarProps, cornerRadius) => { const { datum, scale, index, alignment, style } = props; const r1 = scale.y(datum._y0 || 0); const r2 = scale.y(datum._y1 !== undefined ? datum._y1 : datum._y); const currentAngle = scale.x(datum._x1 !== undefined ? datum._x1 : datum._x); let start; let end; if (style.width) { const width = getAngularWidth(props, style.width); const size = alignment === "middle" ? width / 2 : width; start = alignment === "start" ? currentAngle : currentAngle - size; end = alignment === "end" ? currentAngle : currentAngle + size; } else { start = getStartAngle(props, Number(index)); end = getEndAngle(props, Number(index)); } const getPath = (edge): string => { const pathFunction: any = d3Shape .arc() .innerRadius(r1) .outerRadius(r2) .startAngle(transformAngle(start)) .endAngle(transformAngle(end)) .cornerRadius(cornerRadius[edge]); return pathFunction(); }; const getPathData = (edge) => { const rightPath = getPath(`${edge}Right`); const rightMoves: string[] = rightPath.match(/[A-Z]/g) || []; const rightCoords = rightPath.split(/[A-Z]/).slice(1); const rightMiddle = rightMoves.indexOf("L"); const leftPath = getPath(`${edge}Left`); const leftMoves: string[] = leftPath.match(/[A-Z]/g) || []; const leftCoords = leftPath.split(/[A-Z]/).slice(1); const leftMiddle = leftMoves.indexOf("L"); return { rightMoves, rightCoords, rightMiddle, leftMoves, leftCoords, leftMiddle, }; }; const getTopPath = () => { const { topRight, topLeft } = cornerRadius; const arcLength = r2 * Math.abs(end - start); const { rightMoves, rightCoords, rightMiddle, leftMoves, leftCoords, leftMiddle, } = getPathData("top"); let moves; let coords; if (topRight === topLeft || arcLength < 2 * topRight + 2 * topLeft) { moves = topRight > topLeft ? rightMoves : leftMoves; coords = topRight > topLeft ? rightCoords : leftCoords; } else { // eslint-disable-next-line no-magic-numbers const isShort = (middle) => middle < 3; const rightOffset = topLeft > topRight && isShort(rightMiddle) ? 1 : 2; let leftOffset; if (topRight > topLeft) { const defaultOffset = isShort(rightMiddle) ? leftMiddle : leftMiddle - 2; leftOffset = isShort(leftMiddle) ? leftMiddle - 1 : defaultOffset; } else { const defaultOffset = isShort(leftMiddle) ? 1 : 2; leftOffset = isShort(rightMiddle) ? defaultOffset : leftMiddle - 2; } moves = [ ...rightMoves.slice(0, rightOffset), ...leftMoves.slice(leftOffset), ]; coords = [ ...rightCoords.slice(0, rightOffset), ...leftCoords.slice(leftOffset), ]; } const middle = moves.indexOf("L"); const subMoves = moves.slice(0, middle); const subCoords = coords.slice(0, middle); return subMoves.map((m, i) => ({ command: m, coords: subCoords[i].split(","), })); }; const getBottomPath = () => { const { bottomRight, bottomLeft } = cornerRadius; const arcLength = r1 * Math.abs(end - start); const { rightMoves, rightCoords, rightMiddle, leftMoves, leftCoords, leftMiddle, } = getPathData("bottom"); let moves; let coords; if ( bottomRight === bottomLeft || arcLength < 2 * bottomRight + 2 * bottomLeft ) { moves = bottomRight > bottomLeft ? rightMoves : leftMoves; coords = bottomRight > bottomLeft ? rightCoords : leftCoords; } else { // eslint-disable-next-line no-magic-numbers const isShort = (m, middle) => m.length - middle < 4; const shortPath = bottomRight > bottomLeft ? isShort(rightMoves, rightMiddle) : isShort(leftMoves, leftMiddle); // eslint-disable-next-line no-magic-numbers const rightOffset = shortPath ? -1 : -3; moves = [ ...leftMoves.slice(0, leftMiddle + 2), ...rightMoves.slice(rightOffset), ]; coords = [ ...leftCoords.slice(0, leftMiddle + 2), ...rightCoords.slice(rightOffset), ]; } const middle = moves.indexOf("L"); const subMoves = moves.slice(middle, -1); const subCoords = coords.slice(middle, -1); return subMoves.map((m, i) => ({ command: m, coords: subCoords[i].split(","), })); }; const topPath = getTopPath(); const bottomPath = getBottomPath(); const moves = [...topPath, ...bottomPath]; const path = moves.reduce( (memo, move) => `${memo}${move.command} ${move.coords.join()}`, "", ); return `${path} z`; }; export const getBarPath = (props: BarProps, width: number, cornerRadius) => { if (props.getPath) { return getCustomBarPath(props, width); } return props.horizontal ? getHorizontalBarPath(props, width, cornerRadius) : getVerticalBarPath(props, width, cornerRadius); }; export const getPolarBarPath = (props: BarProps, cornerRadius) => { // TODO Radial bars return getVerticalPolarBarPath(props, cornerRadius); }; ================================================ FILE: packages/victory-bar/src/victory-bar.test.tsx ================================================ import * as React from "react"; import { render, fireEvent, screen } from "@testing-library/react"; import { VictoryChart } from "victory-chart"; import { Helpers } from "victory-core"; import { isBar, getBarHeight } from "../../../test/helpers"; import { Bar } from "./bar"; import { VictoryBar } from "./victory-bar"; describe("components/victory-bar", () => { describe("default component rendering", () => { it("attaches safe user props to the container component", () => { render( , ); const container = screen.getByTestId("victory-bar"); expect(screen.getByLabelText("Chart")).toBeDefined(); expect(container).not.toHaveAttribute("unsafe-prop"); expect(container.tagName).toEqual("svg"); }); it("attaches safe user props to the group component if the component is rendered inside a VictoryChart", () => { render( , { wrapper: VictoryChart }, ); const container = screen.getByTestId("victory-bar"); expect(screen.getByLabelText("Chart")).toBeDefined(); expect(container).not.toHaveAttribute("unsafe-prop"); expect(container.tagName).toEqual("g"); }); it("renders an svg with the correct width and height", () => { const { container } = render(); const svg = container.querySelector("svg"); expect(svg?.getAttribute("style")).toContain("width: 100%; height: 100%"); }); it("renders an svg with the correct viewBox", () => { const { container } = render(); const svg = container.querySelector("svg"); const viewBoxValue = `0 0 ${450} ${300}`; expect(svg?.getAttribute("viewBox")).toEqual(viewBoxValue); }); it("renders 4 bars", () => { const { container } = render(); const bars = container.querySelectorAll("path"); expect(bars.length).toEqual(4); }); it("renders each bar as a rectangle", () => { const { container } = render(); const barCommandStrings = Array.from( container.querySelectorAll("path"), ).map((bar) => bar.getAttribute("d")); barCommandStrings.forEach((commandString) => { expect(isBar(commandString)).toBeTruthy(); }); }); }); describe("rendering data", () => { it("renders bars for {x, y} shaped data (default)", () => { const data = Helpers.range(10).map((i) => ({ x: i, y: i })); const { container } = render(); const bars = container.querySelectorAll("path"); expect(bars.length).toEqual(10); }); it("renders ordered bars when sortKey is passed", () => { const data = Helpers.range(5) .map((i) => ({ x: i, y: i })) .reverse(); const { container } = render(); const barHeight = Array.from(container.querySelectorAll("path")).map( (bar) => { const commandString = bar.getAttribute("d"); return getBarHeight(commandString); }, ); const ascendingBars = [...barHeight].sort((a, b) => a - b); expect(barHeight).toEqual(ascendingBars); }); it("renders reverse ordered bars when sortOrder is descending", () => { const data = Helpers.range(5) .map((i) => ({ x: i, y: i })) .reverse(); const { container } = render( , ); const barHeight = Array.from(container.querySelectorAll("path")).map( (bar) => { const commandString = bar.getAttribute("d"); return getBarHeight(commandString); }, ); const descendingBars = [...barHeight].sort((a, b) => b - a); expect(barHeight).toEqual(descendingBars); }); it("renders bars for array-shaped data", () => { const data = Helpers.range(20).map((i) => [i, i]); const { container } = render(); const bars = container.querySelectorAll("path"); expect(bars).toHaveLength(20); }); it("renders bars for deeply-nested data", () => { const data = Helpers.range(8).map((i) => ({ a: { b: [{ x: i, y: i }] }, })); const { container } = render( , ); const bars = container.querySelectorAll("path"); expect(bars).toHaveLength(8); }); it("renders bars values with null accessor", () => { const data = Helpers.range(8); const { container } = render( // @ts-expect-error "'null' is not assignable to 'x'" , ); const bars = container.querySelectorAll("path"); expect(bars).toHaveLength(8); }); it("renders bars with appropriate relative heights", () => { const { container } = render( , ); const bars = Array.from(container.querySelectorAll("path")); const heights = bars.map((bar) => { const commandString = bar.getAttribute("d"); return getBarHeight(commandString); }); expect(Math.trunc(heights[1] / 2)).toEqual(Math.trunc(heights[0])); expect(((heights[2] / 3) * 2).toFixed(3)).toEqual(heights[1].toFixed(3)); }); it("does not render data with null x or y values", () => { const data = [ { x: 1, y: 2 }, { x: null, y: 4 }, { x: 5, y: null }, ]; const { container } = render(); expect(container.querySelectorAll("path")).toHaveLength(1); }); }); describe("event handling", () => { const clickHandler = jest.fn(); beforeEach(() => { clickHandler.mockReset(); }); it("attaches an event to the parent svg", () => { const { container } = render( , ); const bar = container.querySelector("path"); fireEvent.click(bar!); expect(clickHandler).toHaveBeenCalled(); }); it("attaches an event to data", () => { const data = [ { x: 0, y: 0, label: "0" }, { x: 1, y: 1, label: "1" }, { x: 2, y: 2, label: "2" }, ]; const { container } = render( , ); const bars = container.querySelectorAll("path"); bars.forEach((bar, index) => { clickHandler.mockReset(); fireEvent.click(bar); expect(clickHandler).toHaveBeenCalled(); const barContext = clickHandler.mock.calls[0][1]; expect(barContext.datum.x).toEqual(data[index].x); }); }); it("attaches an event to a label", () => { const data = [ { x: 0, y: 0, label: "label 0" }, { x: 1, y: 1, label: "label 1" }, { x: 2, y: 2, label: "label 2" }, ]; render( datum.label} events={[ { target: "labels", eventHandlers: { onClick: clickHandler }, }, ]} />, ); const label = screen.getByText("label 1"); fireEvent.click(label); expect(clickHandler).toHaveBeenCalled(); }); }); describe("accessibility", () => { it("adds an aria role to each bar in the series", () => { const data = Helpers.range(10).map((y, x) => ({ x, y })); render(); const presentationElements = screen.getAllByRole("presentation"); expect(presentationElements).toHaveLength(11); // bars plus container }); it("applies aria-label and tabIndex to the Bar primitive", () => { const data = Helpers.range(5, 11).map((y, x) => ({ y, x })); const { container } = render( `x: ${datum.x}`} tabIndex={({ index }) => (index as number) + 1} /> } />, ); container.querySelectorAll("path").forEach((bar, index) => { expect(parseInt(bar.getAttribute("tabindex")!)).toEqual(index + 1); expect(bar.getAttribute("aria-label")).toEqual(`x: ${data[index].x}`); }); }); }); }); ================================================ FILE: packages/victory-bar/src/victory-bar.tsx ================================================ import React from "react"; import { getBaseProps } from "./helper-methods"; import { Bar } from "./bar"; import { Helpers, VictoryLabel, VictoryContainer, VictoryTheme, addEvents, Data, Domain, UserProps, EventPropTypeInterface, NumberOrCallback, StringOrNumberOrCallback, VictoryClipContainer, VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, VictoryStyleInterface, EventsMixinClass, } from "victory-core"; export type VictoryBarCornerRadiusKey = | "top" | "bottom" | "topLeft" | "topRight" | "bottomLeft" | "bottomRight"; export type VictoryBarCornerRadiusObject = Partial< Record >; export type VictoryBarTTargetType = "data" | "labels" | "parent"; export type VictoryBarAlignmentType = "start" | "middle" | "end"; export interface VictoryBarProps extends VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps { alignment?: VictoryBarAlignmentType; barRatio?: number; barWidth?: NumberOrCallback; cornerRadius?: NumberOrCallback | VictoryBarCornerRadiusObject; events?: EventPropTypeInterface< VictoryBarTTargetType, number | string | number[] | string[] >[]; eventKey?: StringOrNumberOrCallback; getPath?: (props: VictoryBarProps) => string; style?: VictoryStyleInterface; } const fallbackProps = { width: 450, height: 300, padding: 50, }; const defaultData = [ { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 }, { x: 4, y: 4 }, ]; // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryBarBase extends EventsMixinClass {} /** * Draw SVG bar charts with React. VictoryBar is a composable component, so it doesn"t include axes * Check out VictoryChart for complete bar charts and more. */ class VictoryBarBase extends React.Component { static animationWhitelist: (keyof VictoryBarProps)[] = [ "data", "domain", "height", "padding", "style", "width", ]; static displayName = "VictoryBar"; static role = "bar"; static defaultTransitions = { onLoad: { duration: 2000, before: () => ({ _y: 0, _y1: 0, _y0: 0 }), after: (datum) => ({ _y: datum._y, _y1: datum._y1, _y0: datum._y0 }), }, onExit: { duration: 500, before: () => ({ _y: 0, yOffset: 0 }), }, onEnter: { duration: 500, before: () => ({ _y: 0, _y1: 0, _y0: 0 }), after: (datum) => ({ _y: datum._y, _y1: datum._y1, _y0: datum._y0 }), }, }; static defaultProps: VictoryBarProps = { containerComponent: , data: defaultData, dataComponent: , groupComponent: , labelComponent: , samples: 50, sortOrder: "ascending", standalone: true, theme: VictoryTheme.grayscale, }; static getDomain = Domain.getDomainWithZero; static getData = Data.getData; static getBaseProps(props: VictoryBarProps) { return getBaseProps(props, fallbackProps); } static expectedComponents: (keyof VictoryBarProps)[] = [ "dataComponent", "labelComponent", "groupComponent", "containerComponent", ]; // passed to addEvents.renderData to prevent data props with undefined _x or _y from excluding data from render. // used when inside of VictoryZoomContainer static shouldRenderDatum = () => true; // Overridden in native versions shouldAnimate() { return !!this.props.animate; } render(): React.ReactElement { const { animationWhitelist, role } = VictoryBar; const props = Helpers.modifyProps(this.props, fallbackProps, role); if (this.shouldAnimate()) { return this.animateComponent(props, animationWhitelist); } let children; // when inside a zoom container (the only place VictoryClipContainer is used), all data // should be renderable so bars won't dissappear before they've fully exited the container's bounds // see https://github.com/FormidableLabs/victory/pull/2970 if (props.groupComponent?.type === VictoryClipContainer) { children = this.renderData(props, VictoryBarBase.shouldRenderDatum); } else { children = this.renderData(props); } const component = props.standalone ? this.renderContainer(props.containerComponent, children) : children; return UserProps.withSafeUserProps(component, props); } } export const VictoryBar = addEvents(VictoryBarBase); ================================================ FILE: packages/victory-bar/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-bar/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-box-plot/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-box-plot/CHANGELOG.md ================================================ # victory-box-plot ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash keys with native code ([#2811](https://github.com/FormidableLabs/victory/pull/2811)) * Replace lodash array utils with native code ([#2810](https://github.com/FormidableLabs/victory/pull/2810)) - Replace lodash isNil and isNan with native code ([#2800](https://github.com/FormidableLabs/victory/pull/2800)) * Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ### Patch Changes - Migrate victory-native to TypeScript ([#2739](https://github.com/FormidableLabs/victory/pull/2739)) ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 - victory-vendor@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 - victory-vendor@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 - victory-vendor@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416), [`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 - victory-vendor@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 - victory-vendor@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 - victory-vendor@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 - victory-vendor@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 - victory-vendor@36.6.1 ## 36.6.0 ### Minor Changes - Migrated victory-box-plot to TypeScript ([#2387](https://github.com/FormidableLabs/victory/pull/2387)) ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 - victory-vendor@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-box-plot/README.md ================================================ # VictoryBoxPlot `victory-box-plot@^30.0.0` exports `VictoryBoxPlot` component To view documentation for `VictoryBoxPlot` please see https://commerce.nearform.com/open-source/victory/docs/victory-box-plot To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-boxplot.md ================================================ FILE: packages/victory-box-plot/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-box-plot/package.json ================================================ { "name": "victory-box-plot", "version": "37.3.6", "description": "Box Plot Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6", "victory-vendor": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "devDependencies": { "victory-chart": "*" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs", "../victory-vendor:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-chart:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-box-plot/src/helper-methods.tsx ================================================ import defaults from "lodash/defaults"; import groupBy from "lodash/groupBy"; import orderBy from "lodash/orderBy"; import uniq from "lodash/uniq"; import { Helpers, Scale, Domain, Data, Collection } from "victory-core"; import { min as d3Min, max as d3Max, quantile as d3Quantile, } from "victory-vendor/d3-array"; import { VictoryBoxPlotProps } from "./victory-box-plot"; const TYPES = ["max", "min", "median", "q1", "q3"]; const checkProcessedData = (data) => { /* check if the data is pre-processed. start by checking that it has all required quartile attributes. */ const hasQuartileAttributes = data.every((datum) => { return TYPES.every((val) => typeof datum[`_${val}`] !== "undefined"); }); if (hasQuartileAttributes) { // check that the independent variable is distinct const values = data.map((d) => d._x); if (!uniq(values).length === values.length) { throw new Error(` data prop may only take an array of objects with a unique independent variable. Make sure your x values are distinct. `); } return true; } return false; }; const nanToNull = (val) => (Number.isNaN(val) ? null : val); const getSummaryStatistics = (data) => { const dependentVars = data.map((datum) => datum._y); const quartiles = { _q1: nanToNull(d3Quantile(dependentVars, 0.25)), // eslint-disable-line no-magic-numbers _q3: nanToNull(d3Quantile(dependentVars, 0.75)), // eslint-disable-line no-magic-numbers _min: nanToNull(d3Min(dependentVars)), _median: nanToNull(d3Quantile(dependentVars, 0.5)), _max: nanToNull(d3Max(dependentVars)), }; return Object.assign({}, data[0], quartiles, { _y: data[0]._y }); }; const processData = (data) => { /* check if the data is coming in a pre-processed form, i.e. { x || y, min, max, q1, q3, median }. if not, process it. */ const isProcessed = checkProcessedData(data); if (!isProcessed) { // check if the data is coming with x or y values as an array const arrayX = data.every((datum) => Array.isArray(datum._x)); const arrayY = data.every((datum) => Array.isArray(datum._y)); const sortKey = "_y"; const groupKey = "_x"; if (arrayX) { throw new Error(` data should not be given as in array for x `); } else if (arrayY) { /* generate summary statistics for each datum. to do this, flatten the depedentVarArray and process each datum separately */ return data.map((datum) => { const dataArray = datum[sortKey].map((d) => Object.assign({}, datum, { [sortKey]: d }), ); const sortedData = orderBy(dataArray, sortKey); return getSummaryStatistics(sortedData); }); } else { /* Group data by independent variable and generate summary statistics for each group */ const groupedData = groupBy(data, groupKey); return Object.keys(groupedData).map((key) => { const datum = groupedData[key]; const sortedData = orderBy(datum, sortKey); return getSummaryStatistics(sortedData); }); } } else { return data; } }; export const getData = (props) => { const accessorTypes = TYPES.concat("x", "y"); const formattedData = Data.formatData(props.data, props, accessorTypes); return formattedData.length ? processData(formattedData) : []; }; const reduceDataset = (dataset, props, axis) => { const minDomain = Domain.getMinFromProps(props, axis); const maxDomain = Domain.getMaxFromProps(props, axis); const minData = minDomain !== undefined ? minDomain : dataset.reduce((memo, datum) => { return memo < datum[`_${axis}`] ? memo : datum[`_${axis}`]; }, Infinity); const maxData = maxDomain !== undefined ? maxDomain : dataset.reduce((memo, datum) => { return memo > datum[`_${axis}`] ? memo : datum[`_${axis}`]; }, -Infinity); return Domain.getDomainFromMinMax(minData, maxData); }; const getDomainFromMinMaxValues = (dataset, props, axis) => { const minDomain = Domain.getMinFromProps(props, axis); const maxDomain = Domain.getMaxFromProps(props, axis); const minData = minDomain !== undefined ? minDomain : dataset.reduce((memo, datum) => { return memo < datum._min ? memo : datum._min; }, Infinity); const maxData = maxDomain !== undefined ? maxDomain : dataset.reduce((memo, datum) => { return memo > datum._max ? memo : datum._max; }, -Infinity); return Domain.getDomainFromMinMax(minData, maxData); }; const getDomainFromData = (props, axis) => { const minDomain = Domain.getMinFromProps(props, axis); const maxDomain = Domain.getMaxFromProps(props, axis); const dataset = getData(props); if (dataset.length < 1) { return minDomain !== undefined && maxDomain !== undefined ? Domain.getDomainFromMinMax(minDomain, maxDomain) : undefined; } return axis === "y" ? getDomainFromMinMaxValues(dataset, props, axis) : reduceDataset(dataset, props, axis); }; export const getDomain = (props, axis) => { return Domain.createDomainFunction(getDomainFromData)(props, axis); }; const getLabelStyle = (props, styleObject, namespace?) => { const component = props[`${namespace}LabelComponent`] || props.labelComponent; const baseStyle = styleObject[`${namespace}Labels`] || styleObject.labels; if (!Helpers.isTooltip(component)) { return baseStyle; } const tooltipTheme = (props.theme && props.theme.tooltip) || {}; return defaults({}, tooltipTheme.style, baseStyle); }; const getStyles = (props, styleObject: VictoryBoxPlotProps["style"] = {}) => { if (props.disableInlineStyles) { return {}; } const style = props.style || {}; const parentStyles = { height: "100%", width: "100%" }; const labelStyles = defaults( {}, style.labels, getLabelStyle(props, styleObject), ); const boxStyles = defaults({}, style.boxes, styleObject.boxes); const whiskerStyles = defaults({}, style.whiskers, styleObject.whiskers); return { boxes: boxStyles, labels: labelStyles, parent: defaults({}, style.parent, styleObject.parent, parentStyles), max: defaults({}, style.max, styleObject.max, whiskerStyles), maxLabels: defaults( {}, style.maxLabels, getLabelStyle(props, styleObject, "max"), labelStyles, ), median: defaults({}, style.median, styleObject.median, whiskerStyles), medianLabels: defaults( {}, style.medianLabels, getLabelStyle(props, styleObject, "median"), labelStyles, ), min: defaults({}, style.min, styleObject.min, whiskerStyles), minLabels: defaults( {}, style.minLabels, getLabelStyle(props, styleObject, "min"), labelStyles, ), q1: defaults({}, style.q1, styleObject.q1, boxStyles), q1Labels: defaults( {}, style.q1Labels, getLabelStyle(props, styleObject, "q1"), labelStyles, ), q3: defaults({}, style.q3, styleObject.q3, boxStyles), q3Labels: defaults( {}, style.q3Labels, getLabelStyle(props, styleObject, "q3"), labelStyles, ), whiskers: whiskerStyles, }; }; const getCalculatedValues = (props) => { const { theme, horizontal } = props; const data = getData(props); const range = { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; const domain = { x: getDomain(props, "x"), y: getDomain(props, "y"), }; const scale = { x: Scale.getBaseScale(props, "x") .domain(domain.x) .range(props.horizontal ? range.y : range.x), y: Scale.getBaseScale(props, "y") .domain(domain.y) .range(props.horizontal ? range.x : range.y), }; const defaultStyles = theme && theme.boxplot && theme.boxplot.style ? theme.boxplot.style : {}; const style = getStyles(props, defaultStyles); const defaultOrientation = props.horizontal ? "top" : "right"; const labelOrientation = props.labelOrientation || defaultOrientation; const boxWidth = props.boxWidth || 1; return { data, horizontal, domain, scale, style, labelOrientation, boxWidth }; }; const getWhiskerProps = (props, type) => { const { horizontal, style, boxWidth, whiskerWidth, datum, scale, index, disableInlineStyles, } = props; const { min, max, q1, q3, x, y } = props.positions; const boxValue = type === "min" ? q1 : q3; const whiskerValue = type === "min" ? min : max; const width = typeof whiskerWidth === "number" ? whiskerWidth : boxWidth; return { datum, index, scale, majorWhisker: { x1: horizontal ? boxValue : x, y1: horizontal ? y : boxValue, x2: horizontal ? whiskerValue : x, y2: horizontal ? y : whiskerValue, }, minorWhisker: { x1: horizontal ? whiskerValue : x - width / 2, y1: horizontal ? y - width / 2 : whiskerValue, x2: horizontal ? whiskerValue : x + width / 2, y2: horizontal ? y + width / 2 : whiskerValue, }, style: disableInlineStyles ? {} : style[type] || style.whisker, disableInlineStyles, }; }; const getBoxProps = (props, type) => { const { horizontal, boxWidth, style, scale, datum, index, disableInlineStyles, } = props; const { median, q1, q3, x, y } = props.positions; const defaultX = type === "q1" ? q1 : median; const defaultY = type === "q1" ? median : q3; const defaultWidth = type === "q1" ? median - q1 : q3 - median; const defaultHeight = type === "q1" ? q1 - median : median - q3; return { datum, scale, index, x: horizontal ? defaultX : x - boxWidth / 2, y: horizontal ? y - boxWidth / 2 : defaultY, width: horizontal ? defaultWidth : boxWidth, height: horizontal ? boxWidth : defaultHeight, style: disableInlineStyles ? {} : style[type] || style.boxes, disableInlineStyles, }; }; const getMedianProps = (props) => { const { boxWidth, horizontal, style, datum, scale, index, disableInlineStyles, } = props; const { median, x, y } = props.positions; return { datum, scale, index, x1: horizontal ? median : x - boxWidth / 2, y1: horizontal ? y - boxWidth / 2 : median, x2: horizontal ? median : x + boxWidth / 2, y2: horizontal ? y + boxWidth / 2 : median, style: disableInlineStyles ? {} : style.median, disableInlineStyles, }; }; const getText = (props, type) => { const { datum, index, labels } = props; const propName = `${type}Labels`; const labelProp = props[propName]; if (!labelProp && !labels) { return null; } else if (labelProp === true || labels === true) { const dataName = `_${type}`; return `${datum[dataName]}`; } return Array.isArray(labelProp) ? labelProp[index] : labelProp; }; const getOrientation = (labelOrientation, type) => (typeof labelOrientation === "object" && labelOrientation[type]) || labelOrientation; const getLabelProps = (props, text, type) => { const { datum, positions, index, boxWidth, horizontal, labelOrientation, style, theme, disableInlineStyles, } = props; const orientation = getOrientation(labelOrientation, type); const namespace = `${type}Labels`; const labelStyle = style[namespace] || style.labels; const defaultVerticalAnchors = { top: "end", bottom: "start", left: "middle", right: "middle", }; const defaultTextAnchors = { left: "end", right: "start", top: "middle", bottom: "middle", }; const whiskerWidth = typeof props.whiskerWidth === "number" ? props.whiskerWidth : boxWidth; const width = type === "min" || type === "max" ? whiskerWidth : boxWidth; const getOffset = (coord) => { const sign = { x: orientation === "left" ? -1 : 1, y: orientation === "top" ? -1 : 1, }; return (sign[coord] * width) / 2 + sign[coord] * (labelStyle.padding || 0); }; const labelProps = { text, datum, index, orientation, style: disableInlineStyles ? {} : labelStyle, y: horizontal ? positions.y : positions[type], x: horizontal ? positions[type] : positions.x, dy: horizontal ? getOffset("y") : 0, dx: horizontal ? 0 : getOffset("x"), textAnchor: labelStyle.textAnchor || defaultTextAnchors[orientation], verticalAnchor: labelStyle.verticalAnchor || defaultVerticalAnchors[orientation], angle: labelStyle.angle, horizontal, disableInlineStyles, }; const component = props[`${type}LabelComponent`]; if (!Helpers.isTooltip(component)) { return labelProps; } const tooltipTheme = (theme && theme.tooltip) || {}; return defaults({}, labelProps, Helpers.omit(tooltipTheme, ["style"])); }; const getDataProps = (props, type) => { if (type === "median") { return getMedianProps(props); } else if (type === "min" || type === "max") { return getWhiskerProps(props, type); } return getBoxProps(props, type); }; // if all data points on an axis are out of bound of the domain, filter out this datum const isDatumOutOfBounds = (datum, domain) => { const exists = (val) => val !== undefined; const { _x, _min, _max } = datum; const minDomainX = Collection.getMinValue(domain.x); const maxDomainX = Collection.getMaxValue(domain.x); const minDomainY = Collection.getMinValue(domain.y); const maxDomainY = Collection.getMaxValue(domain.y); const underMin = (min) => (val) => exists(val) && val < min; const overMax = (max) => (val) => exists(val) && val > max; const isUnderMinX = underMin(minDomainX); const isUnderMinY = underMin(minDomainY); const isOverMaxX = overMax(maxDomainX); const isOverMaxY = overMax(maxDomainY); let yOutOfBounds; let xOutOfBounds; // if x is out of the bounds of the domain if (isUnderMinX(_x) || isOverMaxX(_x)) xOutOfBounds = true; // if min/max are out of the bounds of the domain if ( (isUnderMinY(_min) && isUnderMinY(_max)) || (isOverMaxY(_min) && isOverMaxY(_max)) ) yOutOfBounds = true; return yOutOfBounds || xOutOfBounds; }; export const getBaseProps = (initialProps, fallbackProps) => { const modifiedProps = Helpers.modifyProps( initialProps, fallbackProps, "boxplot", ); const props = Object.assign( {}, modifiedProps, getCalculatedValues(modifiedProps), ); const { groupComponent, width, height, padding, standalone, theme, events, sharedEvents, scale, horizontal, data, style, domain, name, } = props; const initialChildProps = { parent: { domain, scale, width, height, data, standalone, name, theme, style: style.parent || {}, padding, groupComponent, horizontal, }, }; const boxScale = scale.y; return data.reduce((acc, datum, index) => { const eventKey = !Helpers.isNil(datum.eventKey) ? datum.eventKey : index; if (isDatumOutOfBounds(datum, domain)) return acc; const positions = { x: horizontal ? scale.y(datum._y) : scale.x(datum._x), y: horizontal ? scale.x(datum._x) : scale.y(datum._y), min: boxScale(datum._min), max: boxScale(datum._max), median: boxScale(datum._median), q1: boxScale(datum._q1), q3: boxScale(datum._q3), }; const dataProps = Object.assign({ index, datum, positions }, props); const dataObj = TYPES.reduce((memo, type) => { memo[type] = getDataProps(dataProps, type); return memo; }, {}); acc[eventKey] = dataObj; TYPES.forEach((type) => { const labelText = getText(dataProps, type); const labelProp = props.labels || props[`${type}Labels`]; if ( (labelText !== null && labelText !== undefined) || (labelProp && (events || sharedEvents)) ) { const target = `${type}Labels`; acc[eventKey][target] = getLabelProps( Object.assign({}, props, dataProps), labelText, type, ); } }); return acc; }, initialChildProps); }; ================================================ FILE: packages/victory-box-plot/src/index.ts ================================================ export * from "./victory-box-plot"; ================================================ FILE: packages/victory-box-plot/src/victory-box-plot.test.tsx ================================================ import { render, screen } from "@testing-library/react"; import React from "react"; import { VictoryChart } from "victory-chart"; import { Border, LineSegment, Whisker } from "victory-core"; import { VictoryBoxPlot } from "./victory-box-plot"; const TEST_GROUP_ID = "test-group-id"; const dataset = [ { x: 1, min: 1, max: 18, median: 8, q1: 5, q3: 15 }, { x: 2, min: 4, max: 20, median: 10, q1: 7, q3: 15 }, { x: 3, min: 3, max: 12, median: 6, q1: 5, q3: 10 }, ]; const TestGroup = ({ children }: { children?: React.ReactElement[] }) => { return {children}; }; const renderWithTestGroup = (data: any[] = dataset) => { const { container } = render( } />, ); const groups = screen.getAllByTestId(TEST_GROUP_ID); return { container, groups }; }; describe("components/victory-box-plot", () => { describe("default component rendering", () => { it("attaches safe user props to the container component", () => { render( , ); const container = screen.getByTestId("victory-boxplot"); expect(screen.getByLabelText("Chart")).toBeDefined(); expect(container).not.toHaveAttribute("unsafe-prop"); expect(container.nodeName).toEqual("svg"); }); it("attaches safe user props to the group component if the component is rendered inside a VictoryChart", () => { render( , { wrapper: VictoryChart }, ); const container = screen.getByTestId("victory-boxplot"); expect(screen.getByLabelText("Chart")).toBeDefined(); expect(container).not.toHaveAttribute("unsafe-prop"); expect(container.nodeName).toEqual("g"); }); it("renders an svg with the correct width and height", () => { const { container } = render(); const svg = container.querySelector("svg"); expect(svg?.getAttribute("style")).toContain("width: 100%; height: 100%"); }); it("renders an svg with the correct viewBox", () => { const { container } = render(); const svg = container.querySelector("svg"); const viewBoxValue = `0 0 ${450} ${300}`; expect(svg?.getAttribute("viewBox")).toEqual(viewBoxValue); }); it("renders 3 points", () => { const { container } = render(); const points = container.querySelectorAll("rect"); // two boxes per point expect(points).toHaveLength(6); }); }); it("does not render data with null x or y values", () => { const data = [ { x: 1, y: 2 }, { x: 1, y: 3 }, { x: null, y: 2 }, { x: null, y: 3 }, { x: 2, y: null }, { x: 2, y: null }, ]; const { groups } = renderWithTestGroup(data); expect(groups).toHaveLength(1); }); it("does not render data with null y values when given an array", () => { const data = [ { x: 1, y: [1, 2, 3] }, { x: 1, y: [null, 2, 5] }, ]; const { groups } = renderWithTestGroup(data); expect(groups).toHaveLength(1); }); it("does not render data with null min, max, median, q1, or q3 values", () => { const data = [ { x: 1, min: 2, median: 5, max: 10, q1: 3, q3: 7 }, { x: 2, min: null, median: 4, max: 9, q1: 3, q3: 6 }, { x: 3, min: 1, median: null, max: 12, q1: 4, q3: 10 }, { x: 4, min: 3, median: 9, max: null, q1: 5, q3: 13 }, { x: 5, min: 2, median: 8, max: 15, q1: null, q3: 12 }, { x: 5, min: 2, median: 10, max: 20, q1: 8, q3: null }, ]; const { groups } = renderWithTestGroup(data); expect(groups).toHaveLength(1); }); describe("accessibility", () => { const buildLabel = (a, b) => `First value: ${a} Second value: ${b}`; it("adds an aria role to each point in the series", () => { const { container } = render(); container.querySelectorAll("rect").forEach((box) => { expect(box.getAttribute("role")).toEqual("presentation"); }); }); it("applies aria-label to whisker primitive", () => { render( buildLabel(datum.x, datum._max)} tabIndex={({ index }) => Number(index) + 1} /> } />, ); dataset .map(({ x, max }) => buildLabel(x, max)) .forEach((label, index) => { const [labeledWhisker] = screen.getAllByLabelText(label); expect(labeledWhisker).toBeDefined(); expect(labeledWhisker.getAttribute("tabindex")).toEqual( `${index + 1}`, ); }); }); it("applies aria-label to border primitive", () => { render( buildLabel(datum.x, datum._q3)} tabIndex={({ index }) => Number(index) + 1} /> } />, ); dataset .map(({ x, q3 }) => buildLabel(x, q3)) .forEach((label, index) => { const labeledBorder = screen.getByLabelText(label); expect(labeledBorder).toBeDefined(); expect(labeledBorder.getAttribute("tabindex")).toEqual( `${index + 1}`, ); }); }); it("applies tabIndex and aria-label to line-segmnet primitive", () => { render( buildLabel(datum.x, datum._median)} tabIndex={({ index }) => Number(index) + 1} /> } />, ); dataset .map(({ x, median }) => buildLabel(x, median)) .forEach((label, index) => { const labeledLine = screen.getByLabelText(label); expect(labeledLine).toBeDefined(); expect(labeledLine.getAttribute("tabindex")).toEqual(`${index + 1}`); }); }); }); }); ================================================ FILE: packages/victory-box-plot/src/victory-box-plot.tsx ================================================ import React from "react"; import { Helpers, VictoryLabel, addEvents, LineSegment, VictoryContainer, VictoryTheme, Box, Whisker, DefaultTransitions, UserProps, EventPropTypeInterface, DomainPropType, DomainPaddingPropType, OrientationTypes, StringOrNumberOrCallback, VictoryDatableProps, VictoryCommonProps, VictoryStyleObject, VictoryLabelStyleObject, EventsMixinClass, } from "victory-core"; import { getDomain, getData, getBaseProps } from "./helper-methods"; const fallbackProps = { width: 450, height: 300, padding: { top: 20, right: 20, bottom: 20, left: 20, }, }; const defaultData = [ { x: 1, min: 5, q1: 7, median: 12, q3: 18, max: 20 }, { x: 2, min: 2, q1: 5, median: 8, q3: 12, max: 15 }, ]; const options = { components: [ { name: "min" }, { name: "minLabels" }, { name: "max" }, { name: "maxLabels" }, { name: "median" }, { name: "medianLabels" }, { name: "q1" }, { name: "q1Labels" }, { name: "q3" }, { name: "q3Labels" }, { name: "parent", index: "parent" }, ], }; export type VictoryBoxPlotLabelType = | boolean | (string | number)[] | { (): any } | { (data: any): string | null }; export interface VictoryBoxPlotStyleInterface { parent?: VictoryStyleObject; max?: VictoryStyleObject; maxLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; min?: VictoryStyleObject; minLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; median?: VictoryStyleObject; medianLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; q1?: VictoryStyleObject; q1Labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; q3?: VictoryStyleObject; q3Labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; boxes?: VictoryStyleObject; whiskers?: VictoryStyleObject; } export interface VictoryBoxPlotLabelOrientationInterface { max?: OrientationTypes; min?: OrientationTypes; median?: OrientationTypes; q1?: OrientationTypes; q3?: OrientationTypes; } export interface VictoryBoxPlotProps extends VictoryCommonProps, VictoryDatableProps { boxWidth?: number; datum?: any; domain?: DomainPropType; domainPadding?: DomainPaddingPropType; events?: EventPropTypeInterface[]; eventKey?: StringOrNumberOrCallback; horizontal?: boolean; labelOrientation?: OrientationTypes | VictoryBoxPlotLabelOrientationInterface; labels?: boolean; max?: StringOrNumberOrCallback | string[]; maxComponent?: React.ReactElement; maxLabelComponent?: React.ReactElement; maxLabels?: VictoryBoxPlotLabelType; median?: StringOrNumberOrCallback | string[]; medianComponent?: React.ReactElement; medianLabelComponent?: React.ReactElement; medianLabels?: VictoryBoxPlotLabelType; min?: StringOrNumberOrCallback | string[]; minComponent?: React.ReactElement; minLabelComponent?: React.ReactElement; minLabels?: VictoryBoxPlotLabelType; q1?: StringOrNumberOrCallback | string[]; q1Component?: React.ReactElement; q1LabelComponent?: React.ReactElement; q1Labels?: VictoryBoxPlotLabelType; q3?: StringOrNumberOrCallback | string[]; q3Component?: React.ReactElement; q3LabelComponent?: React.ReactElement; q3Labels?: VictoryBoxPlotLabelType; style?: VictoryBoxPlotStyleInterface; text?: StringOrNumberOrCallback | string[]; whiskerWidth?: number; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryBoxPlotBase extends EventsMixinClass {} class VictoryBoxPlotBase extends React.Component { static animationWhitelist: Array = [ "data", "domain", "height", "padding", "style", "width", ]; static displayName = "VictoryBoxPlot"; static role = "boxplot"; static defaultTransitions = DefaultTransitions.discreteTransitions(); static defaultProps: VictoryBoxPlotProps = { containerComponent: , data: defaultData, dataComponent: , groupComponent: , maxComponent: , maxLabelComponent: , medianComponent: , medianLabelComponent: , minComponent: , minLabelComponent: , q1Component: , q1LabelComponent: , q3Component: , q3LabelComponent: , samples: 50, sortKey: "x", sortOrder: "ascending", standalone: true, theme: VictoryTheme.grayscale, }; static getDomain(props, axis) { return getDomain(props, axis); } static getData(props) { return getData(props); } static getBaseProps(props) { return getBaseProps(props, fallbackProps); } static expectedComponents: Array = [ "maxComponent", "maxLabelComponent", "medianComponent", "medianLabelComponent", "minComponent", "minLabelComponent", "q1Component", "q1LabelComponent", "q3Component", "q3LabelComponent", "groupComponent", "containerComponent", ]; renderBoxPlot(props) { const types = ["q1", "q3", "max", "min", "median"]; const dataComponents = types .map((type) => { return this.dataKeys.reduce((validDataComponents, _key, index) => { const baseComponent = props[`${type}Component`]; const componentProps = this.getComponentProps( baseComponent, type, index, ); if (this.shouldRenderDatum(componentProps.datum)) { validDataComponents.push( React.cloneElement(baseComponent, componentProps), ); } return validDataComponents; }, [] as React.ReactElement[]); }) .flat(); const labelComponents = types .map((type) => { const components = this.dataKeys.reduce( (validComponents, _key, index) => { const name = `${type}Labels`; const baseComponent = props[`${type}LabelComponent`]; const labelProps = this.getComponentProps( baseComponent, name, index, ); if (labelProps.text !== undefined && labelProps.text !== null) { validComponents.push( React.cloneElement(baseComponent, labelProps), ); } return validComponents; }, [] as React.ReactElement[], ); return components.filter(Boolean); }) .flat(); const children = [...dataComponents, ...labelComponents]; return this.renderContainer(props.groupComponent, children); } // Overridden in native versions shouldAnimate() { return !!this.props.animate; } shouldRenderDatum(datum) { const hasX = !Helpers.isNil(datum._x); const hasY = !Helpers.isNil(datum._y); const hasSummaryStatistics = !Helpers.isNil(datum._min) && !Helpers.isNil(datum._max) && !Helpers.isNil(datum._median) && !Helpers.isNil(datum._q1) && !Helpers.isNil(datum._q3); return hasSummaryStatistics && (this.props.horizontal ? hasY : hasX); } render(): React.ReactElement { const { animationWhitelist, role } = VictoryBoxPlot; const props = Helpers.modifyProps(this.props, fallbackProps, role); if (this.shouldAnimate()) { return this.animateComponent(props, animationWhitelist); } const children = this.renderBoxPlot(props); const component = props.standalone ? this.renderContainer(props.containerComponent, children) : children; return UserProps.withSafeUserProps(component, props); } } export const VictoryBoxPlot = addEvents(VictoryBoxPlotBase, options); ================================================ FILE: packages/victory-box-plot/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-box-plot/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-brush-container/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-brush-container/CHANGELOG.md ================================================ # victory-brush-container ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Refactor containers and portal to function components ([#2799](https://github.com/FormidableLabs/victory/pull/2799)) * Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Improve types for brush container and candlestick ([#2794](https://github.com/FormidableLabs/victory/pull/2794)) * Replace lodash array utils with native code ([#2810](https://github.com/FormidableLabs/victory/pull/2810)) - Replace lodash values and mapValues with native code ([#2808](https://github.com/FormidableLabs/victory/pull/2808)) * Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ### Patch Changes - Fix victory-native component prop types ([#2785](https://github.com/FormidableLabs/victory/pull/2785)) ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Improved the exported types (fixes [#2451](https://github.com/FormidableLabs/victory/issues/2451)) ([#2452](https://github.com/FormidableLabs/victory/pull/2452)) - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Migrate victory-brush-container and victory-brush-line to TypeScript ([#2393](https://github.com/FormidableLabs/victory/pull/2393)) * Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) * Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-brush-container/README.md ================================================ # VictoryBrushContainer `victory-brush-container@^30.0.0` exports `VictoryBrushContainer`, `brushContainerMixin` and `BrushHelpers` To view documentation for `VictoryBrushContainer` please see https://commerce.nearform.com/open-source/victory/docs/victory-brush-container To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-brush-container.md ================================================ FILE: packages/victory-brush-container/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-brush-container/package.json ================================================ { "name": "victory-brush-container", "version": "37.3.6", "description": "Interactive Brush Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-brush-container/src/brush-helpers.test.tsx ================================================ import { BrushHelpers } from "./brush-helpers"; describe("containers/brush-helpers", () => { const { withinBounds, constrainBox } = BrushHelpers; describe("withinBounds", () => { it("returns true when within bounds", () => { const point = { x: 1, y: 1 }; const bounds = { x1: 0, x2: 2, y1: 0, y2: 2 }; const isWithinBoundsResults = withinBounds(point, bounds); expect(isWithinBoundsResults).toBeTruthy(); }); it("returns false when not within bounds", () => { const point = { x: 10, y: 1 }; const bounds = { x1: 0, x2: 2, y1: 0, y2: 2 }; const isWithinBoundsResults = withinBounds(point, bounds); expect(isWithinBoundsResults).toBeFalsy(); }); it("returns true when within bounds using dates", () => { const point = { x: new Date("1/2/2022"), y: 1 }; const bounds = { x1: new Date("1/1/2022"), x2: new Date("2/1/2022"), y1: 0, y2: 2, }; const isWithinBoundsResults = withinBounds(point, bounds); expect(isWithinBoundsResults).toBeTruthy(); }); it("returns false when not within bounds using dates", () => { const point = { x: new Date("3/2/2022"), y: 1 }; const bounds = { x1: new Date("1/1/2022"), x2: new Date("2/1/2022"), y1: 0, y2: 2, }; const isWithinBoundsResults = withinBounds(point, bounds); expect(isWithinBoundsResults).toBeFalsy(); }); }); describe("constrainBox", () => { it("returns the correct box", () => { const fullDomainBox = { x1: 0, x2: 2, y1: 0, y2: 2 }; const box = { x1: 1, x2: 2, y1: 1, y2: 2 }; const constrainBoxResult = constrainBox(box, fullDomainBox); expect(constrainBoxResult).toEqual({ x1: 1, y1: 1, x2: 2, y2: 2 }); }); it("returns the correct box when x axis consists of dates", () => { const fullDomainBox = { x1: new Date("1/2/2022 PST"), x2: new Date("2/1/2022 PST"), y1: 0, y2: 2, }; const box = { x1: new Date("1/1/2022 PST"), x2: new Date("1/10/2022 PST"), y1: 1, y2: 2, }; const constrainBoxResult = constrainBox(box, fullDomainBox); expect(constrainBoxResult).toEqual({ x1: 1641110400000, y1: 1, x2: 1641888000000, y2: 2, }); }); }); }); ================================================ FILE: packages/victory-brush-container/src/brush-helpers.ts ================================================ import defaults from "lodash/defaults"; import throttle from "lodash/throttle"; import { Helpers as CoreHelpers, Selection } from "victory-core"; import isEqual from "react-fast-compare"; const Helpers = { getDimension(props) { const { horizontal, brushDimension } = props; if (!horizontal || !brushDimension) { return brushDimension; } return brushDimension === "x" ? "y" : "x"; }, withinBounds(point, bounds, padding?) { const { x1, x2, y1, y2 } = CoreHelpers.mapValues(bounds, Number); const { x, y } = CoreHelpers.mapValues(point, Number); const paddingValue = padding ? padding / 2 : 0; return ( x + paddingValue >= Math.min(x1, x2) && x - paddingValue <= Math.max(x1, x2) && y + paddingValue >= Math.min(y1, y2) && y - paddingValue <= Math.max(y1, y2) ); }, getDomainBox(props, fullDomain, selectedDomain?) { const brushDimension = this.getDimension(props); const fullDomainObject = defaults({}, fullDomain, props.domain); const selectedDomainObject = defaults({}, selectedDomain, fullDomainObject); const fullCoords = Selection.getDomainCoordinates(props, fullDomainObject); const selectedCoords = Selection.getDomainCoordinates( props, selectedDomainObject, ); return { x1: brushDimension !== "y" ? Math.min(...selectedCoords.x) : Math.min(...fullCoords.x), x2: brushDimension !== "y" ? Math.max(...selectedCoords.x) : Math.max(...fullCoords.x), y1: brushDimension !== "x" ? Math.min(...selectedCoords.y) : Math.min(...fullCoords.y), y2: brushDimension !== "x" ? Math.max(...selectedCoords.y) : Math.max(...fullCoords.y), }; }, getHandles(props, domainBox) { const brushDimension = this.getDimension(props); const { x1, x2, y1, y2 } = domainBox; const minX = Math.min(x1, x2); const maxX = Math.max(x1, x2); const minY = Math.min(y1, y2); const maxY = Math.max(y1, y2); const handleWidth = props.handleWidth / 2; return { left: brushDimension !== "y" && { x1: minX - handleWidth, x2: minX + handleWidth, y1, y2, }, right: brushDimension !== "y" && { x1: maxX - handleWidth, x2: maxX + handleWidth, y1, y2, }, top: brushDimension !== "x" && { x1, x2, y1: minY - handleWidth, y2: minY + handleWidth, }, bottom: brushDimension !== "x" && { x1, x2, y1: maxY - handleWidth, y2: maxY + handleWidth, }, }; }, getActiveHandles(point, props, domainBox) { const handles = this.getHandles(props, domainBox); const activeHandles = ["top", "bottom", "left", "right"].reduce( (memo, opt) => handles[opt] && this.withinBounds(point, handles[opt]) ? memo.concat(opt) : memo, [] as string[], ); return activeHandles.length && activeHandles; }, getResizeMutation(box, handles) { const { x1, y1, x2, y2 } = box; const mutations = { left: { x1: Math.max(x1, x2), x2: Math.min(x1, x2), y1, y2 }, right: { x1: Math.min(x1, x2), x2: Math.max(x1, x2), y1, y2 }, top: { y1: Math.max(y1, y2), y2: Math.min(y1, y2), x1, x2 }, bottom: { y1: Math.min(y1, y2), y2: Math.max(y1, y2), x1, x2 }, }; return handles.reduce((memo, current) => { return Object.assign(memo, mutations[current]); }, {}); }, getMinimumDomain() { return { x: [0, 1 / Number.MAX_SAFE_INTEGER], y: [0, 1 / Number.MAX_SAFE_INTEGER], }; }, getDefaultBrushArea(targetProps, cachedDomain, evt) { const { domain, fullDomain, scale, horizontal, allowResize } = targetProps; const defaultBrushArea = !allowResize && !targetProps.defaultBrushArea ? "move" : targetProps.defaultBrushArea; if (defaultBrushArea === "none") { return this.getMinimumDomain(); } else if (defaultBrushArea === "disable") { return cachedDomain; } else if (defaultBrushArea === "move") { const brushBox = this.getDomainBox(targetProps, fullDomain, cachedDomain); const parentSVG = targetProps.parentSVG || Selection.getParentSVG(evt); const pannedBox = this.panBox( { ...targetProps, ...brushBox, brushDomain: cachedDomain, startX: (brushBox.x1 + brushBox.x2) / 2, startY: (brushBox.y1 + brushBox.y2) / 2, }, Selection.getSVGEventCoordinates(evt, parentSVG), ); const fullDomainBox = targetProps.fullDomainBox || this.getDomainBox(targetProps, fullDomain); const constrainedBox = this.constrainBox(pannedBox, fullDomainBox); return Selection.getBounds({ ...constrainedBox, scale, horizontal }); } return domain; }, getSelectionMutation(point, box, brushDimension) { const { x, y } = point; const { x1, x2, y1, y2 } = box; return { x1: brushDimension !== "y" ? x : x1, y1: brushDimension !== "x" ? y : y1, x2: brushDimension !== "y" ? x : x2, y2: brushDimension !== "x" ? y : y2, }; }, panBox(props, point) { const { domain, startX, startY } = props; const brushDimension = this.getDimension(props); const brushDomain = defaults({}, props.brushDomain, domain); const fullDomain = defaults({}, props.fullDomain, domain); const { x1, x2, y1, y2 } = props.x1 ? props : this.getDomainBox(props, fullDomain, brushDomain); const { x, y } = point; const delta = { x: startX ? startX - x : 0, y: startY ? startY - y : 0, }; return { x1: brushDimension !== "y" ? Math.min(x1, x2) - delta.x : Math.min(x1, x2), x2: brushDimension !== "y" ? Math.max(x1, x2) - delta.x : Math.max(x1, x2), y1: brushDimension !== "x" ? Math.min(y1, y2) - delta.y : Math.min(y1, y2), y2: brushDimension !== "x" ? Math.max(y1, y2) - delta.y : Math.max(y1, y2), }; }, constrainBox(box, fullDomainBox) { const { x1, y1, x2, y2 } = CoreHelpers.mapValues(fullDomainBox, Number); return { x1: box.x2 > x2 ? x2 - Math.abs(box.x2 - box.x1) : Math.max(box.x1, x1), y1: box.y2 > y2 ? y2 - Math.abs(box.y2 - box.y1) : Math.max(box.y1, y1), x2: box.x1 < x1 ? x1 + Math.abs(box.x2 - box.x1) : Math.min(box.x2, x2), y2: box.y1 < y1 ? y1 + Math.abs(box.y2 - box.y1) : Math.min(box.y2, y2), }; }, constrainPoint(point, fullDomainBox) { const { x1, y1, x2, y2 } = CoreHelpers.mapValues(fullDomainBox, Number); return { x: Math.min(Math.max(point.x, x1), x2), y: Math.min(Math.max(point.y, y1), y2), }; }, hasMoved(props) { const { x1, x2, y1, y2, mouseMoveThreshold } = props; const brushDimension = this.getDimension(props); const xMoved = Math.abs(x1 - x2) >= mouseMoveThreshold; const yMoved = Math.abs(y1 - y2) >= mouseMoveThreshold; switch (brushDimension) { case "x": return xMoved; case "y": return yMoved; default: return xMoved || yMoved; } }, onMouseDown(evt, targetProps) { evt.preventDefault(); const { handleWidth, cachedBrushDomain, domain, allowResize, allowDrag, allowDraw, } = targetProps; const brushDimension = this.getDimension(targetProps); const defaultBrushArea = !allowResize && !targetProps.defaultBrushArea ? "move" : targetProps.defaultBrushArea; // Don't trigger events for static brushes if (!allowResize && !allowDrag) { return {}; } const fullDomainBox = targetProps.fullDomainBox || this.getDomainBox(targetProps, domain); const parentSVG = targetProps.parentSVG || Selection.getParentSVG(evt); const { x, y } = Selection.getSVGEventCoordinates(evt, parentSVG); // Ignore events that occur outside of the maximum domain region if (!this.withinBounds({ x, y }, fullDomainBox, handleWidth)) { return {}; } const brushDomain = defaults({}, targetProps.brushDomain, domain); const currentDomain = isEqual(brushDomain, cachedBrushDomain) ? targetProps.currentDomain || brushDomain || domain : brushDomain || domain; const domainBox = this.getDomainBox(targetProps, domain, currentDomain); const activeHandles = allowResize && this.getActiveHandles({ x, y }, targetProps, domainBox); // If the event occurs in any of the handle regions, start a resize if (activeHandles) { return [ { target: "parent", mutation: () => { return { isSelecting: true, domainBox, fullDomainBox, cachedBrushDomain: brushDomain, currentDomain, parentSVG, ...this.getResizeMutation(domainBox, activeHandles), }; }, }, ]; } else if ( this.withinBounds({ x, y }, domainBox) && !isEqual(domain, currentDomain) ) { // if the event occurs within a selected region start a panning event, unless the whole // domain is selected return [ { target: "parent", mutation: () => ({ isPanning: allowDrag, startX: x, startY: y, domainBox, fullDomainBox, currentDomain, cachedBrushDomain: brushDomain, parentSVG, ...domainBox, // set x1, x2, y1, y2 }), }, ]; } // if the event occurs outside the region, or if the whole domain is selected, // start a new selection return allowDraw ? [ { target: "parent", mutation: () => ({ isSelecting: allowResize || defaultBrushArea === "move", domainBox, fullDomainBox, parentSVG, cachedBrushDomain: brushDomain, cachedCurrentDomain: currentDomain, currentDomain: this.getMinimumDomain(), ...this.getSelectionMutation({ x, y }, domainBox, brushDimension), }), }, ] : {}; }, onGlobalMouseMove(evt, targetProps) { const { scale, isPanning, isSelecting, fullDomainBox, onBrushDomainChange, allowResize, allowDrag, horizontal, mouseMoveThreshold, parentSVG, } = targetProps; const brushDimension = this.getDimension(targetProps); const { x, y } = Selection.getSVGEventCoordinates(evt, parentSVG); if ( (!allowResize && !allowDrag) || (mouseMoveThreshold > 0 && !this.hasMoved({ ...targetProps, x2: x, y2: y })) ) { return {}; } if (allowDrag && isPanning) { const { startX, startY } = targetProps; const pannedBox = this.panBox(targetProps, { x, y }); const constrainedBox = this.constrainBox(pannedBox, fullDomainBox); const currentDomain = Selection.getBounds({ ...constrainedBox, scale, horizontal, }); const mutatedProps = { currentDomain, parentSVG, startX: pannedBox.x2 >= fullDomainBox.x2 || pannedBox.x1 <= fullDomainBox.x1 ? startX : x, startY: pannedBox.y2 >= fullDomainBox.y2 || pannedBox.y1 <= fullDomainBox.y1 ? startY : y, ...constrainedBox, }; if (CoreHelpers.isFunction(onBrushDomainChange)) { onBrushDomainChange( currentDomain, defaults({}, mutatedProps, targetProps), ); } return [ { target: "parent", mutation: () => mutatedProps, }, ]; } else if (allowResize && isSelecting) { const { x: x2, y: y2 } = this.constrainPoint( { x: brushDimension !== "y" ? x : targetProps.x2, y: brushDimension !== "x" ? y : targetProps.y2, }, fullDomainBox, ); const currentDomain = Selection.getBounds({ x2, y2, x1: targetProps.x1, y1: targetProps.y1, scale, horizontal, }); const mutatedProps = { x2, y2, currentDomain, parentSVG }; if (CoreHelpers.isFunction(onBrushDomainChange)) { onBrushDomainChange( currentDomain, defaults({}, mutatedProps, targetProps), ); } return [ { target: "parent", mutation: () => mutatedProps, }, ]; } return {}; }, onGlobalMouseUp(evt, targetProps) { // if a panning or selection has not been started, ignore the event if (!targetProps.isPanning && !targetProps.isSelecting) { return {}; } const { x1, y1, x2, y2, isPanning, isSelecting, onBrushDomainChange, onBrushDomainChangeEnd, onBrushCleared, currentDomain, allowResize, allowDrag, } = targetProps; const defaultBrushArea = !allowResize && !targetProps.defaultBrushArea ? "move" : targetProps.defaultBrushArea; const defaultBrushHasArea = defaultBrushArea !== undefined && defaultBrushArea !== "none"; const mutatedProps: { isPanning: boolean; isSelecting: boolean; currentDomain?: any; } = { isPanning: false, isSelecting: false }; // if the mouse hasn't moved since a mouseDown event, select the default brush area if ((allowResize || defaultBrushHasArea) && (x1 === x2 || y1 === y2)) { const cachedDomain = targetProps.cachedCurrentDomain || currentDomain; const defaultDomain = this.getDefaultBrushArea( targetProps, cachedDomain, evt, ); mutatedProps.currentDomain = defaultDomain; if (CoreHelpers.isFunction(onBrushDomainChange)) { onBrushDomainChange( defaultDomain, defaults({}, mutatedProps, targetProps), ); } if (CoreHelpers.isFunction(onBrushDomainChangeEnd)) { onBrushDomainChangeEnd( defaultDomain, defaults({}, mutatedProps, targetProps), ); } if (CoreHelpers.isFunction(onBrushCleared)) { onBrushCleared(defaultDomain, defaults({}, mutatedProps, targetProps)); } } else if ((allowDrag && isPanning) || (allowResize && isSelecting)) { if (CoreHelpers.isFunction(onBrushDomainChangeEnd)) { onBrushDomainChangeEnd( currentDomain, defaults({}, mutatedProps, targetProps), ); } } return [ { target: "parent", mutation: () => mutatedProps, }, ]; }, }; export const BrushHelpers = { ...Helpers, onMouseDown: Helpers.onMouseDown.bind(Helpers), onGlobalMouseUp: Helpers.onGlobalMouseUp.bind(Helpers), onGlobalMouseMove: throttle( Helpers.onGlobalMouseMove.bind(Helpers), 16, // eslint-disable-line no-magic-numbers { leading: true, trailing: false }, ), }; ================================================ FILE: packages/victory-brush-container/src/index.ts ================================================ export * from "./victory-brush-container"; export * from "./brush-helpers"; ================================================ FILE: packages/victory-brush-container/src/victory-brush-container.tsx ================================================ import React from "react"; import { Selection, Rect, DomainTuple, VictoryContainerProps, VictoryContainer, VictoryEventHandler, } from "victory-core"; import { BrushHelpers } from "./brush-helpers"; import defaults from "lodash/defaults"; import isEqual from "react-fast-compare"; export interface VictoryBrushContainerProps extends VictoryContainerProps { allowDrag?: boolean; allowDraw?: boolean; allowResize?: boolean; brushComponent?: React.ReactElement; brushDimension?: "x" | "y"; brushDomain?: { x?: DomainTuple; y?: DomainTuple }; brushStyle?: React.CSSProperties; defaultBrushArea?: "all" | "none" | "disable" | "move"; disable?: boolean; handleComponent?: React.ReactElement; handleStyle?: React.CSSProperties; handleWidth?: number; onBrushCleared?: ( domain: { x: DomainTuple; y: DomainTuple }, props: VictoryBrushContainerProps, ) => void; onBrushDomainChange?: ( domain: { x: DomainTuple; y: DomainTuple }, props: VictoryBrushContainerProps, ) => void; onBrushDomainChangeEnd?: ( domain: { x: DomainTuple; y: DomainTuple }, props: VictoryBrushContainerProps, ) => void; } interface VictoryBrushContainerMutatedProps extends VictoryBrushContainerProps { domain: { x: DomainTuple; y: DomainTuple }; currentDomain: { x: DomainTuple; y: DomainTuple } | undefined; cachedBrushDomain: { x: DomainTuple; y: DomainTuple } | undefined; } export const VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS = { allowDrag: true, allowDraw: true, allowResize: true, brushComponent: , brushStyle: { stroke: "transparent", fill: "black", fillOpacity: 0.1, }, handleComponent: , handleStyle: { stroke: "transparent", fill: "transparent", }, handleWidth: 8, mouseMoveThreshold: 0, }; export const useVictoryBrushContainer = ( initialProps: VictoryBrushContainerProps, ) => { const props = { ...VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS, ...(initialProps as VictoryBrushContainerMutatedProps), }; const { children } = props; const getSelectBox = (coordinates) => { const { x, y } = coordinates; const { brushStyle, brushComponent, name } = props; const brushComponentStyle = brushComponent.props && brushComponent.props.style; const cursor = !props.allowDrag && !props.allowResize ? "auto" : "move"; return x[0] !== x[1] && y[0] !== y[1] ? React.cloneElement(brushComponent, { key: `${name}-brush`, width: Math.abs(x[1] - x[0]) || 1, height: Math.abs(y[1] - y[0]) || 1, x: Math.min(x[0], x[1]), y: Math.min(y[0], y[1]), cursor, style: defaults({}, brushComponentStyle, brushStyle), }) : null; }; const getCursorPointers = () => { const cursors = { yProps: "ns-resize", xProps: "ew-resize", }; if (!props.allowResize && props.allowDrag) { cursors.xProps = "move"; cursors.yProps = "move"; } else if (!props.allowResize && !props.allowDrag) { cursors.xProps = "auto"; cursors.yProps = "auto"; } return cursors; }; const getHandles = (domain) => { const { handleWidth, handleStyle, handleComponent, name } = props; const domainBox = BrushHelpers.getDomainBox(props, domain); const { x1, x2, y1, y2 } = domainBox; const { top, bottom, left, right } = BrushHelpers.getHandles( props, domainBox, ); const width = Math.abs(x2 - x1) || 1; const height = Math.abs(y2 - y1) || 1; const handleComponentStyle = (handleComponent.props && handleComponent.props.style) || {}; const style = defaults({}, handleComponentStyle, handleStyle); const cursors = getCursorPointers(); const yProps = { style, width, height: handleWidth, cursor: cursors.yProps, }; const xProps = { style, width: handleWidth, height, cursor: cursors.xProps, }; const handleProps = { top: top && Object.assign({ x: top.x1, y: top.y1 }, yProps), bottom: bottom && Object.assign({ x: bottom.x1, y: bottom.y1 }, yProps), left: left && Object.assign({ y: left.y1, x: left.x1 }, xProps), right: right && Object.assign({ y: right.y1, x: right.x1 }, xProps), }; const handles = ["top", "bottom", "left", "right"].reduce( (memo, curr) => handleProps[curr] ? memo.concat( React.cloneElement( handleComponent, Object.assign( { key: `${name}-handle-${curr}` }, handleProps[curr], ), ), ) : memo, [] as React.ReactElement[], ); return handles.length ? handles : null; }; const getRect = () => { const { currentDomain, cachedBrushDomain } = props; const brushDomain = defaults({}, props.brushDomain, props.domain); const domain = isEqual(brushDomain, cachedBrushDomain) ? defaults({}, currentDomain, brushDomain) : brushDomain; const coordinates = Selection.getDomainCoordinates(props, domain); const selectBox = getSelectBox(coordinates); return selectBox ? [selectBox, getHandles(domain)] : []; }; return { props, children: [ ...React.Children.toArray(children), ...getRect(), ] as React.ReactElement[], }; }; export const VictoryBrushContainer = ( initialProps: VictoryBrushContainerProps, ) => { const { props, children } = useVictoryBrushContainer(initialProps); return {children}; }; VictoryBrushContainer.role = "container"; VictoryBrushContainer.defaultEvents = ( initialProps: VictoryBrushContainerProps, ) => { const props = { ...VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS, ...initialProps }; const createEventHandler = ( handler: VictoryEventHandler, isDisabled?: (targetProps: any) => boolean, ): VictoryEventHandler => // eslint-disable-next-line max-params (event, targetProps, eventKey, context) => props.disable || isDisabled?.(targetProps) ? {} : handler(event, { ...props, ...targetProps }, eventKey, context); return [ { target: "parent", eventHandlers: { onMouseDown: createEventHandler(BrushHelpers.onMouseDown), onTouchStart: createEventHandler(BrushHelpers.onMouseDown), onGlobalMouseMove: createEventHandler( BrushHelpers.onGlobalMouseMove, (targetProps) => !targetProps.isPanning && !targetProps.isSelecting, ), onGlobalTouchMove: createEventHandler( BrushHelpers.onGlobalMouseMove, (targetProps) => !targetProps.isPanning && !targetProps.isSelecting, ), onGlobalMouseUp: createEventHandler(BrushHelpers.onGlobalMouseUp), onGlobalTouchEnd: createEventHandler(BrushHelpers.onGlobalMouseUp), onGlobalTouchCancel: createEventHandler(BrushHelpers.onGlobalMouseUp), }, }, ]; }; ================================================ FILE: packages/victory-brush-container/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-brush-container/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-brush-line/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-brush-line/CHANGELOG.md ================================================ # victory-brush-line ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ### Patch Changes - Migrate victory-native to TypeScript ([#2739](https://github.com/FormidableLabs/victory/pull/2739)) ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Migrate victory-brush-container and victory-brush-line to TypeScript ([#2393](https://github.com/FormidableLabs/victory/pull/2393)) * Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) * Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-brush-line/README.md ================================================ # VictoryBrushLine `victory-brush-line@^30.0.0` exports `VictoryBrushLine` To view documentation for `VictoryBrushLine` please see https://commerce.nearform.com/open-source/victory/docs/victory-brush-line To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-brush-line.md ================================================ FILE: packages/victory-brush-line/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-brush-line/package.json ================================================ { "name": "victory-brush-line", "version": "37.3.6", "description": "Interactive Brush Line Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-brush-line/src/index.ts ================================================ export * from "./victory-brush-line"; ================================================ FILE: packages/victory-brush-line/src/victory-brush-line.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import pick from "lodash/pick"; import { Selection, Helpers, Collection, LineSegment, Scale, Domain, Box, DomainTuple, VictoryStyleObject, } from "victory-core"; import isEqual from "react-fast-compare"; export type VictoryBrushLineTargetType = "data" | "labels" | "parent"; export interface VictoryBrushLineProps { allowDrag?: boolean; allowDraw?: boolean; allowResize?: boolean; brushAreaComponent?: React.ReactElement; brushAreaStyle?: VictoryStyleObject; brushAreaWidth?: number; brushComponent?: React.ReactElement; brushDomain?: DomainTuple; brushStyle?: VictoryStyleObject; brushWidth?: number; className?: string; dimension?: "x" | "y"; events?: React.DOMAttributes; disable?: boolean; groupComponent?: React.ReactElement; handleComponent?: React.ReactElement; handleStyle?: VictoryStyleObject; handleWidth?: number; id?: string | number; lineComponent?: React.ReactElement; name?: string; onBrushDomainChange?: ( domain: DomainTuple, props?: VictoryBrushLineProps, ) => void; style?: VictoryStyleObject; type?: string; width?: number; } const SMALL_NUMBER = 1 / Number.MAX_SAFE_INTEGER; const getScale = (props) => { const { scale = {}, dimension = "x" } = props; if (scale[dimension]) { return scale[dimension]; } const fallbackScale = Scale.getBaseScale(props, dimension); const range = Helpers.getRange(props, dimension); const domain = Domain.getDomainFromProps(props, dimension) || [0, 1]; fallbackScale.range(range).domain(domain); return fallbackScale; }; const getDimension = (props) => { const { horizontal, dimension = "x" } = props; if (!horizontal) { return dimension; } return dimension === "x" ? "y" : "x"; }; const toRange = (props, domain) => { const scale = getScale(props); return [scale(Math.min(...domain)), scale(Math.max(...domain))]; }; const toDomain = (props, range) => { const scale = getScale(props); return [scale.invert(Math.min(...range)), scale.invert(Math.max(...range))]; }; const getFullRange = (props) => { const scale = getScale(props); return scale.range(); }; const getFullDomain = (props) => { const scale = getScale(props); return scale.domain(); }; const withinBound = (value, bound) => { return ( value >= Collection.getMinValue(bound) && value <= Collection.getMaxValue(bound) ); }; const getBrushDomain = (brushDomain, fullDomain) => { if (brushDomain) { const brushMin = Collection.getMinValue(brushDomain); const brushMax = Collection.getMaxValue(brushDomain); const domainMin = Collection.getMinValue(fullDomain); const domainMax = Collection.getMaxValue(fullDomain); const defaultMin = brushMin < domainMin ? domainMin : Number(domainMax) - SMALL_NUMBER; const defaultMax = brushMax > domainMax ? domainMax : Number(domainMin) + SMALL_NUMBER; const min = withinBound(brushMin, fullDomain) ? brushMin : defaultMin; const max = withinBound(brushMax, fullDomain) ? brushMax : defaultMax; return [min, max]; } return fullDomain; }; type ReduceReturnType = { min?: string; max?: string; }; const getActiveHandle = (props, position, range) => { const width = props.handleWidth / 2; const dimension = getDimension(props); const getHandle = (type) => { const base = { min: dimension === "x" ? Math.min(...range) : Math.max(...range), max: dimension === "x" ? Math.max(...range) : Math.min(...range), }; return [base[type] - width, base[type] + width]; }; const active = ["min", "max"].reduce((memo, type) => { memo[type] = withinBound(position, getHandle(type)) ? type : undefined; return memo; }, {} as ReduceReturnType); return active.min && active.max ? "both" : active.min || active.max; }; const getMinimumDomain = () => { return [0, SMALL_NUMBER]; }; const panBox = (props, position) => { const { brushDomain, startPosition } = props; const range = toRange(props, brushDomain); const fullRange = getFullRange(props); const size = Math.abs(range[1] - range[0]); const globalMin = Math.min(...fullRange); const globalMax = Math.max(...fullRange); const delta = startPosition ? startPosition - position : 0; const min = Math.min(...range) - delta; const max = Math.max(...range) - delta; const constrainedMin = min > globalMax - size ? globalMax - size : Math.max(min, globalMin); const constrainedMax = max < globalMin + size ? globalMin + size : Math.min(max, globalMax); return [constrainedMin, constrainedMax]; }; const fallbackProps = { brushAreaStyle: { stroke: "none", fill: "black", opacity: ({ active }) => (active ? 0.2 : 0.1), // eslint-disable-line no-magic-numbers }, brushStyle: { pointerEvents: "none", stroke: "none", fill: "black", opacity: ({ active }) => (active ? 0.4 : 0.3), // eslint-disable-line no-magic-numbers }, handleStyle: { pointerEvents: "none", stroke: "none", fill: "none", }, }; export class VictoryBrushLine< T extends VictoryBrushLineProps, > extends React.Component { static defaultProps = { allowDrag: true, allowDraw: true, allowResize: true, brushAreaComponent: , brushComponent: , groupComponent: , handleComponent: , handleWidth: 10, lineComponent: , width: 10, }; static defaultEvents = function (props) { return props.disable ? undefined : [ { target: props.type, eventHandlers: { onMouseEnter: (evt, targetProps) => { evt.preventDefault(); const { allowResize, brushDomain } = targetProps; const dimension = getDimension(targetProps); const parentSVG = targetProps.parentSVG || Selection.getParentSVG(evt); const position = Selection.getSVGEventCoordinates( evt, parentSVG, )[dimension]; const fullDomain = getFullDomain(targetProps); const currentDomain = getBrushDomain(brushDomain, fullDomain); const range = toRange(targetProps, currentDomain); const activeHandle = allowResize && getActiveHandle(targetProps, position, range); const activeBrushes = { brushArea: !targetProps.brushDomain, brush: withinBound(position, range) && !isEqual(fullDomain, currentDomain), minHandle: activeHandle === "min" || activeHandle === "both", maxHandle: activeHandle === "min" || activeHandle === "both", }; return [ { mutation: () => ({ activeBrushes, brushDomain: targetProps.brushDomain, parentSVG, }), }, ]; }, onMouseDown: (evt, targetProps) => { evt.preventDefault(); const { allowResize, allowDrag, allowDraw, activeBrushes, brushDomain, } = targetProps; const dimension = getDimension(targetProps); // Don't trigger events for static brushes if (!allowResize && !allowDrag) { return []; } const fullDomain = getFullDomain(targetProps); const currentDomain = getBrushDomain(brushDomain, fullDomain); const parentSVG = targetProps.parentSVG || Selection.getParentSVG(evt); const position = Selection.getSVGEventCoordinates( evt, parentSVG, )[dimension]; const range = toRange(targetProps, currentDomain); const activeHandle = allowResize && getActiveHandle(targetProps, position, range); // If the event occurs in any of the handle regions, start a resize if (activeHandle) { return [ { mutation: () => { return { parentSVG, isSelecting: true, activeHandle, brushDomain: currentDomain, startPosition: position, activeBrushes, }; }, }, ]; } else if ( withinBound(position, range) && !isEqual(fullDomain, currentDomain) ) { // if the event occurs within a selected region start a panning event, unless the whole // domain is selected return [ { mutation: () => ({ isPanning: allowDrag, startPosition: position, brushDomain: currentDomain, activeBrushes, parentSVG, }), }, ]; } // if the event occurs outside the region, or if the whole domain is selected, // start a new selection return allowDraw ? [ { mutation: () => ({ isSelecting: allowResize, brushDomain: null, startPosition: position, activeBrushes, parentSVG, }), }, ] : []; }, onMouseMove: (evt, targetProps) => { const { isPanning, isSelecting, allowResize, allowDrag, onBrushDomainChange, brushDomain, } = targetProps; const dimension = getDimension(targetProps); if (isPanning || isSelecting) { evt.preventDefault(); evt.stopPropagation(); } const parentSVG = targetProps.parentSVG || Selection.getParentSVG(evt); const position = Selection.getSVGEventCoordinates( evt, parentSVG, )[dimension]; const fullDomain = getFullDomain(targetProps); const domain = getBrushDomain(brushDomain, fullDomain); const initialRange = toRange(targetProps, domain); const activeHandle = getActiveHandle( targetProps, position, initialRange, ); const activeBrushes = { brushArea: !targetProps.brushDomain, brush: withinBound(position, initialRange) && !isEqual(fullDomain, domain), minHandle: activeHandle === "min" || activeHandle === "both", maxHandle: activeHandle === "max" || activeHandle === "both", }; if (!targetProps.isPanning && !targetProps.isSelecting) { return [ { mutation: () => ({ activeBrushes, brushDomain: targetProps.brushDomain, parentSVG, }), }, ]; } if (allowDrag && isPanning) { const fullRange = getFullRange(targetProps); const range = panBox(targetProps, position); const currentDomain = toDomain(targetProps, range); const startPosition = Math.max(...range) >= Math.max(...fullRange) || Math.min(...range) <= Math.min(...fullRange) ? targetProps.startPosition : position; const mutatedProps = { startPosition, isPanning: true, brushDomain: currentDomain, activeBrushes: { brush: true }, parentSVG, }; if (Helpers.isFunction(onBrushDomainChange)) { onBrushDomainChange( currentDomain, defaults({}, mutatedProps, targetProps), ); } return [ { mutation: () => mutatedProps, }, ]; } else if (allowResize && isSelecting) { let currentDomain = brushDomain || getMinimumDomain(); const range = toRange(targetProps, currentDomain); const oppositeHandle = targetProps.activeHandle === "min" ? "max" : "min"; const handle = targetProps.activeHandle && getActiveHandle(targetProps, position, range) === "both" ? oppositeHandle : targetProps.activeHandle; if (!handle) { currentDomain = toDomain(targetProps, [ targetProps.startPosition, position, ]); } else { const rangeMax = dimension === "x" ? Math.max(...range) : Math.min(...range); const rangeMin = dimension === "x" ? Math.min(...range) : Math.max(...range); const min = handle === "max" ? rangeMin : position; const max = handle === "min" ? rangeMax : position; currentDomain = toDomain(targetProps, [min, max]); } const mutatedProps = { brushDomain: currentDomain, startPosition: targetProps.startPosition, isSelecting, activeHandle: handle, parentSVG, activeBrushes: { brush: true, minHandle: activeHandle === "min", maxHandle: activeHandle === "max", }, }; if (Helpers.isFunction(onBrushDomainChange)) { onBrushDomainChange( currentDomain, defaults({}, mutatedProps, targetProps), ); } return [ { mutation: () => mutatedProps, }, ]; } return []; }, onMouseUp: (evt, targetProps) => { const { onBrushDomainChange, brushDomain, allowResize, activeBrushes, } = targetProps; // if the mouse hasn't moved since a mouseDown event, select the whole domain region const mutatedProps = { isPanning: false, isSelecting: false, activeHandle: null, startPosition: null, brushDomain, activeBrushes, }; if (allowResize && Helpers.isFunction(onBrushDomainChange)) { onBrushDomainChange( brushDomain, defaults({}, mutatedProps, targetProps), ); } return [ { mutation: () => mutatedProps, }, ]; }, onMouseLeave: (evt, targetProps) => { const { brushDomain } = targetProps; return [ { mutation: () => ({ isPanning: false, isSelecting: false, activeHandle: null, startPosition: null, brushDomain, activeBrushes: {}, }), }, ]; }, }, }, ]; }; getRectDimensions(props, brushWidth, domain?) { const { brushDomain } = props; const dimension = getDimension(props); const range = toRange( props, domain || getBrushDomain(brushDomain, getFullDomain(props)), ); const coordinates = dimension === "x" ? { y1: props.y1, y2: props.y2, x1: Math.min(...range), x2: Math.max(...range), } : { x1: props.x1, x2: props.x2, y1: Math.min(...range), y2: Math.max(...range), }; const { x1, x2, y1, y2 } = coordinates; const offset = { x: dimension === "x" ? 0 : brushWidth / 2, y: dimension === "y" ? 0 : brushWidth / 2, }; const x = Math.min(x1, x2) - offset.x; const y = Math.min(y1, y2) - offset.y; const width = Math.max(x1, x2) + offset.x - x; const height = Math.max(y1, y2) + offset.y - y; return { x, y, width, height }; } getHandleDimensions(props) { const { handleWidth, x1, x2, y1, y2, brushDomain } = props; const dimension = getDimension(props); const brushWidth = props.brushWidth || props.width; const domain = getBrushDomain(brushDomain, getFullDomain(props)); const range = toRange(props, domain); const defaultX = Math.min(x1, x2) - brushWidth / 2; const defaultY = Math.min(y1, y2) - brushWidth / 2; const x = { min: dimension === "x" ? Math.min(...range) - handleWidth / 2 : defaultX, max: dimension === "x" ? Math.max(...range) - handleWidth / 2 : defaultX, }; const y = { min: dimension === "y" ? Math.max(...range) - handleWidth / 2 : defaultY, max: dimension === "y" ? Math.min(...range) - handleWidth / 2 : defaultY, }; const width = dimension === "x" ? handleWidth : brushWidth; const height = dimension === "x" ? brushWidth : handleWidth; return { min: { x: x.min, y: y.min, width, height }, max: { x: x.max, y: y.max, width, height }, }; } getCursor(props) { const { activeBrushes = {} } = props; const dimension = getDimension(props); if (activeBrushes.minHandle || activeBrushes.maxHandle) { return dimension === "x" ? "ew-resize" : "ns-resize"; } else if (activeBrushes.brush) { return "move"; } return "crosshair"; } renderHandles(props) { const { handleComponent, handleStyle, id, brushDomain, datum = {}, activeBrushes = {}, } = props; if (!brushDomain) { return null; } const handleDimensions = this.getHandleDimensions(props); const style = Object.assign({}, fallbackProps.handleStyle, handleStyle); const minDatum = Object.assign( { handleValue: Collection.getMinValue(brushDomain) }, datum, ); const maxDatum = Object.assign( { handleValue: Collection.getMaxValue(brushDomain) }, datum, ); const minHandleProps = Object.assign( { key: `${id}-min`, style: Helpers.evaluateStyle(style, { datum: minDatum, active: activeBrushes.minHandle, }), }, handleDimensions.min, ); const maxHandleProps = Object.assign( { key: `${id}-max`, style: Helpers.evaluateStyle(style, { datum: maxDatum, active: activeBrushes.maxHandle, }), }, handleDimensions.max, ); return [ React.cloneElement(handleComponent, minHandleProps), React.cloneElement(handleComponent, maxHandleProps), ]; } renderBrush(props) { const { brushComponent, brushStyle, activeBrushes = {}, datum = {}, brushDomain, } = props; if (!brushDomain) { return null; } const brushWidth = props.brushWidth || props.width; const rectDimensions = this.getRectDimensions(props, brushWidth); const baseStyle = Object.assign({}, fallbackProps.brushStyle, brushStyle); const style = Helpers.evaluateStyle(baseStyle, { datum, active: activeBrushes.brush, }); const brushProps = Object.assign({ style }, rectDimensions); return React.cloneElement(brushComponent, brushProps); } renderBrushArea(props) { const { brushAreaComponent, brushAreaStyle, activeBrushes = {}, datum = {}, } = props; const brushAreaWidth = props.brushAreaWidth || props.width; const cursor = this.getCursor(props); const rectDimensions = this.getRectDimensions( props, brushAreaWidth, getFullDomain(props), ); const baseStyle = Object.assign( { cursor }, fallbackProps.brushAreaStyle, brushAreaStyle, ); const style = Helpers.evaluateStyle(baseStyle, { datum, active: activeBrushes.brushArea, }); const brushAreaProps = Object.assign({ style }, rectDimensions); return React.cloneElement(brushAreaComponent, brushAreaProps); } renderLine(props) { const filteredProps = pick(props, [ "x1", "x2", "y1", "y2", "datum", "scale", "active", "style", ]); return React.cloneElement(props.lineComponent, filteredProps); } render() { return ( {this.renderLine(this.props)} {this.renderBrushArea(this.props)} {this.renderBrush(this.props)} {this.renderHandles(this.props)} ); } } ================================================ FILE: packages/victory-brush-line/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-brush-line/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-candlestick/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-candlestick/CHANGELOG.md ================================================ # victory-candlestick ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ### Patch Changes - Remove duplicate types from interfaces ([#2940](https://github.com/FormidableLabs/victory/pull/2940)) * Prevents boolean passed to candlestick labels prop from rendering ([#2931](https://github.com/FormidableLabs/victory/pull/2931)) ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Improve types for brush container and candlestick ([#2794](https://github.com/FormidableLabs/victory/pull/2794)) * Replace lodash isNil and isNan with native code ([#2800](https://github.com/FormidableLabs/victory/pull/2800)) - Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) * Replace instances of lodash.range with equivalent native code ([#2760](https://github.com/FormidableLabs/victory/pull/2760)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ### Patch Changes - Migrate victory-candlestick to TypeScript ([#2716](https://github.com/FormidableLabs/victory/pull/2716)) ## 36.8.1 ## 36.8.0 ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-candlestick/README.md ================================================ # VictoryCandlestick `victory-candlestick@^30.0.0` exports `VictoryCandlestick` and `Candle` components To view documentation for `VictoryCandlestick` please see https://commerce.nearform.com/open-source/victory/docs/victory-candlestick To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-candlestick.md ================================================ FILE: packages/victory-candlestick/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-candlestick/package.json ================================================ { "name": "victory-candlestick", "version": "37.3.6", "description": "Candlestick Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "devDependencies": { "victory-vendor": "*", "victory-chart": "*" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-chart:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-candlestick/src/candle.test.tsx ================================================ import React from "react"; import { render } from "@testing-library/react"; import { VictoryContainer } from "victory-core"; import * as d3Scale from "victory-vendor/d3-scale"; import { Candle } from "./candle"; describe("victory-primitives/candle", () => { const baseProps = { data: [ { x: 1, open: 10, close: 30, high: 50, low: 5, eventKey: 0 }, { x: 2, open: 40, close: 80, high: 100, low: 10, eventKey: 1 }, ], datum: { x: 1, open: 10, close: 30, high: 50, low: 5, eventKey: 0 }, scale: { x: d3Scale.scaleLinear(), y: d3Scale.scaleLinear(), }, candleWidth: 2, x: 5, high: 50, low: 5, close: 30, open: 10, }; const renderCandle = (props = {}) => render( , ); it("should render a wick line", () => { const { container } = renderCandle(); const wicks = container.querySelectorAll("line"); const values = [ { x1: 5, x2: 5, y1: 50, y2: 10, }, { x1: 5, x2: 5, y1: 30, y2: 5, }, ]; wicks.forEach((wick, i) => { const [x1, x2, y1, y2] = ["x1", "x2", "y1", "y2"].map((prop) => parseInt(wick.getAttribute(prop) || ""), ); expect(values[i]).toMatchObject({ x1, x2, y1, y2 }); }); }); it("should render a candle rectangle", () => { const { container } = renderCandle(); const rect = container.querySelector("rect"); const [width, height, x, y] = ["width", "height", "x", "y"].map((prop) => rect?.getAttribute(prop), ); // width = style.width || 0.5 * (width - 2 * padding) / data.length; expect(width).toEqual("2"); expect(height).toEqual("20"); // x = x - width / 2 expect(x).toEqual("4"); expect(y).toEqual("10"); }); }); ================================================ FILE: packages/victory-candlestick/src/candle.tsx ================================================ import React from "react"; import { Helpers, Line, NumberOrCallback, Rect, VictoryCommonPrimitiveProps, VictoryStyleObject, } from "victory-core"; import defaults from "lodash/defaults"; export interface CandleProps extends VictoryCommonPrimitiveProps { candleRatio?: number; candleWidth?: NumberOrCallback; close?: number; datum?: any; groupComponent?: React.ReactElement; high?: number; lineComponent?: React.ReactElement; low?: number; open?: number; rectComponent?: React.ReactElement; wickStrokeWidth?: number; width?: number; x?: number; } const getCandleWidth = ( candleWidth: CandleProps["candleWidth"], props: CandleProps, ) => { const { style } = props; if (candleWidth) { return Helpers.isFunction(candleWidth) ? Helpers.evaluateProp(candleWidth, props) : candleWidth; } else if (style.width) { return style.width; } return candleWidth; }; const getCandleProps = (props, style: VictoryStyleObject) => { const { id, x, close, open, horizontal, candleWidth } = props; const candleLength = Math.abs(close - open); return { key: `${id}-candle`, style: Helpers.omit(style, ["width"]), x: horizontal ? Math.min(open, close) : x - candleWidth / 2, y: horizontal ? x - candleWidth / 2 : Math.min(open, close), width: horizontal ? candleLength : candleWidth, height: horizontal ? candleWidth : candleLength, }; }; const getHighWickProps = (props, style: VictoryStyleObject) => { const { horizontal, high, open, close, x, id } = props; return { key: `${id}-highWick`, style: Helpers.omit(style, ["width"]), x1: horizontal ? high : x, x2: horizontal ? Math.max(open, close) : x, y1: horizontal ? x : high, y2: horizontal ? x : Math.min(open, close), }; }; const getLowWickProps = (props, style: VictoryStyleObject) => { const { horizontal, low, open, close, x, id } = props; return { key: `${id}-lowWick`, style: Helpers.omit(style, ["width"]), x1: horizontal ? Math.min(open, close) : x, x2: horizontal ? low : x, y1: horizontal ? x : Math.max(open, close), y2: horizontal ? x : low, }; }; const evaluateProps = (props) => { /** * Potential evaluated props of following must be evaluated in this order: * 1) `style` * 2) `cornerRadius` * * Everything else does not have to be evaluated in a particular order: * `ariaLabel` * `desc` * `id` * `tabIndex` */ const style = Helpers.evaluateStyle( Object.assign({ stroke: "black" }, props.style), props, ); const candleWidth = getCandleWidth( props.candleWidth, Object.assign({}, props, { style }), ); const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const desc = Helpers.evaluateProp(props.desc, props); const id = Helpers.evaluateProp(props.id, props); const tabIndex = Helpers.evaluateProp(props.tabIndex, props); return Object.assign({}, props, { ariaLabel, style, candleWidth, desc, id, tabIndex, }); }; const defaultProps: Partial = { groupComponent: , lineComponent: , rectComponent: , role: "presentation", shapeRendering: "auto", }; export const Candle = (props: CandleProps) => { const modifiedProps = evaluateProps(defaults({}, props, defaultProps)); const { ariaLabel, events, groupComponent, clipPath, rectComponent, lineComponent, role, shapeRendering, className, wickStrokeWidth, transform, style, desc, tabIndex, } = modifiedProps; const wickStyle = defaults({ strokeWidth: wickStrokeWidth }, style); const sharedProps = { ...events, "aria-label": ariaLabel, role, shapeRendering, className, transform, clipPath, desc, tabIndex, }; const candleProps = Object.assign( getCandleProps(modifiedProps, style), sharedProps, ); const highWickProps = Object.assign( getHighWickProps(modifiedProps, wickStyle), sharedProps, ); const lowWickProps = Object.assign( getLowWickProps(modifiedProps, wickStyle), sharedProps, ); return React.cloneElement(groupComponent, {}, [ React.cloneElement(rectComponent, candleProps), React.cloneElement(lineComponent, highWickProps), React.cloneElement(lineComponent, lowWickProps), ]); }; ================================================ FILE: packages/victory-candlestick/src/helper-methods.test.ts ================================================ import { fromJS } from "immutable"; import { Helpers } from "victory-core"; import { getData, getDomain } from "./helper-methods"; const immutableGetDataTest = { createData: (x) => fromJS(x), testLabel: "with immutable data", }; const getDataTest = { createData: (x) => x, testLabel: "with js data", }; [getDataTest, immutableGetDataTest].forEach(({ createData, testLabel }) => { describe(`victory-candlestick/helper-methods ${testLabel}`, () => { describe("getData", () => { it("sorts data by sortKey", () => { const data = createData( Helpers.range(5) .map((i) => ({ x: i, open: i, close: i, high: i, low: i })) .reverse(), ); const dataResult = getData({ data, x: "x", open: "open", close: "close", high: "high", low: "low", sortKey: "x", }); expect(dataResult.map((datum) => datum.x)).toEqual([0, 1, 2, 3, 4]); }); }); describe("getDomain", () => { const dataSet = createData([ { x: 5, open: 10, close: 20, high: 25, low: 5 }, { x: 10, open: 15, close: 25, high: 30, low: 10 }, ]); it("returns a domain array for the x axis", () => { const domainXResult = getDomain( { data: dataSet, x: "x", open: "open", close: "close", high: "high", low: "low", }, "x", ); expect(domainXResult).toEqual([5, 10]); }); it("returns a domain array for the y axis", () => { const domainYResult = getDomain( { data: dataSet, open: "open", close: "close", high: "high", low: "low", }, "y", ); expect(domainYResult).toEqual([5, 30]); }); }); }); }); ================================================ FILE: packages/victory-candlestick/src/helper-methods.ts ================================================ import defaults from "lodash/defaults"; import isPlainObject from "lodash/isPlainObject"; import { Helpers, Scale, Domain, Data, LabelHelpers, Collection, VictoryStyleObject, } from "victory-core"; const TYPES = ["close", "open", "high", "low"]; const DEFAULT_CANDLE_WIDTH = 8; export const getData = (props) => { const accessorTypes = ["x", "high", "low", "close", "open"]; return Data.formatData(props.data, props, accessorTypes); }; const reduceData = (dataset, axis, type) => { const yDataTypes = { min: "_low", max: "_high" }; const dataType = axis === "x" ? "_x" : yDataTypes[type]; const baseCondition = type === "min" ? Infinity : -Infinity; return dataset.reduce((memo, datum) => { const current = datum[dataType]; return (memo < current && type === "min") || (memo > current && type === "max") ? memo : current; }, baseCondition); }; const getDomainFromData = (props, axis) => { const minDomain = Domain.getMinFromProps(props, axis); const maxDomain = Domain.getMaxFromProps(props, axis); const dataset = getData(props); if (dataset.length < 1) { return minDomain !== undefined && maxDomain !== undefined ? Domain.getDomainFromMinMax(minDomain, maxDomain) : undefined; } const min = minDomain !== undefined ? minDomain : reduceData(dataset, axis, "min"); const max = maxDomain !== undefined ? maxDomain : reduceData(dataset, axis, "max"); return Domain.getDomainFromMinMax(min, max); }; export const getDomain = (props, axis) => { return Domain.createDomainFunction(getDomainFromData)(props, axis); }; const getLabelStyle = (props, styleObject, namespace) => { const component = props[`${namespace}LabelComponent`]; const baseStyle = styleObject[`${namespace}Labels`] || styleObject.labels; if (!Helpers.isTooltip(component)) { return baseStyle; } const tooltipTheme = (props.theme && props.theme.tooltip) || {}; return defaults({}, tooltipTheme.style, baseStyle); }; const getStyles = ( props, style, defaultStyles: { parent?: any; labels?: any; data?: any; } = {}, ) => { if (props.disableInlineStyles) { return {}; } const width = "100%"; const height = "100%"; if (!style) { return defaults( { parent: { height, width, }, }, defaultStyles, ); } const defaultParent = defaultStyles.parent || {}; const defaultLabels = defaultStyles.labels || {}; const defaultData = defaultStyles.data || {}; const labelStyle = defaults({}, style.labels, defaultLabels); return { parent: defaults({}, style.parent, defaultParent, { width, height, }), labels: labelStyle, data: defaults({}, style.data, defaultData), openLabels: defaults( {}, style.openLabels, getLabelStyle(props, defaultStyles, "open"), labelStyle, ), closeLabels: defaults( {}, style.closeLabels, getLabelStyle(props, defaultStyles, "close"), labelStyle, ), lowLabels: defaults( {}, style.lowLabels, getLabelStyle(props, defaultStyles, "low"), labelStyle, ), highLabels: defaults( {}, style.highLabels, getLabelStyle(props, defaultStyles, "high"), labelStyle, ), }; }; // This method will edit or remove candlestick data points that fall outside of the desired domain const formatDataFromDomain = (datum, domain) => { const minDomainX = Collection.getMinValue(domain.x); const maxDomainX = Collection.getMaxValue(domain.x); const minDomainY = Collection.getMinValue(domain.y); const maxDomainY = Collection.getMaxValue(domain.y); let { _x, _low, _open, _close, _high } = datum; // if _x falls outside of min or max if (_x < minDomainX || _x > maxDomainX) _x = null; // if all values fall outside of domain, null the data point if ( _low < minDomainY && _open < minDomainY && _close < minDomainY && _high < minDomainY ) _low = _open = _close = _high = null; if ( _low > maxDomainY && _open > maxDomainY && _close > maxDomainY && _high > maxDomainY ) _low = _open = _close = _high = null; return Object.assign({}, datum, { _x, _low, _open, _close, _high }); }; const getCalculatedValues = (props) => { const { polar } = props; const defaultStyle = Helpers.getDefaultStyles(props, "candlestick"); const style = getStyles(props, props.style, defaultStyle); const data = getData(props); const range = { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; const domain = { x: getDomain(props, "x"), y: getDomain(props, "y"), }; const scale = { x: Scale.getBaseScale(props, "x") .domain(domain.x) .range(props.horizontal ? range.y : range.x), y: Scale.getBaseScale(props, "y") .domain(domain.y) .range(props.horizontal ? range.x : range.y), }; const origin = polar ? props.origin || Helpers.getPolarOrigin(props) : undefined; const defaultOrientation = props.horizontal ? "top" : "right"; const labelOrientation = props.labelOrientation || defaultOrientation; return { domain, data, scale, style, origin, labelOrientation }; }; const isTransparent = (attr) => { return attr === "none" || attr === "transparent"; }; const getDataStyles = ( datum, style: { fill?: string; stroke?: string } = {}, props, ) => { if (props.disableInlineStyles) { return {}; } const candleColor = datum._open > datum._close ? props.candleColors.negative : props.candleColors.positive; const fill = style.fill || candleColor; const strokeColor = style.stroke; const stroke = isTransparent(strokeColor) ? fill : strokeColor || "black"; return Object.assign({}, style, { stroke, fill }); }; const getText = (props, type) => { const { datum, index, labels } = props; const propName = `${type}Labels`; const labelProp = props[propName]; if (!labelProp && !labels) { return null; } else if (labelProp === true || labels === true) { const dataName = `_${type}`; return `${datum[dataName]}`; } return Array.isArray(labelProp) ? labelProp[index] : labelProp; }; const getCandleWidth = (props, style?: VictoryStyleObject) => { const { data, candleWidth, scale } = props; if (candleWidth) { return Helpers.isFunction(candleWidth) ? Helpers.evaluateProp(candleWidth, props) : candleWidth; } else if (style && style.width) { return style.width; } const range = scale.x.range(); const extent = Math.abs(range[1] - range[0]); const candles = data.length + 2; const candleRatio = props.candleRatio || 0.5; const defaultWidth = candleRatio * (data.length < 2 ? DEFAULT_CANDLE_WIDTH : extent / candles); return Math.max(1, defaultWidth); }; const getOrientation = (labelOrientation, type = "labels") => { return isPlainObject(labelOrientation) ? labelOrientation[type] : labelOrientation; }; const calculatePlotValues = (props) => { const { positions, labelStyle, x, horizontal, computedType, candleWidth, orientation, } = props; positions.labels = (positions.open + positions.close) / 2; const signX = orientation === "left" ? -1 : 1; const signY = orientation === "top" ? -1 : 1; if (horizontal) { const yValue = x; const xValue = positions[computedType]; const dy = orientation === "top" || orientation === "bottom" ? signY * (candleWidth / 2) + signY * (labelStyle?.padding || 0) : 0; const dx = orientation === "top" || orientation === "bottom" ? 0 : signX * (labelStyle?.padding || 1); return { yValue, xValue, dx, dy }; } const xValue = x; const yValue = positions[computedType]; const dy = orientation === "top" || orientation === "bottom" ? signY * (labelStyle?.padding || 1) : 0; const dx = orientation === "top" || orientation === "bottom" ? 0 : signX * (candleWidth / 2) + signX * (labelStyle?.padding || 0); return { yValue, xValue, dx, dy }; }; /* eslint-disable max-params*/ const getLabelProps = (props, text, style, type?: string) => { const { x, high, low, open, close, index, scale, datum, data, horizontal, candleWidth, labelOrientation, theme, } = props; const component = props[`${type}LabelComponent`] || props.labelComponent; const defaultOrientation = horizontal ? "top" : "right"; const orientation = (component.props && component.props.orientation) || getOrientation(labelOrientation, type) || defaultOrientation; const positions = { high, low, open, close }; const namespace = type ? `${type}Labels` : "labels"; const labelStyle = style[namespace] || style.labels; const defaultVerticalAnchors = { top: "end", bottom: "start", left: "middle", right: "middle", }; const defaultTextAnchors = { left: "end", right: "start", top: "middle", bottom: "middle", }; const computedType = type ? type : "labels"; const plotProps = { positions, labelStyle, x, horizontal, computedType, candleWidth, orientation, }; const { yValue, xValue, dx, dy } = calculatePlotValues(plotProps); const labelProps = { style: labelStyle, y: yValue, x: xValue, dx, dy, text, index, scale, datum, data, orientation, textAnchor: labelStyle?.textAnchor || defaultTextAnchors[orientation], verticalAnchor: labelStyle?.verticalAnchor || defaultVerticalAnchors[orientation], angle: labelStyle?.angle, horizontal, }; if (!Helpers.isTooltip(component)) { return labelProps; } const tooltipTheme = (theme && theme.tooltip) || {}; return defaults({}, labelProps, Helpers.omit(tooltipTheme, ["style"])); }; /* eslint-enable max-params*/ export const getBaseProps = (initialProps, fallbackProps) => { const props = Helpers.modifyProps(initialProps, fallbackProps, "candlestick"); const calculatedValues = getCalculatedValues(props); const { data, style, scale, domain, origin, labelOrientation } = calculatedValues; const { groupComponent, width, height, padding, standalone, name, candleWidth, candleRatio, theme, polar, wickStrokeWidth, labels, events, sharedEvents, horizontal, disableInlineStyles, } = props; const initialChildProps = { parent: { domain, scale, width, height, data, standalone, theme, polar, origin, name, style: style.parent, padding, horizontal, }, }; return data.reduce((childProps, datum, index) => { const eventKey = !Helpers.isNil(datum.eventKey) ? datum.eventKey : index; const x = scale.x(datum._x1 !== undefined ? datum._x1 : datum._x); const formattedDatum = formatDataFromDomain(datum, domain); const { _low, _open, _close, _high } = formattedDatum; const high = scale.y(_high); const close = scale.y(_close); const open = scale.y(_open); const low = scale.y(_low); const dataStyle = getDataStyles(formattedDatum, style.data, props); const dataProps = { x, high, low, candleWidth, candleRatio, scale, data, datum: formattedDatum, groupComponent, index, style: dataStyle, width, polar, origin, wickStrokeWidth, open, close, horizontal, labelOrientation, disableInlineStyles, }; dataProps.candleWidth = getCandleWidth(dataProps); const extendedProps = defaults(Object.assign({}, dataProps), props); childProps[eventKey] = { data: dataProps, }; if (labels) { const text = LabelHelpers.getText(props, formattedDatum, index); if ( (text !== undefined && text !== null) || (labels && (events || sharedEvents)) ) { childProps[eventKey].labels = getLabelProps(extendedProps, text, style); } } TYPES.forEach((type) => { const labelText = getText(extendedProps, type); const labelProp = props.labels || props[`${type}Labels`]; if ( (labelText !== null && labelText !== undefined) || (labelProp && (events || sharedEvents)) ) { const target = `${type}Labels`; childProps[eventKey][target] = getLabelProps( extendedProps, labelText, style, type, ); } }); return childProps; }, initialChildProps); }; ================================================ FILE: packages/victory-candlestick/src/index.ts ================================================ export * from "./victory-candlestick"; export * from "./candle"; ================================================ FILE: packages/victory-candlestick/src/victory-candlestick.test.tsx ================================================ import { fireEvent, render, screen } from "@testing-library/react"; import React from "react"; import { VictoryChart } from "victory-chart"; import { Helpers } from "victory-core"; import { Candle } from "./candle"; import { VictoryCandlestick } from "./victory-candlestick"; const MyCandle = () =>
; const dataSet = [ { x: 5, open: 10, close: 20, high: 25, low: 5 }, { x: 1, open: 80, close: 40, high: 120, low: 10, label: "1" }, ]; describe("components/victory-candlestick", () => { describe("default component rendering", () => { it("attaches safe user props to the container component", () => { render( , ); const container = screen.getByTestId("victory-candlestick"); expect(screen.getByLabelText("Chart")).toBeDefined(); expect(container).not.toHaveAttribute("unsafe-prop"); expect(container.nodeName).toEqual("svg"); }); it("attaches safe user props to the group component if the component is rendered inside a VictoryChart", () => { render( , { wrapper: VictoryChart }, ); const container = screen.getByTestId("victory-candlestick"); expect(screen.getByLabelText("Chart")).toBeDefined(); expect(container).not.toHaveAttribute("unsafe-prop"); expect(container.nodeName).toEqual("g"); }); it("renders an svg with the correct width and height", () => { const { container } = render(); const svg = container.querySelector("svg"); expect(svg?.getAttribute("style")).toContain("width: 100%; height: 100%"); }); it("renders an svg with the correct viewBox", () => { const { container } = render(); const svg = container.querySelector("svg"); const viewBoxValue = `0 0 ${450} ${300}`; expect(svg?.getAttribute("viewBox")).toEqual(viewBoxValue); }); it("renders 8 points", () => { const { container } = render(); const points = container.querySelectorAll("rect"); expect(points).toHaveLength(8); }); }); describe("rendering data", () => { it("renders injected points for {x, y} shaped data (default)", () => { const data = Helpers.range(5).map((i) => ({ x: i, open: i, close: i, high: i, low: i, })); render(} />); const points = screen.getAllByTestId("my-candle"); expect(points).toHaveLength(5); }); it("renders points for {x, y} shaped data (default)", () => { const data = Helpers.range(5).map((i) => ({ x: i, open: i, close: i, high: i, low: i, })); const { container } = render(); const points = container.querySelectorAll("rect"); expect(points).toHaveLength(5); }); it("renders ordered bars when sortKey is passed", () => { const data = Helpers.range(5) .map((i) => ({ x: i, open: i, close: i, high: i, low: i })) .reverse(); const { container } = render( , ); const candles = container.querySelectorAll("rect"); const xValues = Array.from(candles).map((bar) => Number(bar.getAttribute("x")), ); const xValuesAscending = [...xValues].sort((a, b) => a - b); expect(xValues).toEqual(xValuesAscending); }); it("renders reverse ordered bars when sortOrder is descending", () => { const data = Helpers.range(5) .map((i) => ({ x: i, open: i, close: i, high: i, low: i })) .reverse(); const { container } = render( , ); const candles = container.querySelectorAll("rect"); const xValues = Array.from(candles).map((bar) => Number(bar.getAttribute("x")), ); const xValuesDescending = [...xValues].sort((a, b) => b - a); expect(xValues).toEqual(xValuesDescending); }); it("renders points for array-shaped data", () => { const data = Helpers.range(10).map((i) => [i, i, i, i, i]); const { container } = render( , ); const points = container.querySelectorAll("rect"); expect(points).toHaveLength(10); }); it("renders points for deeply-nested data", () => { const data = Helpers.range(20).map((i) => ({ a: { b: [{ x: i, open: i, close: i, high: i, low: i }] }, })); const { container } = render( , ); const points = container.querySelectorAll("rect"); expect(points).toHaveLength(20); }); it("renders data values with null accessor", () => { const data = Helpers.range(10); const { container } = render( // @ts-expect-error "'null' is not assignable to 'x'" , ); const points = container.querySelectorAll("rect"); expect(points).toHaveLength(10); }); it("does not render data with null x, open, close, high, or low values", () => { const data = [ { x: 1, open: 10, close: 17, high: 19, low: 8 }, { x: null, open: 17, close: 17, high: 17, low: 17 }, { x: 2, open: null, close: 17, high: 17, low: 17 }, { x: 3, open: 17, close: null, high: 17, low: 17 }, { x: 4, open: 17, close: 17, high: null, low: 17 }, { x: 5, open: 17, close: 17, high: 17, low: null }, ]; const { container } = render(); const points = container.querySelectorAll("rect"); expect(points).toHaveLength(1); }); it("does not render a label when it receives true as the lablels prop", () => { const data = [{ x: 1, open: 10, close: 17, high: 19, low: 8 }]; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const { container } = render(); const trueLabel = Array.from( container.querySelectorAll("text[id^=candlestick-labels] > tspan"), ).find((t) => t.textContent === "true"); expect(trueLabel).toBe(undefined); }); }); describe("event handling", () => { const clickHandler = jest.fn(); beforeEach(() => { clickHandler.mockReset(); }); it("attaches an event to data", () => { const { container } = render( , ); const data = container.querySelectorAll("rect"); data.forEach((node, index) => { clickHandler.mockReset(); fireEvent.click(node); const { key } = clickHandler.mock.calls[0][1]; expect(key).toEqual(`candlestick-data-${index}`); }); }); }); describe("accessibility", () => { it("adds an aria role to each point in the series", () => { const data = [ { x: 0, open: 9, close: 30, high: 56, low: 7 }, { x: 1, open: 80, close: 40, high: 120, low: 10 }, { x: 2, open: 50, close: 80, high: 90, low: 20 }, ]; render(); const presentationElements = screen.getAllByRole("presentation"); // Each data point is 4 (container, rect and 2 lines) for 12 total // plus the chart container element expect(presentationElements).toHaveLength(13); }); it("adds an aria-label and tabIndex to Candle primitive", () => { const data = [ { x: new Date(2016, 6, 1), open: 20, close: 43, high: 66, low: 7 }, { x: new Date(2016, 6, 2), open: 80, close: 40, high: 120, low: 10 }, { x: new Date(2016, 6, 3), open: 50, close: 80, high: 90, low: 20 }, ]; const { container } = render( `open ${datum.open}, close ${datum.close}` } tabIndex={({ index }) => Number(index) + 5} /> } />, ); container.querySelectorAll("rect").forEach((node, index) => { const expectedLabel = `open ${data[index].open}, close ${data[index].close}`; expect(node.getAttribute("aria-label")).toEqual(expectedLabel); expect(node.getAttribute("tabindex")).toEqual(`${index + 5}`); }); }); }); }); ================================================ FILE: packages/victory-candlestick/src/victory-candlestick.tsx ================================================ import React from "react"; import { Helpers, VictoryLabel, addEvents, VictoryContainer, VictoryTheme, DefaultTransitions, UserProps, StringOrNumberOrCallback, EventPropTypeInterface, OrientationTypes, VictoryCommonProps, VictoryDatableProps, VictoryLabelStyleObject, VictoryMultiLabelableProps, VictoryStyleObject, NumberOrCallback, EventsMixinClass, } from "victory-core"; import { Candle } from "./candle"; import { getDomain, getData, getBaseProps } from "./helper-methods"; export interface VictoryCandlestickStyleInterface { close?: VictoryStyleObject; closeLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; data?: VictoryStyleObject; high?: VictoryStyleObject; highLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; low?: VictoryStyleObject; lowLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; open?: VictoryStyleObject; openLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; parent?: VictoryStyleObject; } export type VictoryCandlestickLabelsType = | (string | number)[] | boolean | ((datum: any) => number | string); export interface VictoryCandlestickProps extends Omit, VictoryDatableProps, VictoryMultiLabelableProps { candleColors?: { positive?: string; negative?: string; }; candleRatio?: number; candleWidth?: NumberOrCallback; close?: StringOrNumberOrCallback | string[]; closeLabelComponent?: React.ReactElement; closeLabels?: VictoryCandlestickLabelsType; eventKey?: StringOrNumberOrCallback | string[]; events?: EventPropTypeInterface< | "data" | "labels" | "open" | "openLabels" | "close" | "closeLabels" | "low" | "lowLabels" | "high" | "highLabels", StringOrNumberOrCallback | string[] >[]; high?: StringOrNumberOrCallback | string[]; highLabelComponent?: React.ReactElement; highLabels?: VictoryCandlestickLabelsType; labelOrientation?: | OrientationTypes | { open?: OrientationTypes; close?: OrientationTypes; low?: OrientationTypes; high?: OrientationTypes; }; low?: StringOrNumberOrCallback | string[]; lowLabelComponent?: React.ReactElement; lowLabels?: VictoryCandlestickLabelsType; open?: StringOrNumberOrCallback | string[]; openLabelComponent?: React.ReactElement; openLabels?: VictoryCandlestickLabelsType; size?: number; style?: VictoryCandlestickStyleInterface; wickStrokeWidth?: number; } /* eslint-disable no-magic-numbers */ const fallbackProps = { width: 450, height: 300, padding: 50, candleColors: { positive: "#ffffff", negative: "#252525", }, }; const options = { components: [ { name: "lowLabels" }, { name: "highLabels" }, { name: "openLabels" }, { name: "closeLabels" }, { name: "labels" }, { name: "data" }, { name: "parent", index: "parent" }, ], }; const defaultData = [ { x: new Date(2016, 6, 1), open: 5, close: 10, high: 15, low: 0 }, { x: new Date(2016, 6, 2), open: 10, close: 15, high: 20, low: 5 }, { x: new Date(2016, 6, 3), open: 15, close: 20, high: 25, low: 10 }, { x: new Date(2016, 6, 4), open: 20, close: 25, high: 30, low: 15 }, { x: new Date(2016, 6, 5), open: 25, close: 30, high: 35, low: 20 }, { x: new Date(2016, 6, 6), open: 30, close: 35, high: 40, low: 25 }, { x: new Date(2016, 6, 7), open: 35, close: 40, high: 45, low: 30 }, { x: new Date(2016, 6, 8), open: 40, close: 45, high: 50, low: 35 }, ]; /* eslint-enable no-magic-numbers */ const datumHasXandY = (datum) => { return !Helpers.isNil(datum._x) && !Helpers.isNil(datum._y); }; // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryCandlestickBase extends EventsMixinClass {} /** * VictoryCandlestick renders a dataset as a series of candlesticks. * VictoryCandlestick can be composed with VictoryChart to create candlestick charts. */ class VictoryCandlestickBase extends React.Component { static animationWhitelist = [ "data", "domain", "height", "padding", "samples", "size", "style", "width", ]; static displayName = "VictoryCandlestick"; static role = "candlestick"; static defaultTransitions = DefaultTransitions.discreteTransitions(); static defaultProps: VictoryCandlestickProps = { containerComponent: , data: defaultData, dataComponent: , groupComponent: , labelComponent: , highLabelComponent: , lowLabelComponent: , openLabelComponent: , closeLabelComponent: , samples: 50, sortOrder: "ascending", standalone: true, theme: VictoryTheme.grayscale, }; static getDomain(props, axis) { return getDomain(props, axis); } static getData(props) { return getData(props); } static getBaseProps(props: VictoryCandlestickProps) { return getBaseProps(props, fallbackProps); } static expectedComponents = [ "openLabelComponent", "closeLabelComponent", "highLabelComponent", "lowLabelComponent", "dataComponent", "labelComponent", "groupComponent", "containerComponent", ]; // Overridden in native versions shouldAnimate() { return !!this.props.animate; } shouldRenderDatum = (datum) => { return ( !Helpers.isNil(datum._x) && !Helpers.isNil(datum._high) && !Helpers.isNil(datum._low) && !Helpers.isNil(datum._close) && !Helpers.isNil(datum._open) ); }; renderCandleData( props: VictoryCandlestickProps, shouldRenderDatum = datumHasXandY, ) { const { dataComponent, labelComponent, groupComponent } = props; const types = ["close", "open", "low", "high"]; if (!groupComponent) { throw new Error("VictoryCandlestick expects a groupComponent prop"); } const children: React.ReactElement[] = []; if (dataComponent) { const dataComponents = this.dataKeys.reduce( (validDataComponents, _dataKey, index) => { const dataProps = this.getComponentProps( dataComponent, "data", index, ); if (shouldRenderDatum((dataProps as any).datum)) { validDataComponents.push( React.cloneElement(dataComponent, dataProps), ); } return validDataComponents; }, [], ); children.push(...dataComponents); } const labelComponents = types.flatMap((type) => this.dataKeys .map((key, index) => { const name = `${type}Labels`; const baseComponent: React.ReactElement = props[`${type}LabelComponent`]; const labelProps = this.getComponentProps(baseComponent, name, index); if ( (labelProps as any).text !== undefined && (labelProps as any).text !== null ) { return React.cloneElement(baseComponent, labelProps); } return undefined; }) .filter( (comp: React.ReactElement | undefined): comp is React.ReactElement => comp !== undefined, ), ); children.push(...labelComponents); if (labelComponent) { const labelsComponents = this.dataKeys .map((_dataKey, index) => { const labelProps = this.getComponentProps( labelComponent, "labels", index, ); if ( (labelProps as any).text !== undefined && (labelProps as any).text !== null && typeof (labelProps as any).text !== "boolean" ) { return React.cloneElement(labelComponent, labelProps); } return undefined; }) .filter( (comp: React.ReactElement | undefined): comp is React.ReactElement => comp !== undefined, ); children.push(...labelsComponents); } return this.renderContainer(groupComponent, children); } render(): React.ReactElement { const { animationWhitelist, role } = VictoryCandlestick; const props = Helpers.modifyProps(this.props, fallbackProps, role); if (this.shouldAnimate()) { return this.animateComponent(props, animationWhitelist); } const children = this.renderCandleData(props, this.shouldRenderDatum); const component = props.standalone ? this.renderContainer(props.containerComponent, children) : children; return UserProps.withSafeUserProps(component, props); } } export const VictoryCandlestick = addEvents(VictoryCandlestickBase, options); ================================================ FILE: packages/victory-candlestick/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-candlestick/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-canvas/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-canvas/CHANGELOG.md ================================================ # victory-canvas ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Fix incorrect typescript props ([#2745](https://github.com/FormidableLabs/victory/pull/2745)) ## 36.8.2 ### Patch Changes - Migrate victory-canvas to TypeScript ([#2710](https://github.com/FormidableLabs/victory/pull/2710)) ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-bar@36.6.8 - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-bar@36.6.7 - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-bar@36.6.6 - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 - victory-bar@36.6.5 ## 36.6.4 ### Patch Changes - Added explicit `any` type defs (fixes [#2358](https://github.com/FormidableLabs/victory/issues/2358)) ([`57ed0fe30`](https://github.com/FormidableLabs/victory/commit/57ed0fe304dbc8753da1126a02d44de8004e96aa)) * Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) * Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 - victory-bar@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-bar@36.6.3 - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-bar@36.6.2 - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 - victory-bar@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 - victory-bar@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-canvas/README.md ================================================ #VictoryCanvas This is an experimental set of primitive components that can be used in place of the default Victory primitives to render data to a Canvas container rather than SVG. The current version of this package includes components for line, bar, and scatter charts. Example usage: ```jsx } dataComponent={} data={data} /> ``` ```jsx } dataComponent={} data={data} /> ``` ```jsx } dataComponent={} data={data} /> ``` This package currently exports: - `CanvasGroup` - `CanvasCurve` - `CanvasPoint` - `CanvasBar` Please visit our documentation site to read more about these components https://commerce.nearform.com/open-source/victory To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs ================================================ FILE: packages/victory-canvas/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-canvas/package.json ================================================ { "name": "victory-canvas", "version": "37.3.6", "description": "HTML5 Canvas Components for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-bar": "37.3.6", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-bar:build:lib:esm", "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-bar:build:lib:cjs", "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-bar:build:lib:esm", "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-bar:build:lib:esm", "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-bar:types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-bar:types:create", "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-bar:types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-bar:types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-bar:build", "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-canvas/src/canvas-bar.tsx ================================================ import React from "react"; import { BarProps, VictoryBarAlignmentType, VictoryBarCornerRadiusObject, getBarPath, getBarWidth, getCornerRadius, getPolarBarPath, getStyle, } from "victory-bar"; import { useCanvasContext } from "./hooks/use-canvas-context"; import { NumberOrCallback, VictoryCommonPrimitiveProps } from "victory-core"; export interface CanvasBarProps extends VictoryCommonPrimitiveProps { alignment?: VictoryBarAlignmentType; barOffset?: number[]; barRatio?: number; barWidth?: NumberOrCallback; cornerRadius?: NumberOrCallback | VictoryBarCornerRadiusObject; datum?: any; getPath?: (props: CanvasBarProps) => string; horizontal?: boolean; width?: number; x?: number; y?: number; y0?: number; } const evaluateProps = (props: CanvasBarProps) => { /** * Potential evaluated props of following must be evaluated in this order: * 1) `style` * 2) `barWidth` * 3) `cornerRadius` */ const style = getStyle(props.style, props as BarProps); const barWidth = getBarWidth( props.barWidth, Object.assign({}, props, { style }), ); const cornerRadius = getCornerRadius( props.cornerRadius, Object.assign({}, props, { style, barWidth }), ); const modifiedProps = Object.assign({}, props, { style, barWidth, cornerRadius, }); return modifiedProps; }; const usePreviousValue = (value) => { const ref = React.useRef(); React.useEffect(() => { ref.current = value; }); return ref.current; }; export const CanvasBar = (props: CanvasBarProps) => { const { canvasRef } = useCanvasContext(); const modifiedProps = evaluateProps(props); const { polar, style, barWidth, cornerRadius, origin } = modifiedProps; const path2d = React.useMemo(() => { const p = polar ? getPolarBarPath(modifiedProps, cornerRadius) : getBarPath(modifiedProps, barWidth, cornerRadius); return new Path2D(p); }, [polar, barWidth, cornerRadius, modifiedProps]); const previousPath = usePreviousValue(path2d); const draw = React.useCallback( (ctx: CanvasRenderingContext2D, path: Path2D) => { ctx.fillStyle = style.fill; ctx.strokeStyle = style.stroke; ctx.globalAlpha = style.fillOpacity; ctx.lineWidth = style.strokeWidth; if (polar) { ctx.translate(origin?.x || 0, origin?.y || 0); } ctx.fill(path); ctx.setTransform(1, 0, 0, 1, 0, 0); }, [style, origin, polar], ); // This will clear the previous bar without clearing the entire canvas const clearPreviousPath = React.useCallback( (ctx: CanvasRenderingContext2D) => { if (previousPath) { ctx.save(); // This ensures that the entire shape is erased const strokeWidth = (style.strokeWidth as number) || 0; ctx.lineWidth = strokeWidth + 2; ctx.globalCompositeOperation = "destination-out"; draw(ctx, previousPath); ctx.stroke(previousPath); ctx.restore(); } }, [draw, previousPath, style], ); React.useEffect(() => { const ctx = canvasRef.current?.getContext("2d"); if (!ctx) return; clearPreviousPath(ctx); draw(ctx, path2d); }, [ canvasRef, draw, polar, barWidth, cornerRadius, modifiedProps, path2d, clearPreviousPath, ]); return null; }; ================================================ FILE: packages/victory-canvas/src/canvas-curve.tsx ================================================ import React from "react"; import { LineHelpers, NumberOrCallback, StringOrCallback, VictoryCommonPrimitiveProps, } from "victory-core"; import { useCanvasContext } from "./hooks/use-canvas-context"; import { LineRadial } from "../../victory-vendor/d3-shape"; export interface CanvasCurveProps extends VictoryCommonPrimitiveProps { ariaLabel?: StringOrCallback; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type interpolation?: string | Function; openCurve?: boolean; tabIndex?: NumberOrCallback; } export const CanvasCurve = (props: CanvasCurveProps) => { const { canvasRef, clear, clip } = useCanvasContext(); const { style, data } = props; const { stroke, strokeWidth } = style; const draw = React.useCallback( (ctx: CanvasRenderingContext2D) => { const line = LineHelpers.getLineFunction(props) as LineRadial< [number, number] >; ctx.strokeStyle = stroke; ctx.lineWidth = strokeWidth; line.context(ctx)(data); ctx.stroke(); }, [data, props, stroke, strokeWidth], ); React.useEffect(() => { const ctx = canvasRef.current?.getContext("2d"); if (!ctx) return; clear(ctx); draw(ctx); clip(ctx); }, [canvasRef, draw, clear, clip]); return null; }; ================================================ FILE: packages/victory-canvas/src/canvas-group.tsx ================================================ import React from "react"; import { CanvasContext } from "./hooks/use-canvas-context"; import { PaddingProps } from "victory-core"; export interface CanvasGroupProps { children?: React.ReactNode | React.ReactNode[]; clipWidth?: number; height?: number; padding?: PaddingProps; width?: number; } export const CanvasGroup = (props: CanvasGroupProps) => { const canvasRef = React.useRef(null); const { children, width = 0, height = 0, clipWidth, padding } = props; const clear = React.useCallback( (ctx: CanvasRenderingContext2D) => { return ctx.clearRect(0, 0, width, height); }, [width, height], ); // This needs to be called in the child component to ensure it is called after the // shape is drawn const clip = React.useCallback( (ctx: CanvasRenderingContext2D) => { const paddingRight = typeof padding === "number" ? padding : padding?.right || 0; const paddingLeft = typeof padding === "number" ? padding : padding?.left || 0; const maxClipWidth = width - paddingRight - paddingLeft; ctx.clearRect( width - paddingRight, 0, clipWidth ? (maxClipWidth - clipWidth) * -1 : 0, height, ); }, [width, height, padding, clipWidth], ); return ( {children} ); }; CanvasGroup.role = "container"; ================================================ FILE: packages/victory-canvas/src/canvas-point.tsx ================================================ import React from "react"; import { Helpers, PointPathHelpers, ScatterSymbolType, VictoryCommonPrimitiveProps, } from "victory-core"; import { useCanvasContext } from "./hooks/use-canvas-context"; export interface CanvasPointProps extends VictoryCommonPrimitiveProps { datum?: any; getPath?: (x: number, y: number, size: number) => string; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type size?: number | Function; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type symbol?: ScatterSymbolType | Function; x?: number; y?: number; } const getPath = (props) => { const { x, y, size, symbol } = props; if (props.getPath) { return props.getPath(x, y, size); } const pathFunctions = { circle: PointPathHelpers.circle, square: PointPathHelpers.square, diamond: PointPathHelpers.diamond, triangleDown: PointPathHelpers.triangleDown, triangleUp: PointPathHelpers.triangleUp, plus: PointPathHelpers.plus, minus: PointPathHelpers.minus, star: PointPathHelpers.star, cross: PointPathHelpers.cross, }; const symbolFunction = typeof pathFunctions[symbol] === "function" ? pathFunctions[symbol] : pathFunctions.circle; return symbolFunction(x, y, size); }; const evaluateProps = (props: CanvasPointProps) => { /** * Potential evaluated props are: * `size` * `style` * `symbol` */ const size = Helpers.evaluateProp(props.size, props); const style = Helpers.evaluateStyle(props.style, props); const symbol = Helpers.evaluateProp(props.symbol, props); return Object.assign({}, props, { size, style, symbol, }); }; export const CanvasPoint = (props: CanvasPointProps) => { const { canvasRef } = useCanvasContext(); const modifiedProps = evaluateProps(props); const draw = React.useCallback( (ctx: CanvasRenderingContext2D) => { const { style } = modifiedProps; const path = getPath(modifiedProps); ctx.fillStyle = style.fill; const path2d = new Path2D(path); ctx.fill(path2d); }, [modifiedProps], ); React.useEffect(() => { const ctx = canvasRef.current?.getContext("2d"); if (!ctx) return; draw(ctx); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return null; }; ================================================ FILE: packages/victory-canvas/src/hooks/use-canvas-context.ts ================================================ import React from "react"; export type CanvasContextValue = { canvasRef: React.RefObject; clear: (ctx: CanvasRenderingContext2D) => void; clip: (ctx: CanvasRenderingContext2D) => void; }; export const CanvasContext = React.createContext< CanvasContextValue | undefined >(undefined); export const useCanvasContext = () => { const context = React.useContext(CanvasContext); if (!context) { throw new Error( `This component must be wrapped in a CanvasContext.Provider component. Try setting groupComponent={} in your chart component.`, ); } return context; }; ================================================ FILE: packages/victory-canvas/src/index.ts ================================================ export * from "./canvas-bar"; export * from "./canvas-group"; export * from "./canvas-curve"; export * from "./canvas-point"; export { useCanvasContext } from "./hooks/use-canvas-context"; ================================================ FILE: packages/victory-canvas/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-canvas/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-chart/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-chart/CHANGELOG.md ================================================ # victory-chart ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) * Improve types in victory-core helpers ([#2999](https://github.com/FormidableLabs/victory/pull/2999)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ### Patch Changes - Assign merged props to a const instead of modifying initialProps ([#2718](https://github.com/FormidableLabs/victory/pull/2718)) ## 36.8.1 ## 36.8.0 ### Minor Changes - Remove v37 experimental code ([#2697](https://github.com/FormidableLabs/victory/pull/2697)) ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) * Fixed issue where VictoryChart would throw an unhandled exception when passed non-element children (fixes [#2391](https://github.com/FormidableLabs/victory/issues/2391)) ([#2536](https://github.com/FormidableLabs/victory/pull/2536)) ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-axis@36.6.8 - victory-core@36.6.8 - victory-polar-axis@36.6.8 - victory-shared-events@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-axis@36.6.7 - victory-core@36.6.7 - victory-polar-axis@36.6.7 - victory-shared-events@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-axis@36.6.6 - victory-core@36.6.6 - victory-polar-axis@36.6.6 - victory-shared-events@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 - victory-axis@36.6.5 - victory-polar-axis@36.6.5 - victory-shared-events@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 - victory-axis@36.6.4 - victory-polar-axis@36.6.4 - victory-shared-events@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-axis@36.6.3 - victory-core@36.6.3 - victory-polar-axis@36.6.3 - victory-shared-events@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-axis@36.6.2 - victory-core@36.6.2 - victory-polar-axis@36.6.2 - victory-shared-events@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 - victory-axis@36.6.1 - victory-polar-axis@36.6.1 - victory-shared-events@36.6.1 ## 36.6.0 ### Patch Changes - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 - victory-axis@36.6.0 - victory-polar-axis@36.6.0 - victory-shared-events@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-chart/README.md ================================================ # VictoryChart `victory-chart@^30.0.0` exports `VictoryChart` To view documentation for `VictoryChart` please see https://commerce.nearform.com/open-source/victory/docs/victory-chart To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-chart.md ================================================ FILE: packages/victory-chart/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-chart/package.json ================================================ { "name": "victory-chart", "version": "37.3.6", "description": "Chart Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", "victory-axis": "37.3.6", "victory-core": "37.3.6", "victory-polar-axis": "37.3.6", "victory-shared-events": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-axis:build:lib:esm", "../victory-core:build:lib:esm", "../victory-polar-axis:build:lib:esm", "../victory-shared-events:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-axis:build:lib:cjs", "../victory-core:build:lib:cjs", "../victory-polar-axis:build:lib:cjs", "../victory-shared-events:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-axis:build:lib:esm", "../victory-core:build:lib:esm", "../victory-polar-axis:build:lib:esm", "../victory-shared-events:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-axis:build:lib:esm", "../victory-core:build:lib:esm", "../victory-polar-axis:build:lib:esm", "../victory-shared-events:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-axis:types:create", "../victory-core:types:create", "../victory-polar-axis:types:create", "../victory-shared-events:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-axis:types:create", "../victory-core:types:create", "../victory-polar-axis:types:create", "../victory-shared-events:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-axis:types:create", "../victory-core:types:create", "../victory-polar-axis:types:create", "../victory-shared-events:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-axis:types:create", "../victory-core:types:create", "../victory-polar-axis:types:create", "../victory-shared-events:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-axis:build", "../victory-core:build", "../victory-polar-axis:build", "../victory-shared-events:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-chart/src/helper-methods.test.tsx ================================================ import React from "react"; import { VictoryAxis } from "victory-axis"; import { getChildComponents } from "./helper-methods"; const MockVictoryLine = () =>
; describe("victory-chart/helpers-methods", () => { describe("getChildComponents", () => { const defaultAxes = { independent: , dependent: , }; it("returns a pair of default axes when no children are given", () => { const children = []; const result = getChildComponents({ children }, defaultAxes); expect(result).toHaveLength(2); expect(result).toEqual([defaultAxes.independent, defaultAxes.dependent]); }); it("adds default axes when none of the children are axis components", () => { const line = ; const children = [line]; const result = getChildComponents({ children }, defaultAxes); expect(result).toHaveLength(3); expect(result).toContain(defaultAxes.independent); expect(result).toContain(defaultAxes.dependent); }); it("does not add default axes if axis any axis components exist in children", () => { const axis = ; const children = [axis]; const result = getChildComponents({ children }, defaultAxes); expect(result).toHaveLength(1); expect((result[0] as typeof axis).props).toEqual(axis.props); }); }); }); ================================================ FILE: packages/victory-chart/src/helper-methods.tsx ================================================ import React from "react"; import { Helpers, Scale, Axis, Wrapper } from "victory-core"; import defaults from "lodash/defaults"; const fallbackProps = { width: 450, height: 300, padding: 50, }; function getAxisProps(child, props, calculatedProps) { const { domain, scale, stringMap, categories, horizontal } = calculatedProps; return { stringMap, horizontal, categories, startAngle: props.startAngle, endAngle: props.endAngle, innerRadius: props.innerRadius, domain, scale, }; } export function getBackgroundWithProps(props, calculatedProps) { const backgroundElement = props.backgroundComponent; const height = props.polar ? calculatedProps.range.y[1] : calculatedProps.range.y[0] - calculatedProps.range.y[1]; const width = calculatedProps.range.x[1] - calculatedProps.range.x[0]; const xScale = props.horizontal ? calculatedProps.scale.y.range()[0] : calculatedProps.scale.x.range()[0]; const yScale = props.horizontal ? calculatedProps.scale.x.range()[1] : calculatedProps.scale.y.range()[1]; const xCoordinate = props.polar ? calculatedProps.origin.x : xScale; const yCoordinate = props.polar ? calculatedProps.origin.y : yScale; const parentName = props.name || "chart"; const backgroundProps = { height, polar: props.polar, scale: calculatedProps.scale, style: props.style.background, x: xCoordinate, y: yCoordinate, key: `${parentName}-background`, width, }; return React.cloneElement( backgroundElement, defaults({}, backgroundElement.props, backgroundProps), ); } function getChildProps(child, props, calculatedProps) { const axisChild = Axis.findAxisComponents([child]); if (axisChild.length > 0) { return getAxisProps(axisChild[0], props, calculatedProps); } const { categories, domain, range, scale, stringMap, horizontal } = calculatedProps; return { categories, domain, range, scale, stringMap, horizontal }; } function getStyles(props) { const styleProps = props.style && props.style.parent; return { parent: defaults({}, styleProps, { height: "100%", width: "100%", userSelect: "none", }), }; } export function getCalculatedProps(initialProps, childComponents) { const style = getStyles(initialProps); const props = Helpers.modifyProps(initialProps, fallbackProps, "chart"); const { horizontal, polar } = props; const allStrings = Wrapper.getStringsFromChildren(props, childComponents); const categories = Wrapper.getCategories(props, childComponents, allStrings); const stringMap = createStringMap(props, childComponents, allStrings); const domain = { x: getDomain( Object.assign({}, props, { categories }), "x", childComponents, ), y: getDomain( Object.assign({}, props, { categories }), "y", childComponents, ), }; const range = { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; const baseScale = { x: Scale.getScaleFromProps(props, "x") || Wrapper.getScale(props, "x"), y: Scale.getScaleFromProps(props, "y") || Wrapper.getScale(props, "y"), }; const scale = { x: baseScale.x.domain(domain.x).range(horizontal ? range.y : range.x), y: baseScale.y.domain(domain.y).range(horizontal ? range.x : range.y), }; const origin = polar ? Helpers.getPolarOrigin(props) : Axis.getOrigin(domain); const padding = Helpers.getPadding(props.padding); return { categories, domain, range, horizontal, scale, stringMap, style, origin, padding, }; } export function getChildren(props, childComponents, calculatedProps) { const children = childComponents || getChildComponents(props); const newCalculatedProps = calculatedProps || getCalculatedProps(props, children); const baseStyle = newCalculatedProps.style.parent; const { height, polar, theme, width } = props; const { origin, horizontal } = newCalculatedProps; const parentName = props.name || "chart"; return children.filter(React.isValidElement).map((child, index) => { const role = child.type && child.type.role; const style = Array.isArray(child.props.style) ? child.props.style : defaults({}, child.props.style, { parent: baseStyle }); const childProps = getChildProps(child, props, newCalculatedProps); const name = child.props.name || `${parentName}-${role}-${index}`; const newProps = defaults( { horizontal, height, polar, theme, width, style, name, origin: polar ? origin : undefined, padding: newCalculatedProps.padding, key: `${name}-key-${index}`, standalone: false, }, childProps, ); return React.cloneElement(child, newProps); }); } export const getChildComponents = (props, defaultAxes?) => { let childComponents = React.Children.toArray(props.children); if (childComponents.length === 0) { childComponents.push(defaultAxes.independent, defaultAxes.dependent); } else { const axisComponents = { dependent: Axis.getAxisComponentsWithParent(childComponents, "dependent"), independent: Axis.getAxisComponentsWithParent( childComponents, "independent", ), }; if ( axisComponents.dependent.length === 0 && axisComponents.independent.length === 0 ) { childComponents = props.prependDefaultAxes ? [defaultAxes.independent, defaultAxes.dependent].concat( childComponents, ) : childComponents.concat([ defaultAxes.independent, defaultAxes.dependent, ]); } } return childComponents; }; const getDomain = (props, axis, childComponents) => { const children = childComponents || React.Children.toArray(props.children); const domain = Wrapper.getDomain(props, axis, children); const axisComponent = Axis.getAxisComponent(children, axis); const invertDomain = axisComponent && axisComponent.props && axisComponent.props.invertAxis; return invertDomain ? domain.concat().reverse() : domain; }; const createStringMap = (props, childComponents, allStrings) => { const x = !allStrings.x || allStrings.x.length === 0 ? null : allStrings.x.reduce((memo, string, index) => { memo[string] = index + 1; return memo; }, {}); const y = !allStrings.y || allStrings.y.length === 0 ? null : allStrings.y.reduce((memo, string, index) => { memo[string] = index + 1; return memo; }, {}); return { x, y }; }; ================================================ FILE: packages/victory-chart/src/index.ts ================================================ export * from "./victory-chart"; ================================================ FILE: packages/victory-chart/src/victory-chart.test.tsx ================================================ import React from "react"; import { VictoryAxis } from "victory-axis"; import { render, screen, fireEvent } from "@testing-library/react"; import { VictoryChart } from "./victory-chart"; describe("components/victory-chart", () => { describe("default component rendering", () => { it("renders an svg with the correct width and height", () => { const { container } = render(); const svg = container.querySelector("svg")!; expect(svg.getAttribute("style")).toContain("width: 100%; height: 100%"); }); it("renders an svg with the correct viewBox", () => { const { container } = render(); const svg = container.querySelector("svg")!; const viewBoxValue = `0 0 ${450} ${300}`; expect(svg.getAttribute("viewBox")).toEqual(viewBoxValue); }); }); describe("axis rendering", () => { it("renders two axes by default", () => { const props = { defaultAxes: { independent: , dependent: , }, }; render(); const axes = screen.getAllByTestId("axis"); expect(axes).toHaveLength(2); }); it("renders one axis if one axis is given", () => { render( , ); const axes = screen.getAllByTestId("axis"); expect(axes).toHaveLength(1); }); // TODO: Is this test useful? It's hard to test this with react testing library, which // may mean it should be removed. it("allows axis to control the crossAxis, and offset props", () => { render( props.crossAxis} data-offset-x={(props) => props.offsetX} data-offset-y={(props) => props.offsetY} /> , ); const axis = screen.getByTestId("axis"); expect(axis.getAttribute("data-cross-axis")).toEqual("false"); expect(axis.getAttribute("data-offset-x")).toEqual("50"); expect(axis.getAttribute("data-offset-y")).toEqual("50"); }); it("accepts user props", () => { render(); expect(screen.getByTestId("victory-chart")).toBeDefined(); expect(screen.getByLabelText("Chart")).toBeDefined(); }); }); describe("event handling", () => { it("attaches an event to the parent svg", () => { const clickHandler = jest.fn(); const { container } = render( , ); const svg = container.querySelector("svg")!; fireEvent.click(svg); expect(clickHandler).toHaveBeenCalled(); }); }); describe("animation", () => { it("handles basic animation parameters without crashing", () => { const { container } = render( , ); expect(container.querySelector("svg")).toBeInTheDocument(); }); }); }); ================================================ FILE: packages/victory-chart/src/victory-chart.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import isEmpty from "lodash/isEmpty"; import { Background, Helpers, Hooks, UserProps, VictoryContainer, VictoryTheme, Wrapper, CategoryPropType, DomainPropType, EventPropTypeInterface, StringOrNumberOrCallback, VictoryCommonProps, VictoryStyleInterface, VictoryStyleObject, } from "victory-core"; import { VictorySharedEvents } from "victory-shared-events"; import { VictoryAxis } from "victory-axis"; import { VictoryPolarAxis } from "victory-polar-axis"; import { getBackgroundWithProps, getChildComponents, getCalculatedProps, getChildren, } from "./helper-methods"; import isEqual from "react-fast-compare"; const fallbackProps = { width: 450, height: 300, padding: 50, }; const defaultProps = { backgroundComponent: , containerComponent: , defaultAxes: { independent: , dependent: , }, defaultPolarAxes: { independent: , dependent: , }, groupComponent: , standalone: true, theme: VictoryTheme.grayscale, }; const VictoryChartImpl: React.FC = (initialProps) => { const propsWithDefaults = React.useMemo( () => defaults({}, initialProps, defaultProps), [initialProps], ); const role = "chart"; const { getAnimationProps, setAnimationState, getProps } = Hooks.useAnimationState(); const props = getProps(propsWithDefaults); const modifiedProps = Helpers.modifyProps(props, fallbackProps, role); const { desc, eventKey, containerComponent, standalone, groupComponent, externalEventMutations, width, height, theme, polar, name, title, } = modifiedProps; const axes = props.polar ? modifiedProps.defaultPolarAxes : modifiedProps.defaultAxes; const childComponents = React.useMemo( () => getChildComponents(modifiedProps, axes), [modifiedProps, axes], ); const calculatedProps = React.useMemo( () => getCalculatedProps(modifiedProps, childComponents), [modifiedProps, childComponents], ); const { domain, scale, style, origin, horizontal } = calculatedProps; const newChildren = React.useMemo(() => { const children = getChildren(props, childComponents, calculatedProps); const mappedChildren = children.map((child, index) => { const childProps = Object.assign( { animate: getAnimationProps(props, child, index) }, child.props, ); return React.cloneElement(child, childProps); }); if (props.style && props.style.background) { const backgroundComponent = getBackgroundWithProps( props, calculatedProps, ); mappedChildren.unshift(backgroundComponent); } return mappedChildren; }, [getAnimationProps, childComponents, props, calculatedProps]); const containerProps = React.useMemo(() => { if (standalone) { return { desc, domain, width, height, horizontal, name, origin: polar ? origin : undefined, polar, theme, title, scale, standalone, style: style.parent, }; } return {}; }, [ desc, domain, height, horizontal, name, origin, polar, scale, standalone, style, title, theme, width, ]); const container = React.useMemo(() => { if (standalone) { const defaultContainerProps = defaults( {}, containerComponent.props, containerProps, UserProps.getSafeUserProps(propsWithDefaults), ); return React.cloneElement(containerComponent, defaultContainerProps); } return groupComponent; }, [ groupComponent, standalone, containerComponent, containerProps, propsWithDefaults, ]); const events = React.useMemo(() => { return Wrapper.getAllEvents(props); }, [props]); const previousProps = Hooks.usePreviousProps(propsWithDefaults); React.useEffect(() => { // This is called before dismount to keep state in sync return () => { if (propsWithDefaults.animate) { setAnimationState(previousProps, propsWithDefaults); } }; }, [setAnimationState, previousProps, propsWithDefaults]); if (!isEmpty(events)) { return ( {newChildren} ); } return React.cloneElement(container, container.props, newChildren); }; export const VictoryChart = React.memo(VictoryChartImpl, isEqual); VictoryChart.displayName = "VictoryChart"; // @ts-expect-error FIXME: Does this "expectedComponents" do anything? VictoryChart.expectedComponents = ["groupComponent", "containerComponent"]; export type AxesType = { dependent?: React.ReactElement | null; independent?: React.ReactElement | null; }; export interface VictoryChartProps extends VictoryCommonProps { backgroundComponent?: React.ReactElement; categories?: CategoryPropType; children?: React.ReactNode | React.ReactNode[]; desc?: string; defaultAxes?: AxesType; defaultPolarAxes?: AxesType; domain?: DomainPropType; endAngle?: number; eventKey?: StringOrNumberOrCallback; events?: EventPropTypeInterface< string, string[] | number[] | string | number >[]; innerRadius?: number; prependDefaultAxes?: boolean; startAngle?: number; style?: Pick & { background?: VictoryStyleObject; }; title?: string; } ================================================ FILE: packages/victory-chart/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-chart/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-core/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-core/CHANGELOG.md ================================================ # victory-core ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Upgrade typescript to 5.7.2 ([#2997](https://github.com/FormidableLabs/victory/pull/2997)) * Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) - Improve types in victory-core helpers ([#2999](https://github.com/FormidableLabs/victory/pull/2999)) * Zoomed bar graph items will no longer be culled from the view when more than 50% of width or height is outside of the clipping parent. Instead they will be clipped once 100% outside" ([#2970](https://github.com/FormidableLabs/victory/pull/2970)) - fix hydration error using victory line with nextjs ([#2973](https://github.com/FormidableLabs/victory/pull/2973)) ## 37.3.2 ### Patch Changes - Fix regression to touchAction override in VictoryContainer ([#2953](https://github.com/FormidableLabs/victory/pull/2953)) ## 37.3.1 ### Patch Changes - Remove duplicate types from interfaces ([#2940](https://github.com/FormidableLabs/victory/pull/2940)) ## 37.3.0 ### Minor Changes - Clean theme color updates and other minor adjustments ([#2920](https://github.com/FormidableLabs/victory/pull/2920)) * Fix VictoryPortal id changing for every render ([#2929](https://github.com/FormidableLabs/victory/pull/2929)) ### Patch Changes - fix categories array of strings ([#2919](https://github.com/FormidableLabs/victory/pull/2919)) ## 37.2.0 ### Minor Changes - Minor updates for clean theme ([#2909](https://github.com/FormidableLabs/victory/pull/2909)) * more minor updates to clean theme ([#2915](https://github.com/FormidableLabs/victory/pull/2915)) ### Patch Changes - replace useId for backwards compatibility ([#2916](https://github.com/FormidableLabs/victory/pull/2916)) ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Refactor containers and portal to function components ([#2799](https://github.com/FormidableLabs/victory/pull/2799)) * Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash keys with native code ([#2811](https://github.com/FormidableLabs/victory/pull/2811)) * Replace lodash identity with native code ([#2829](https://github.com/FormidableLabs/victory/pull/2829)) - Refactor victory-accessible-group to a function component ([#2777](https://github.com/FormidableLabs/victory/pull/2777)) * Convert victory-animation to function component ([#2788](https://github.com/FormidableLabs/victory/pull/2788)) - Replace lodash.invert with native code ([#2830](https://github.com/FormidableLabs/victory/pull/2830)) * Replace lodash difference with native code ([#2828](https://github.com/FormidableLabs/victory/pull/2828)) - Replace lodash array utils with native code ([#2810](https://github.com/FormidableLabs/victory/pull/2810)) * allow VictoryBoxPlot childStyles to override VictoryGroup parent styles ([#2824](https://github.com/FormidableLabs/victory/pull/2824)) - Replace lodash values and mapValues with native code ([#2808](https://github.com/FormidableLabs/victory/pull/2808)) * Replace lodash isNil and isNan with native code ([#2800](https://github.com/FormidableLabs/victory/pull/2800)) - Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ### Patch Changes - Migrate victory-native to TypeScript ([#2739](https://github.com/FormidableLabs/victory/pull/2739)) ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) * Replace instances of lodash.range with equivalent native code ([#2760](https://github.com/FormidableLabs/victory/pull/2760)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Fix incorrect typescript props ([#2745](https://github.com/FormidableLabs/victory/pull/2745)) * Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) - Migrate victory-shared-events to TypeScript ([#2733](https://github.com/FormidableLabs/victory/pull/2733)) * Migrate victory-tooltip to typescript ([#2725](https://github.com/FormidableLabs/victory/pull/2725)) ## 36.8.2 ### Patch Changes - Fix the label background position when using dy fns ([#2720](https://github.com/FormidableLabs/victory/pull/2720)) * Migrate victory-legend to TypeScript ([#2712](https://github.com/FormidableLabs/victory/pull/2712)) ## 36.8.1 ### Patch Changes - Correctly type props in Victory Primitives ([#2695](https://github.com/FormidableLabs/victory/pull/2695)) ## 36.8.0 ### Minor Changes - Remove v37 experimental code ([#2697](https://github.com/FormidableLabs/victory/pull/2697)) ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) * Fixed issue where VictoryChart would throw an unhandled exception when passed non-element children (fixes [#2391](https://github.com/FormidableLabs/victory/issues/2391)) ([#2536](https://github.com/FormidableLabs/victory/pull/2536)) - Fix text label measurements after SSR hydration mismatch ([#2626](https://github.com/FormidableLabs/victory/pull/2626)) ## 36.7.0 ### Minor Changes - added ref forwarding for path and bar components ([#2673](https://github.com/FormidableLabs/victory/pull/2673)) ## 36.6.12 ### Patch Changes - Add aria-hidden flag to svg for textsize util to fix accessibility issue ([#2661](https://github.com/FormidableLabs/victory/pull/2661)) ## 36.6.11 ### Patch Changes - Fix text size regression when using line-height ([#2615](https://github.com/FormidableLabs/victory/pull/2615)) ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Lint fixes. ([#2497](https://github.com/FormidableLabs/victory/pull/2497)) * Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) - Improve accuracy of text size measurements (fixes [#2475](https://github.com/FormidableLabs/victory/issues/2475)) ([#2505](https://github.com/FormidableLabs/victory/pull/2505)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-vendor@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-vendor@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-vendor@36.6.6 ## 36.6.5 ### Patch Changes - Manually included types for Immutable (fixes [#2439](https://github.com/FormidableLabs/victory/issues/2439)) ([#2440](https://github.com/FormidableLabs/victory/pull/2440)) - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-vendor@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-vendor@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-vendor@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-vendor@36.6.2 ## 36.6.1 ### Patch Changes - - Removed Template Literal Types to increase TS compatibility (fixes [#2418](https://github.com/FormidableLabs/victory/issues/2418)) ([#2420](https://github.com/FormidableLabs/victory/pull/2420)) - Improved type for `VictoryLabelProps["textAnchor"]` (fixes [#2361](https://github.com/FormidableLabs/victory/issues/2361)) - Fixed exported types for `VictoryAxis`, `VictoryBoxPlot`, `VictoryErrorBar`, and `VictoryScatter` (fixes [#2411](https://github.com/FormidableLabs/victory/issues/2411)) - Migrate `victory-cursor-container` to TS (fixes [#2402](https://github.com/FormidableLabs/victory/issues/2402)) - Updated dependencies []: - victory-vendor@36.6.1 ## 36.6.0 ### Minor Changes - Migration of victory-errorbar to TypeScript ([#2395](https://github.com/FormidableLabs/victory/pull/2395)) ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-vendor@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-core/README.md ================================================ # VictoryCore `victory-core@30.0.0` exports packages that are used by several Victory components: - `VictoryAnimation` - `VictoryClipContainer` - `VictoryContainer` - `VictoryLabel` - `VictoryPortal` and `Portal` - `VictoryTheme` - `VictoryTransition` - Several primitive components: `Arc`, `Border` / `Box`, `Circle`, `ClipPath`, `LineSegment` (formerly `Axis` / `Grid`), `Line`, `Path`, `Point`, `Rect`, `Text`, `TSpan`, `Whisker` - Several utilities: - `addEvents`, `Axis`, `Collection`, `CommonProps`, `Data`, `DefaultTransitions`, `Domain`, `Events`, `Helpers`, `Immutable`, `LabelHelpers`, `Log`, `PropTypes`, `Scale`, `Selection`, `Style`, `TextSize`, `Timer`, `Transitions`, `Wrapper` Please visit our documentation site to read more about these components https://commerce.nearform.com/open-source/victory To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs ================================================ FILE: packages/victory-core/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-core/package.json ================================================ { "name": "victory-core", "version": "37.3.6", "description": "Victory Core", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.21", "react-fast-compare": "^3.2.0", "victory-vendor": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-vendor:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-core/src/exports.test.ts ================================================ import * as VictoryCore from "./index"; /* eslint-disable @typescript-eslint/no-unused-vars */ // Import EVERYTHING from 'victory-core' to ensure it's getting exported correctly: import { // @ts-expect-error TEST_EXPORTS should not be defined, so it should error! TEST_EXPORTS, AnimatePropTypeInterface, AnimationData, AnimationEasing, AnimationInfo, AnimationStyle, Arc, ArcProps, Axis, Background, BackgroundProps, BlockProps, Border, BorderProps, Box, CallbackArgs, CategoryPropType, Circle, ClipPath, ClipPathProps, Collection, ColorScalePropType, CoordinatesPropType, D3Scale, Data, DataGetterPropType, DefaultTransitions, Domain, DomainPaddingPropType, DomainPropType, DomainTuple, EventCallbackInterface, EventPropTypeInterface, Events, Helpers, Hooks, Immutable, InterpolationPropType, LabelHelpers, LabelOrientationType, LabelProps, Line, LineHelpers, LineSegment, LineSegmentProps, Log, NumberOrCallback, OrientationOrCallback, OrientationTypes, OriginType, PaddingOrCallback, PaddingProps, PaddingType, Path, Point, PointPathHelpers, PointProps, Portal, PortalContext, PortalContextValue, PortalOutlet, PortalOutletProps, PortalProvider, PortalProviderProps, PortalProps, RangePropType, RangeTuple, Rect, SVGCoordinateBounds, SVGCoordinateType, Scale, ScaleName, ScalePropType, ScaleXYPropType, ScatterSymbolType, Selection, SliceNumberOrCallback, SortOrderPropType, StringOrCallback, StringOrNumberOrCallback, StringOrNumberOrList, Style, TSpan, Text, TextAnchorType, TextProps, TextSize, TextSizeStyleInterface, TickProps, Timer, TimerContext, Transitions, UserProps, VerticalAnchorType, VictoryAccessibleGroup, VictoryAccessibleGroupProps, VictoryAnimation, VictoryAnimationProps, VictoryAnimationState, VictoryAxisCommonProps, VictoryClipContainer, VictoryClipContainerProps, VictoryCommonPrimitiveProps, VictoryCommonProps, VictoryCommonThemeProps, VictoryContainer, useVictoryContainer, VictoryContainerProps, VictoryDatableProps, VictoryLabel, VictoryLabelProps, VictoryLabelStyleObject, VictoryLabelableProps, VictoryMultiLabelableProps, VictoryNumberCallback, VictoryOrientationCallback, VictoryPaddingCallback, VictoryPortal, VictoryPortalProps, VictoryPrimitiveShapeProps, VictorySingleLabelableProps, VictoryStringCallback, VictoryStringOrNumberCallback, VictoryStyleInterface, VictoryStyleObject, VictoryTheme, VictoryThemeDefinition, VictoryTickStyleObject, VictoryTransition, Whisker, WhiskerAxes, WhiskerProps, Wrapper, addEvents, mergeRefs, usePortalContext, } from "./index"; import pick from "lodash/pick"; describe("victory-core", () => { it("exports addEvents", () => { // This test exists to ensure we don't have an "unused import" error expect(addEvents).toBeInstanceOf(Function); }); it("should export everything", () => { expect(Object.keys(VictoryCore).sort()).toMatchInlineSnapshot(` [ "Arc", "Axis", "Background", "Border", "Box", "Circle", "ClipPath", "Collection", "Data", "DefaultTransitions", "Domain", "Events", "Helpers", "Hooks", "Immutable", "LabelHelpers", "Line", "LineHelpers", "LineSegment", "Log", "Path", "Point", "PointPathHelpers", "Portal", "PortalContext", "PortalOutlet", "PortalProvider", "Rect", "Scale", "Selection", "Style", "TSpan", "Text", "TextSize", "Timer", "TimerContext", "Transitions", "UserProps", "VictoryAccessibleGroup", "VictoryAnimation", "VictoryClipContainer", "VictoryContainer", "VictoryLabel", "VictoryPortal", "VictoryTheme", "VictoryTransition", "Whisker", "Wrapper", "addEvents", "mergeRefs", "usePortalContext", "useVictoryContainer", ] `); }); describe("namespaces", () => { // These "namespaces" represent when we use `export * as` const namespaces = [ "Axis", "Collection", "Data", "DefaultTransitions", "Domain", "Events", "Helpers", "Hooks", "Immutable", "LabelHelpers", "LineHelpers", "Log", "PointPathHelpers", "Scale", "Selection", "Style", "TextSize", "Transitions", "UserProps", "Wrapper", ]; it("should export all namespaces", () => { expect(pick(VictoryCore, namespaces)).toMatchInlineSnapshot(` { "Axis": { "findAxisComponents": [Function], "getAxis": [Function], "getAxisComponent": [Function], "getAxisComponentsWithParent": [Function], "getAxisValue": [Function], "getDomain": [Function], "getOrigin": [Function], "getOriginSign": [Function], "getTickFormat": [Function], "getTicks": [Function], "isVertical": [Function], "modifyProps": [Function], "stringTicks": [Function], }, "Collection": { "containsDates": [Function], "containsNumbers": [Function], "containsOnlyStrings": [Function], "containsStrings": [Function], "difference": [Function], "getMaxValue": [Function], "getMinValue": [Function], "isArrayOfArrays": [Function], "removeUndefined": [Function], }, "Data": { "createStringMap": [Function], "downsample": [Function], "formatData": [Function], "formatDataFromDomain": [Function], "generateData": [Function], "getCategories": [Function], "getData": [Function], "getStringsFromAxes": [Function], "getStringsFromCategories": [Function], "getStringsFromData": [Function], "isDataComponent": [Function], }, "DefaultTransitions": { "continuousPolarTransitions": [Function], "continuousTransitions": [Function], "discreteTransitions": [Function], }, "Domain": { "createDomainFunction": [Function], "formatDomain": [Function], "getDomain": [Function], "getDomainFromCategories": [Function], "getDomainFromData": [Function], "getDomainFromMinMax": [Function], "getDomainFromProps": [Function], "getDomainWithZero": [Function], "getMaxFromProps": [Function], "getMinFromProps": [Function], "getSymmetricDomain": [Function], "isDomainComponent": [Function], }, "Events": { "emulateReactEvent": [Function], "getComponentEvents": [Function], "getEventState": [Function], "getEvents": [Function], "getExternalMutation": [Function], "getExternalMutations": [Function], "getExternalMutationsWithChildren": [Function], "getGlobalEventNameFromKey": [Function], "getGlobalEvents": [Function], "getPartialEvents": [Function], "getScopedEvents": [Function], "omitGlobalEvents": [Function], }, "Helpers": { "createAccessor": [Function], "degreesToRadians": [Function], "evaluateProp": [Function], "evaluateStyle": [Function], "getCurrentAxis": [Function], "getDefaultStyles": [Function], "getPadding": [Function], "getPoint": [Function], "getPolarOrigin": [Function], "getRadius": [Function], "getRange": [Function], "getStyles": [Function], "invert": [Function], "isFunction": [Function], "isHorizontal": [Function], "isNil": [Function], "isTooltip": [Function], "mapValues": [Function], "modifyProps": [Function], "omit": [Function], "radiansToDegrees": [Function], "range": [Function], "reduceChildren": [Function], "scalePoint": [Function], }, "Hooks": { "useAnimationState": [Function], "usePreviousProps": [Function], }, "Immutable": { "IMMUTABLE_ITERABLE": "@@__IMMUTABLE_ITERABLE__@@", "IMMUTABLE_LIST": "@@__IMMUTABLE_LIST__@@", "IMMUTABLE_MAP": "@@__IMMUTABLE_MAP__@@", "IMMUTABLE_RECORD": "@@__IMMUTABLE_RECORD__@@", "isImmutable": [Function], "isIterable": [Function], "isList": [Function], "isMap": [Function], "isRecord": [Function], "shallowToJS": [Function], }, "LabelHelpers": { "getDegrees": [Function], "getPolarAngle": [Function], "getPolarTextAnchor": [Function], "getPolarVerticalAnchor": [Function], "getProps": [Function], "getText": [Function], }, "LineHelpers": { "getInterpolationFunction": [Function], "getLineFunction": [Function], }, "Log": { "warn": [Function], }, "PointPathHelpers": { "circle": [Function], "cross": [Function], "diamond": [Function], "minus": [Function], "plus": [Function], "square": [Function], "star": [Function], "triangleDown": [Function], "triangleUp": [Function], }, "Scale": { "getBaseScale": [Function], "getDefaultScale": [Function], "getScaleFromName": [Function], "getScaleFromProps": [Function], "getScaleType": [Function], "getType": [Function], "validScale": [Function], }, "Selection": { "getBounds": [Function], "getDataCoordinates": [Function], "getDomainCoordinates": [Function], "getParentSVG": [Function], "getSVGEventCoordinates": [Function], }, "Style": { "getColorScale": [Function], "toTransformString": [Function], }, "TextSize": { "_approximateTextSizeInternal": { "impl": [Function], }, "approximateTextSize": [Function], "convertLengthToPixels": [Function], }, "Transitions": { "getInitialTransitionState": [Function], "getTransitionPropsFactory": [Function], }, "UserProps": { "assert": [Function], "getSafeUserProps": [Function], "withSafeUserProps": [Function], }, "Wrapper": { "addBinsToParentPropsIfHistogram": [Function], "getAllEvents": [Function], "getCategories": [Function], "getCategoryAndAxisStringsFromChildren": [Function], "getChildStyle": [Function], "getColor": [Function], "getData": [Function], "getDataFromChildren": [Function], "getDefaultDomainPadding": [Function], "getDomain": [Function], "getDomainFromChildren": [Function], "getScale": [Function], "getStringsFromChildren": [Function], "getStringsFromChildrenCategories": [Function], "getStringsFromData": [Function], "getStyle": [Function], "getWidth": [Function], }, } `); }); }); }); ================================================ FILE: packages/victory-core/src/index.ts ================================================ export * from "./types/callbacks"; export * from "./types/prop-types"; export * from "./victory-accessible-group/victory-accessible-group"; export * from "./victory-animation/victory-animation"; export * from "./victory-container/victory-container"; export * from "./victory-label/victory-label"; export * from "./victory-transition/victory-transition"; export * from "./victory-clip-container/victory-clip-container"; export * from "./victory-theme/types"; export * from "./victory-theme/victory-theme"; export * from "./victory-portal/portal"; export * from "./victory-portal/victory-portal"; export * from "./victory-portal/portal-context"; export * from "./victory-portal/portal-outlet"; export * from "./victory-primitives"; export { Border as Box } from "./victory-primitives"; export type { BorderProps as BoxProps } from "./victory-primitives"; export * from "./victory-util"; ================================================ FILE: packages/victory-core/src/types/callbacks.ts ================================================ import { D3Scale, Datum, ID, ScalePropType } from "./prop-types"; import { BlockProps, OrientationTypes } from "../victory-theme/types"; /** * This is the first parameter of a callback when a callback is used to * resolve the value of a property instead of a concrete value. * * Note that additional properties here like `scale`, `x`, `y`, etc are resolved * values of properties from the VictoryXXXProps for each component. */ export interface CallbackArgs { active?: boolean; data?: Datum[]; datum?: Datum; horizontal?: boolean; index?: ID; x?: number; y?: number; scale?: | ScalePropType | D3Scale | { x?: ScalePropType | D3Scale; y?: ScalePropType | D3Scale; }; tick?: any; ticks?: any; text?: any; } export type VictoryStringOrNumberCallback = ( args: CallbackArgs, ) => string | number; export type VictoryStringCallback = (args: CallbackArgs) => string; export type StringOrNumberOrCallback = | string | number | VictoryStringOrNumberCallback; export type StringOrCallback = string | VictoryStringCallback; export type SliceNumberOrCallback< T, P extends string | number | symbol = never, > = number | ((props: Omit) => number); export type VictoryNumberCallback = (args: CallbackArgs) => number; export type VictoryPaddingCallback = ( args: CallbackArgs, ) => number | BlockProps; export type VictoryOrientationCallback = ( args: CallbackArgs, ) => OrientationTypes; export type NumberOrCallback = number | VictoryNumberCallback; export type PaddingOrCallback = number | BlockProps | VictoryPaddingCallback; export type OrientationOrCallback = | OrientationTypes | VictoryOrientationCallback; ================================================ FILE: packages/victory-core/src/types/prop-types.ts ================================================ import * as React from "react"; import { ScaleLinear, ScaleLogarithmic, ScalePower, ScaleTime, } from "victory-vendor/d3-scale"; import { AnimationEasing, AnimationStyle, } from "../victory-animation/victory-animation"; import { CallbackArgs, StringOrNumberOrCallback } from "./callbacks"; export type AxisType = "x" | "y"; export type DatumValue = number | string | Date | null | undefined; export type Datum = any; export type ForAxes = T | { x?: T; y?: T }; export type ID = number | string; export type ValueOrAccessor = | ValueType | ((props: PropsType) => ValueType); export type Tuple = [T, T]; export type ValueOrAxes = T | ForAxes; export type DomainPaddingPropType = ValueOrAxes; export type DomainPropType = ValueOrAxes; export type DomainValue = number | Date; // This should be a tuple type, but every time we use it, it fails with a type error. // type number[] is not assignable to type [number, number] | [Date, Date]. export type DomainTuple = Tuple | Tuple; export type PaddingType = number | Tuple; export type RangePropType = ValueOrAxes; export type RangeTuple = number[]; export type StringOrNumberOrList = string | number | (string | number)[]; export interface Padding { top: number; bottom: number; left: number; right: number; } export interface AnimatePropTypeInterface { duration?: number; delay?: number; onEnd?: () => void; onExit?: { duration?: number; before?: (datum: Datum, index: number, data: Datum[]) => AnimationStyle; }; onEnter?: { duration?: number; before?: (datum: Datum, index: number, data: Datum[]) => AnimationStyle; after?: (datum: Datum, index: number, data: Datum[]) => AnimationStyle; }; onLoad?: { duration?: number; before?: (datum: Datum, index: number, data: Datum[]) => AnimationStyle; after?: (datum: Datum, index: number, data: Datum[]) => AnimationStyle; }; easing?: AnimationEasing; animationWhitelist?: string[]; } export interface EventCallbackInterface { childName?: string | string[]; target?: TTarget; eventKey?: TEventKey; mutation: (props: any) => any; callback?: (props: any) => any; } export interface EventPropTypeInterface { childName?: string | Array; target: TTarget; eventKey?: TEventKey; eventHandlers: { [key: string]: | { ( event: React.SyntheticEvent, props?: any, ): EventCallbackInterface; } | { ( event: React.SyntheticEvent, props?: any, ): EventCallbackInterface[]; } | { (event: React.SyntheticEvent, props?: any): void; }; }; } type NumberValue = number | { valueOf(): number }; /** * D3 scale function shape. Don't want to introduce typing dependency to d3 */ export interface D3Scale { (numberValue: NumberValue): number; base?: () => number; ticks: (count?: number) => number[]; tickFormat: (count?: number) => (d: number) => string; domain: { (): number[]; (domain: NumberValue[]): D3Scale; }; range: { (): TRange[]; (range: TRange): D3Scale; }; copy: () => this; invert: (value: number) => number; } export type D3ScaleFn = () => | ScaleLinear | ScaleLogarithmic | ScaleTime | ScalePower; export type ScaleName = "linear" | "time" | "log" | "sqrt"; export type ScalePropType = ScaleName; export type ScaleXYPropType = { x: D3Scale; y: D3Scale; }; export type CategoryPropType = | string[] | { x: string[] } | { y: string[] } | { x: string[]; y: string[]; }; export type DataGetterPropType = ValueOrAccessor< string | string[] | number | number[], any // The arg will be the `datum`, which can have any type >; export type InterpolationPropType = | "basis" | "basisClosed" | "basisOpen" | "bundle" | "cardinal" | "cardinalClosed" | "cardinalOpen" | "catmullRom" | "catmullRomClosed" | "catmullRomOpen" | "linear" | "linearClosed" | "monotoneX" | "monotoneY" | "natural" | "radial" | "step" | "stepAfter" | "stepBefore"; export type ColorScalePropType = | "grayscale" | "qualitative" | "heatmap" | "warm" | "cool" | "red" | "green" | "blue" | string[]; export type SortOrderPropType = "ascending" | "descending"; export interface VictoryLabelableProps { labelComponent?: React.ReactElement; } export interface VictoryMultiLabelableProps extends VictoryLabelableProps { labels?: | string[] | number[] | { (data: any): string | string[] | number | number[] | null }; } export interface VictorySingleLabelableProps extends VictoryLabelableProps { label?: string | { (data: any): string | number | null }; } export type CoordinatesPropType = { x: number; y: number; }; export type VictoryEventHandler = ( event?: any, targetProps?: any, eventKey?: any, context?: any, ) => void; ================================================ FILE: packages/victory-core/src/victory-accessible-group/victory-accessible-group.test.tsx ================================================ import { render } from "@testing-library/react"; import React from "react"; import { SVGWrapper } from "../../../../test/helpers"; import { VictoryAccessibleGroup } from "./victory-accessible-group"; describe("components/victory-accessible-group", () => { it("renders an g with an aria-label", () => { const { container } = render( , { wrapper: SVGWrapper }, ); expect(container.querySelector("g")).toMatchInlineSnapshot(` `); }); it("renders an g with a tabIndex and className", () => { const { container } = render( , { wrapper: SVGWrapper }, ); expect(container.querySelector("g")).toMatchInlineSnapshot(` `); }); it("renders an g with a desc node if given", () => { const { container } = render( , { wrapper: SVGWrapper }, ); expect(container.querySelector("g")).toMatchInlineSnapshot(` test description `); }); it("uses the desc getAttribute value for descId and aria-describedby if no aria-describedby getAttribute value", () => { const { container } = render( , { wrapper: SVGWrapper }, ); expect(container.querySelector("g")).toMatchInlineSnapshot(` applies to both aria-describedby and descId `); }); }); ================================================ FILE: packages/victory-core/src/victory-accessible-group/victory-accessible-group.tsx ================================================ import React from "react"; export interface VictoryAccessibleGroupProps { desc?: string; "aria-describedby"?: string; "aria-label"?: string; children?: React.ReactElement | React.ReactElement[]; className?: string; tabIndex?: number; } export const VictoryAccessibleGroup = ({ desc, children, tabIndex, className = "VictoryAccessibleGroup", ...props }: VictoryAccessibleGroupProps) => { const descId = desc && (props["aria-describedby"] || desc.split(" ").join("-")); return desc ? ( {desc} {children} ) : ( {children} ); }; ================================================ FILE: packages/victory-core/src/victory-animation/util.test.tsx ================================================ import { victoryInterpolator } from "./util"; describe("victoryInterpolator", () => { it("does not attempt to interpolate identical values", () => { // This case fails with the default interpolator, returning *almost* 3. expect(victoryInterpolator(3, 3)(0.25920000000000004)).toEqual(3); }); it("does not attempt to interpolate Boolean values", () => { // The default interpolator would return 0.5. expect(victoryInterpolator(false, true)(0.5)).toEqual(true); }); it("always returns the end value if starting from null", () => { const interpolator = victoryInterpolator(null, 5); expect(interpolator(0)).toEqual(5); expect(interpolator(0.49)).toEqual(5); expect(interpolator(0.5)).toEqual(5); expect(interpolator(1)).toEqual(5); }); it("always returns the end value if ending on null", () => { const interpolator = victoryInterpolator(5, null); expect(interpolator(0)).toBeNull(); expect(interpolator(0.49)).toBeNull(); expect(interpolator(0.5)).toBeNull(); expect(interpolator(1)).toBeNull(); }); it("always returns the end value if starting from undefined", () => { const interpolator = victoryInterpolator(undefined, 5); expect(interpolator(0)).toEqual(5); expect(interpolator(0.49)).toEqual(5); expect(interpolator(0.5)).toEqual(5); expect(interpolator(1)).toEqual(5); }); it("always returns the end value if ending on undefined", () => { const interpolator = victoryInterpolator(5, undefined); expect(interpolator(0)).toBeUndefined(); expect(interpolator(0.49)).toBeUndefined(); expect(interpolator(0.5)).toBeUndefined(); expect(interpolator(1)).toBeUndefined(); }); it("interpolates functions", () => { const fromFn = () => 5; const toFn = () => 10; const interpolator = victoryInterpolator(fromFn, toFn); const halfwayFn = interpolator(0.5); expect(halfwayFn).toBeInstanceOf(Function); expect(halfwayFn()).toEqual(7.5); }); it("interpolates string values", () => { // From https://github.com/d3/d3-interpolate/blob/main/test/value-test.js#L5-L7 const interpolator = victoryInterpolator("foo", "bar"); expect(interpolator(0.5)).toEqual("bar"); }); it("interpolates color values", () => { // From https://github.com/d3/d3-interpolate/blob/main/test/value-test.js#L15 const interpolator = victoryInterpolator("red", "blue"); expect(interpolator(0.5)).toEqual("rgb(128, 0, 128)"); }); it("interpolates object values", () => { // From https://github.com/d3/d3-interpolate/blob/main/test/value-test.js#L44 const interpolator = victoryInterpolator( { color: "red" }, { color: "blue" }, ); expect(interpolator(0.5)).toEqual({ color: "rgb(128, 0, 128)" }); }); }); ================================================ FILE: packages/victory-core/src/victory-animation/util.ts ================================================ import isPlainObject from "lodash/isPlainObject"; import orderBy from "lodash/orderBy"; import { interpolate } from "victory-vendor/d3-interpolate"; export const isInterpolatable = function (obj) { // d3 turns null into 0 and undefined into NaN, which we don't want. if (obj !== null) { switch (typeof obj) { case "undefined": return false; case "number": // The standard `isNaN` is fine in this case since we already know the // type is number. return ( !isNaN(obj) && obj !== Number.POSITIVE_INFINITY && obj !== Number.NEGATIVE_INFINITY ); case "string": // d3 might not *actually* be able to interpolate the string, but it // won't cause any issues to let it try. return true; case "boolean": // d3 turns Booleans into integers, which we don't want. Sure, we could // interpolate from 0 -> 1, but we'd be sending a non-Boolean to // something expecting a Boolean. return false; case "object": // Don't try to interpolate class instances (except Date or Array). return obj instanceof Date || Array.isArray(obj) || isPlainObject(obj); case "function": // Careful! There may be extra properties on function objects that the // component expects to access - for instance, it may be a `d3.scale()` // function, which has its own methods attached. We don't know if the // component is only going to call the function (in which case it's // safely interpolatable) or if it's going to access special properties // (in which case our function generated from `interpolateFunction` will // most likely cause an error). We could check for enumerable properties // on the function object here to see if it's a "plain" function, but // let's just require that components prevent such function props from // being animated in the first place. return true; } } return false; }; /** * Interpolate immediately to the end value at the given step `when`. * Some nicer default behavior might be to jump at the halfway point or return * `a` if `t` is 0 (instead of always returning `b`). But d3's default * interpolator does not do these things: * * d3.interpolate('aaa', 'bbb')(0) === 'bbb' * * ...and things might get wonky if we don't replicate that behavior. * * @param {any} a - Start value. * @param {any} b - End value. * @param {Number} when - Step value (0 to 1) at which to jump to `b`. * @returns {Function} An interpolation function. */ export const interpolateImmediate = function (a, b, when = 0) { return function (t) { return t < when ? a : b; }; }; /** * Interpolate to or from a function. The interpolated value will be a function * that calls `a` (if it's a function) and `b` (if it's a function) and calls * `d3.interpolate` on the resulting values. Note that our function won't * necessarily be called (that's up to the component this eventually gets * passed to) - but if it does get called, it will return an appropriately * interpolated value. * * @param {any} a - Start value. * @param {any} b - End value. * @returns {Function} An interpolation function. */ export const interpolateFunction = function (a, b) { return function (t) { if (t >= 1) { return b; } return function (this: unknown) { /* eslint-disable prefer-rest-params */ const aval = typeof a === "function" ? a.apply(this, arguments) : a; const bval = typeof b === "function" ? b.apply(this, arguments) : b; return interpolate(aval, bval)(t); }; }; }; /** * Interpolate to or from an object. This method is a modification of the object interpolator in * d3-interpolate https://github.com/d3/d3-interpolate/blob/master/src/object.js. This interpolator * differs in that it uses our custom interpolators when interpolating the value of each property in * an object. This allows the correct interpolation of nested objects, including styles * * @param {any} startValue - Start value. * @param {any} endValue - End value. * @returns {Function} An interpolation function. */ export const interpolateObject = function (startValue, endValue) { const interpolateTypes = (x, y) => { if (x === y || !isInterpolatable(x) || !isInterpolatable(y)) { return interpolateImmediate(x, y); } if (typeof x === "function" || typeof y === "function") { return interpolateFunction(x, y); } if ( (typeof x === "object" && isPlainObject(x)) || (typeof y === "object" && isPlainObject(y)) ) { return interpolateObject(x, y); } return interpolate(x, y); }; // When the value is an array, attempt to sort by "key" so that animating nodes may be identified // based on "key" instead of index const keyData = (val) => { return Array.isArray(val) ? orderBy(val, "key") : val; }; const i = {}; const c = {}; let a = startValue; let b = endValue; let k; if (a === null || typeof a !== "object") { a = {}; } if (b === null || typeof b !== "object") { b = {}; } for (k in b) { if (k in a) { i[k] = interpolateTypes(keyData(a[k]), keyData(b[k])); } else { c[k] = b[k]; } } return function (t) { for (k in i) { c[k] = i[k](t); } return c; }; }; export const interpolateString = function (a, b) { const format = (val) => { return typeof val === "string" ? val.replace(/,/g, "") : val; }; return interpolate(format(a), format(b)); }; /** * By default, the list of interpolators used by `d3.interpolate` has a few * downsides: * * - `null` values get turned into 0. * - `undefined`, `function`, and some other value types get turned into NaN. * - Boolean types get turned into numbers, which probably will be meaningless * to whatever is consuming them. * - It tries to interpolate between identical start and end values, doing * unnecessary calculations that sometimes result in floating point rounding * errors. * * If only the default interpolators are used, `VictoryAnimation` will happily * pass down NaN (and other bad) values as props to the wrapped component. * The component will then either use the incorrect values or complain that it * was passed props of the incorrect type. This custom interpolator is added * using the `d3.interpolators` API, and prevents such cases from happening * for most values. * * @param {any} a - Start value. * @param {any} b - End value. * @returns {Function|undefined} An interpolation function, if necessary. */ export const victoryInterpolator = function (a: T, b: T): (t: number) => T { // If the values are strictly equal, or either value is not interpolatable, // just use either the start value `a` or end value `b` at every step, as // there is no reasonable in-between value. if (a === b || !isInterpolatable(a) || !isInterpolatable(b)) { return interpolateImmediate(a, b); } if (typeof a === "function" || typeof b === "function") { return interpolateFunction(a, b); } if (isPlainObject(a) || isPlainObject(b)) { // @ts-expect-error These generics are tough, but they work :) return interpolateObject(a, b); } if (typeof a === "string" || typeof b === "string") { return interpolateString(a, b); } // @ts-expect-error These generics are tough, but they work :) return interpolate(a, b); }; ================================================ FILE: packages/victory-core/src/victory-animation/victory-animation.tsx ================================================ import React from "react"; import * as d3Ease from "victory-vendor/d3-ease"; import { victoryInterpolator } from "./util"; import TimerContext from "../victory-util/timer-context"; /** * Single animation object to interpolate */ export type AnimationStyle = { [key: string]: string | number }; /** * Animation styles to interpolate */ export type AnimationData = AnimationStyle | AnimationStyle[]; export type AnimationEasing = | "back" | "backIn" | "backOut" | "backInOut" | "bounce" | "bounceIn" | "bounceOut" | "bounceInOut" | "circle" | "circleIn" | "circleOut" | "circleInOut" | "linear" | "linearIn" | "linearOut" | "linearInOut" | "cubic" | "cubicIn" | "cubicOut" | "cubicInOut" | "elastic" | "elasticIn" | "elasticOut" | "elasticInOut" | "exp" | "expIn" | "expOut" | "expInOut" | "poly" | "polyIn" | "polyOut" | "polyInOut" | "quad" | "quadIn" | "quadOut" | "quadInOut" | "sin" | "sinIn" | "sinOut" | "sinInOut"; export interface VictoryAnimationProps { children: (style: AnimationStyle, info: AnimationInfo) => React.ReactElement; duration?: number; easing?: AnimationEasing; delay?: number; onEnd?: () => void; data: AnimationData; } export interface VictoryAnimationState { data: AnimationStyle; animationInfo: AnimationInfo; } export interface AnimationInfo { progress: number; animating: boolean; terminating?: boolean; } export interface VictoryAnimation { context: React.ContextType; } /** d3-ease changed the naming scheme for ease from "linear" -> "easeLinear" etc. */ const formatAnimationName = (name: AnimationEasing) => { const capitalizedName = name.charAt(0).toUpperCase() + name.slice(1); return `ease${capitalizedName}`; }; const DEFAULT_DURATION = 1000; export const VictoryAnimation = ({ duration = DEFAULT_DURATION, easing = "quadInOut", delay = 0, data, children, onEnd, }: VictoryAnimationProps) => { const [state, setState] = React.useState({ data: Array.isArray(data) ? data[0] : data, animationInfo: { progress: 0, animating: false, }, }); const timer = React.useContext(TimerContext).animationTimer; const queue = React.useRef( Array.isArray(data) ? data.slice(1) : [], ); const interpolator = React.useRef AnimationStyle)>( null, ); const loopID = React.useRef(undefined); const ease = d3Ease[formatAnimationName(easing)]; React.useEffect(() => { // Length check prevents us from triggering `onEnd` in `traverseQueue`. if (queue.current.length) { traverseQueue(); } // Clean up the animation loop return () => { if (loopID.current) { timer.unsubscribe(loopID.current); } else { timer.stop(); } }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); React.useEffect(() => { // If the previous animation didn't finish, force it to complete before starting a new one if ( interpolator.current && state.animationInfo && state.animationInfo.progress < 1 ) { setState({ data: interpolator.current(1), animationInfo: { progress: 1, animating: false, terminating: true, }, }); } else { // Cancel existing loop if it exists timer.unsubscribe(loopID.current); // Set the tween queue to the new data queue.current = Array.isArray(data) ? data : [data]; // Start traversing the tween queue traverseQueue(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [data]); const traverseQueue = () => { if (queue.current.length) { const nextData = queue.current[0]; // Compare cached version to next props interpolator.current = victoryInterpolator(state.data, nextData); // Reset step to zero if (delay) { setTimeout(() => { loopID.current = timer.subscribe(functionToBeRunEachFrame, duration); }, delay); } else { loopID.current = timer.subscribe(functionToBeRunEachFrame, duration); } } else if (onEnd) { onEnd(); } }; const functionToBeRunEachFrame = (elapsed: number) => { if (!interpolator.current) return; // Step can generate imprecise values, sometimes greater than 1 // if this happens set the state to 1 and return, cancelling the timer const step = duration ? elapsed / duration : 1; if (step >= 1) { setState({ data: interpolator.current(1), animationInfo: { progress: 1, animating: false, terminating: true, }, }); if (loopID.current) { timer.unsubscribe(loopID.current); } queue.current.shift(); traverseQueue(); return; } // If we're not at the end of the timer, set the state by passing // current step value that's transformed by the ease function to the // interpolator, which is cached for performance whenever props are received setState({ data: interpolator.current(ease(step)), animationInfo: { progress: step, animating: step < 1, }, }); }; return children(state.data, state.animationInfo); }; ================================================ FILE: packages/victory-core/src/victory-clip-container/victory-clip-container.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import uniqueId from "lodash/uniqueId"; import * as Helpers from "../victory-util/helpers"; import * as UserProps from "../victory-util/user-props"; import { ClipPath } from "../victory-primitives/clip-path"; import { Circle } from "../victory-primitives/circle"; import { Rect } from "../victory-primitives/rect"; import { BlockProps } from "../victory-theme/victory-theme"; import { OriginType } from "../victory-label/victory-label"; export interface VictoryClipContainerProps { "aria-label"?: string; children?: React.ReactElement | React.ReactElement[]; circleComponent?: React.ReactElement; className?: string; clipHeight?: number; clipId?: number | string; clipPadding?: BlockProps; clipPathComponent?: React.ReactElement; clipWidth?: number; events?: React.DOMAttributes; groupComponent?: React.ReactElement; origin?: OriginType; polar?: boolean; radius?: number; rectComponent?: React.ReactElement; translateX?: number; translateY?: number; } interface VictoryClipContainerState { clipId?: number | string; } export class VictoryClipContainer extends React.Component< VictoryClipContainerProps, VictoryClipContainerState > { static displayName = "VictoryClipContainer"; static role = "container"; static defaultProps = { circleComponent: , rectComponent: , clipPathComponent: , groupComponent: , }; public clipId: VictoryClipContainerProps["clipId"]; constructor(props: VictoryClipContainerProps) { super(props); this.state = { clipId: props?.clipId, }; } // The clipId state is used to prevent hydration mismatches between the server and client (issue #2941). // A better alternative would be to utilize React 18's useId hook instead of uniqueId, which would avoid needing state for this purpose. // However, this component currently supports React 16 at the time of writing, so this workaround is necessary. componentDidMount() { if (!this.state.clipId) { this.setState({ clipId: uniqueId("victory-clip-") }); } } calculateAttributes(props) { const { polar, origin, clipWidth = 0, clipHeight = 0, translateX = 0, translateY = 0, } = props; const clipPadding = Helpers.getPadding(props.clipPadding); const radius = props.radius || Helpers.getRadius(props); return { x: (polar ? origin.x : translateX) - clipPadding.left, y: (polar ? origin.y : translateY) - clipPadding.top, width: Math.max( (polar ? radius : clipWidth) + clipPadding.left + clipPadding.right, 0, ), height: Math.max( (polar ? radius : clipHeight) + clipPadding.top + clipPadding.bottom, 0, ), }; } renderClippedGroup(props, clipId) { const userProps = UserProps.getSafeUserProps(props); const { style, events, transform, children, className, groupComponent, tabIndex, } = props; const clipComponent = this.renderClipComponent(props, clipId); const groupProps = Object.assign( { className, style, transform, key: `clipped-group-${clipId}`, clipPath: `url(#${clipId})`, }, events, ); return React.cloneElement( groupComponent, { ...groupProps, tabIndex, ...userProps }, [clipComponent, ...React.Children.toArray(children)], ); } renderGroup(props) { const { style, events, transform, children, className, groupComponent, tabIndex, } = props; return React.cloneElement( groupComponent, Object.assign( { className, style, transform, "aria-label": props["aria-label"], tabIndex, }, events, ), children, ); } renderClipComponent(props, clipId) { const { polar, origin, clipWidth = 0, clipHeight = 0, translateX = 0, translateY = 0, circleComponent, rectComponent, clipPathComponent, } = props; const { top, bottom, left, right } = Helpers.getPadding(props.clipPadding); let child; if (polar) { const radius = props.radius || Helpers.getRadius(props); const circleProps = { r: Math.max(radius + left + right, radius + top + bottom, 0), cx: origin.x - left, cy: origin.y - top, }; child = React.cloneElement(circleComponent, circleProps); } else { const rectProps = { x: translateX - left, y: translateY - top, width: Math.max(clipWidth + left + right, 0), height: Math.max(clipHeight + top + bottom, 0), }; child = React.cloneElement(rectComponent, rectProps); } return React.cloneElement( clipPathComponent, Object.assign({ key: `clip-path-${clipId}` }, props, { clipId }), child, ); } getClipValue(props, axis) { const clipValues = { x: props.clipWidth, y: props.clipHeight }; if (clipValues[axis] !== undefined) { return clipValues[axis]; } const range = Helpers.getRange(props, axis); return range ? Math.abs(range[0] - range[1]) || undefined : undefined; } getTranslateValue(props, axis) { const translateValues = { x: props.translateX, y: props.translateY }; if (translateValues[axis] !== undefined) { return translateValues[axis]; } const range = Helpers.getRange(props, axis); return range ? Math.min(...range) : undefined; } render() { const clipHeight = this.getClipValue(this.props, "y"); const clipWidth = this.getClipValue(this.props, "x"); if (clipWidth === undefined || clipHeight === undefined) { return this.renderGroup(this.props); } const translateX = this.getTranslateValue(this.props, "x"); const translateY = this.getTranslateValue(this.props, "y"); const clipProps = defaults({}, this.props, { clipHeight, clipWidth, translateX, translateY, }); return this.renderClippedGroup(clipProps, this.state.clipId); } } ================================================ FILE: packages/victory-core/src/victory-container/victory-container.test.tsx ================================================ import React from "react"; import { VictoryContainer } from "./victory-container"; import { fireEvent, render } from "@testing-library/react"; describe("components/victory-container", () => { it("renders an svg with a role of img", () => { const { container } = render(); const output = container.querySelector("svg")!; expect(output.getAttribute("role")).toContain("img"); }); it("renders an svg with a custom role", () => { const { container } = render(); expect(container.querySelector("svg")!.getAttribute("role")).toEqual( "presentation", ); }); it("renders an svg with a title node", () => { const { container } = render( , ); expect(container.querySelector("title")).toMatchInlineSnapshot(` Victory Chart `); }); it("renders an svg with a desc node", () => { const { container } = render( , ); expect(container.querySelector("desc")).toMatchInlineSnapshot(` description `); }); it("renders an svg with an aria-describedby attribute", () => { const { container } = render( , ); const describedElement = container.querySelector( `svg[aria-describedby~="testid"]`, ); expect(describedElement).toBeDefined(); }); it("renders an svg with an aria-labelledby attribute", () => { const { container } = render( , ); const describedElement = container.querySelector( `svg[aria-labelledby~="testid"]`, ); expect(describedElement).toBeDefined(); }); it("renders an svg with the correct viewbox", () => { const width = 300; const height = 300; const { container } = render( , ); const svg = container.querySelector("svg")!; const viewBoxValue = `0 0 ${width} ${height}`; expect(svg.getAttribute("viewBox")).toEqual(viewBoxValue); }); it("attaches an event to the container", () => { const clickHandler = jest.fn(); const { container } = render( , ); const svg = container.querySelector("svg")!; fireEvent.click(svg); expect(clickHandler).toBeCalled(); }); }); ================================================ FILE: packages/victory-core/src/victory-container/victory-container.tsx ================================================ import React, { useRef } from "react"; import uniqueId from "lodash/uniqueId"; import { Portal } from "../victory-portal/portal"; import * as UserProps from "../victory-util/user-props"; import { OriginType } from "../victory-label/victory-label"; import { D3Scale } from "../types/prop-types"; import { VictoryThemeDefinition } from "../victory-theme/types"; import { mergeRefs } from "../victory-util"; import { PortalOutlet } from "../victory-portal/portal-outlet"; import { PortalProvider } from "../victory-portal/portal-context"; export interface VictoryContainerProps { "aria-describedby"?: string; "aria-labelledby"?: string; children?: React.ReactElement | React.ReactElement[]; className?: string; containerId?: number | string; containerRef?: React.Ref; desc?: string; events?: React.DOMAttributes; height?: number; name?: string; origin?: OriginType; polar?: boolean; portalComponent?: React.ReactElement; portalZIndex?: number; preserveAspectRatio?: string; responsive?: boolean; role?: string; scale?: { x?: D3Scale; y?: D3Scale; }; style?: React.CSSProperties; tabIndex?: number; theme?: VictoryThemeDefinition; title?: string; width?: number; // Props defined by the Open UI Automation (OUIA) 1.0-RC spec // See https://ouia.readthedocs.io/en/latest/README.html#ouia-component ouiaId?: number | string; ouiaSafe?: boolean; ouiaType?: string; } const defaultProps = { className: "VictoryContainer", portalComponent: , portalZIndex: 99, responsive: true, role: "img", }; export function useVictoryContainer( initialProps: TProps, ) { const props = { ...defaultProps, ...initialProps }; const { title, desc, width, height, responsive } = props; const localContainerRef = useRef(null); // Generated ID stored in ref because it needs to persist across renders const generatedId = useRef(uniqueId("victory-container-")); const containerId = props.containerId ?? generatedId.current; const getIdForElement = (elName: string) => `${containerId}-${elName}`; const userProps = UserProps.getSafeUserProps(props); const dimensions = responsive ? { width: "100%", height: "100%" } : { width, height }; const viewBox = responsive ? `0 0 ${width} ${height}` : undefined; const preserveAspectRatio = responsive ? props.preserveAspectRatio : undefined; const ariaLabelledBy = [title && getIdForElement("title"), props["aria-labelledby"]] .filter(Boolean) .join(" ") || undefined; const ariaDescribedBy = [desc && getIdForElement("desc"), props["aria-describedby"]] .filter(Boolean) .join(" ") || undefined; const titleId = getIdForElement("title"); const descId = getIdForElement("desc"); return { ...props, titleId, descId, dimensions, viewBox, preserveAspectRatio, ariaLabelledBy, ariaDescribedBy, userProps, localContainerRef, }; } export const VictoryContainer = (initialProps: VictoryContainerProps) => { const { role, title, desc, children, className, portalZIndex, portalComponent, width, height, style, tabIndex, responsive, events, ouiaId, ouiaSafe, ouiaType, dimensions, ariaDescribedBy, ariaLabelledBy, viewBox, preserveAspectRatio, userProps, titleId, descId, containerRef, localContainerRef, } = useVictoryContainer(initialProps); React.useEffect(() => { if (!events?.onWheel) return; const handleWheel = (e: WheelEvent) => e.preventDefault(); const container = localContainerRef?.current; container?.addEventListener("wheel", handleWheel); return () => { container?.removeEventListener("wheel", handleWheel); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return (
{title ? {title} : null} {desc ? {desc} : null} {children}
); }; VictoryContainer.role = "container"; ================================================ FILE: packages/victory-core/src/victory-label/victory-label.test.tsx ================================================ import React from "react"; import { screen, fireEvent, render } from "@testing-library/react"; import { SVGWrapper } from "../../../../test/helpers"; import { Log } from "../victory-util"; import { VictoryLabel } from "./victory-label"; describe("components/victory-label", () => { it("accepts user props", () => { render( , { wrapper: SVGWrapper }, ); expect(screen.getByTestId("victory-label")).toBeDefined(); expect(screen.getByLabelText("test-aria-label")).toBeDefined(); }); it("has expected content with render", () => { const { container } = render(, { wrapper: SVGWrapper, }); expect(container.querySelector("tspan")!.innerHTML).toMatchInlineSnapshot( `"such text, wow"`, ); }); it("sets dx and dy for text element", () => { const { container } = render( , { wrapper: SVGWrapper }, ); const output = container.querySelector("text")!; expect(output.getAttribute("dx")).toEqual("30"); // dy = props.dy + (capHeight(0.71) / 2 + (0.5 - length(1) / 2) * lineHeight(1)) * fontSize(14); expect(output.getAttribute("dy")).toBeNull(); }); it("sets x and y for text element", () => { const { container } = render( // @ts-expect-error "type string is not assignable to number" , { wrapper: SVGWrapper }, ); const output = container.querySelector("text")!; expect(output.getAttribute("x")).toEqual("100%"); expect(parseFloat(output.getAttribute("y")!)).toEqual(34.97); }); it("has a transform property that rotates the text to match the labelAngle getAttribute", () => { const { container } = render( , { wrapper: SVGWrapper }, ); const output = container.querySelector("text")!; expect(output.getAttribute("transform")).toContain("rotate(46"); }); it("accepts the angle getAttribute as a function", () => { const { container } = render( 46} text="such text, wow" />, { wrapper: SVGWrapper }, ); const output = container.querySelector("text")!; expect(output.getAttribute("transform")).toContain("rotate(46"); }); it("strips px from fontSize", () => { // This suppresses the warning about the fontSize being a string. // TODO: For some reason this test file doesn't respect the eslint override for this rule. jest.spyOn(console, "warn").mockImplementation(() => {}); const { container } = render( props.style.fontSize} />, { wrapper: SVGWrapper }, ); const output = container.querySelector("text")!; expect(output.getAttribute("data-font-size")).toEqual("10"); }); it("uses a default fontSize when an invalid fontSize is given", () => { // This suppresses the console warning for invalid fontSize prop // TODO: For some reason this test file doesn't respect the eslint override for this rule. jest.spyOn(Log, "warn").mockImplementation(() => {}); const { container } = render( , { wrapper: SVGWrapper }, ); const output = container.querySelector("tspan")!; expect(output.getAttribute("style")).toContain("font-size: 14px"); }); it("renders an array of text as seperate tspans", () => { const { container } = render( , { wrapper: SVGWrapper }, ); const output = container.querySelectorAll("tspan"); expect(output.length).toEqual(3); }); it("renders splits newlines into tspans", () => { const { container } = render(, { wrapper: SVGWrapper, }); const output = container.querySelectorAll("tspan"); expect(output.length).toEqual(3); }); it("renders title and desc if provided ", () => { const { container } = render( , { wrapper: SVGWrapper }, ); const { container: container2 } = render( , { wrapper: SVGWrapper }, ); const title = container.querySelectorAll("title"); expect(title.length).toEqual(1); const desc = container.querySelectorAll("desc"); expect(desc.length).toEqual(1); const noTitle = container2.querySelectorAll("title"); expect(noTitle.length).toEqual(0); const noDesc = container2.querySelectorAll("desc"); expect(noDesc.length).toEqual(0); }); it("renders tspan styles independently when `style` is an array", () => { const fill = ["red", "green", "blue"]; const { container } = render( , { wrapper: SVGWrapper }, ); const output = container.querySelectorAll("tspan"); output.forEach((tspan, index) => { expect(tspan.getAttribute("style")).toContain(`fill: ${fill[index]}`); }); }); describe("event handling", () => { it("attaches an to the parent object", () => { const clickHandler = jest.fn(); const { container } = render( , { wrapper: SVGWrapper }, ); fireEvent.click(container.querySelector("text")!); expect(clickHandler).toHaveBeenCalled(); }); }); it("renders 'tspan' elements inline when `inline` getAttribute is passed", () => { const { container } = render( , { wrapper: SVGWrapper }, ); const output = container.querySelectorAll("tspan"); output.forEach((tspan) => { // passing `inline` sets x and dy to undefined expect(tspan.getAttribute("x")).toBeNull(); expect( tspan.getAttribute("dy") === null || tspan.getAttribute("dy") === "0", ).toBeTruthy(); expect(tspan.getAttribute("dx")).toEqual("5"); }); }); it("passes lineHeight as an array if provided", () => { const lineHeight = [1, 2, 3]; const expectedDy = [0, 21, 35]; const { container } = render( , { wrapper: SVGWrapper }, ); const output = container.querySelectorAll("tspan"); output.forEach((tspan, index) => { /* to calculate dy: ((this.lineHeight[i] + (this.lineHeight[i - 1] || this.lineHeight[0])) / 2) */ expect(parseInt(tspan.getAttribute("dy")!)).toEqual(expectedDy[index]); }); }); it("defaults lineHeight to 1 if an empty array is provided for lineHeight", () => { const expectedDy = [0, 14, 14, 14]; const { container } = render( , { wrapper: SVGWrapper }, ); const output = container.querySelectorAll("tspan"); output.forEach((tspan, index) => { expect(parseInt(tspan.getAttribute("dy")!)).toEqual(expectedDy[index]); }); }); it("defaults style to `defaultStyles` if an empty array is provided for `style`", () => { const { container } = render( , { wrapper: SVGWrapper }, ); expect( container.querySelector("tspan")!.getAttribute("style"), ).toMatchInlineSnapshot( `"fill: #252525; font-size: 14px; font-family: 'Gill Sans', 'Gill Sans MT', 'Ser­avek', 'Trebuchet MS', sans-serif; stroke: transparent;"`, ); }); it("passes id if provided as a string", () => { const { container } = render( , { wrapper: SVGWrapper }, ); const output = container.querySelectorAll("text"); output.forEach((text) => { expect(text.getAttribute("id")).toEqual("my-custom-id"); }); }); it("passes id if provided as a number", () => { const { container } = render( , { wrapper: SVGWrapper }, ); const output = container.querySelectorAll("text"); output.forEach((text) => { expect(text.getAttribute("id")).toEqual("12345"); }); }); it("runs function if id provided as a function", () => { const { container } = render( `created-in-function-${Math.random()}`} />, { wrapper: SVGWrapper }, ); const output = container.querySelectorAll("text"); output.forEach((text) => { expect(text.getAttribute("id")).toMatch(/^created-in-function-[\d\.]+$/); }); }); }); ================================================ FILE: packages/victory-core/src/victory-label/victory-label.tsx ================================================ /* eslint no-magic-numbers: ["error", { "ignore": [-0.5, 0.5, 0, 1, 2] }]*/ import React from "react"; import defaults from "lodash/defaults"; import isEmpty from "lodash/isEmpty"; import { VictoryPortal } from "../victory-portal/victory-portal"; import { Rect } from "../victory-primitives/rect"; import { Text } from "../victory-primitives/text"; import { TSpan } from "../victory-primitives/tspan"; import * as Helpers from "../victory-util/helpers"; import * as LabelHelpers from "../victory-util/label-helpers"; import * as Log from "../victory-util/log"; import * as Style from "../victory-util/style"; import * as TextSize from "../victory-util/textsize"; import * as UserProps from "../victory-util/user-props"; import { NumberOrCallback, StringOrCallback, StringOrNumberOrCallback, } from "../types/callbacks"; import { ValueOrAccessor } from "../types/prop-types"; import { PaddingProps, VerticalAnchorType, VictoryLabelStyleObject, } from "../victory-theme/victory-theme"; export type TextAnchorType = "start" | "middle" | "end" | "inherit"; export type OriginType = { x: number; y: number }; export type LabelOrientationType = "parallel" | "perpendicular" | "vertical"; export interface VictoryLabelProps { angle?: StringOrNumberOrCallback; ariaLabel?: StringOrCallback; backgroundComponent?: React.ReactElement; backgroundStyle?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; backgroundPadding?: PaddingProps | PaddingProps[]; capHeight?: StringOrNumberOrCallback; children?: StringOrNumberOrCallback; className?: string; datum?: Record; data?: any[]; desc?: string; direction?: string; disableInlineStyles?: boolean; events?: React.DOMAttributes; groupComponent?: React.ReactElement; id?: StringOrNumberOrCallback; inline?: boolean; labelPlacement?: LabelOrientationType; lineHeight?: StringOrNumberOrCallback | (string | number)[]; origin?: OriginType; polar?: boolean; renderInPortal?: boolean; style?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; tabIndex?: NumberOrCallback; text?: string[] | StringOrNumberOrCallback; textComponent?: React.ReactElement; textAnchor?: ValueOrAccessor; title?: string; transform?: ValueOrAccessor; tspanComponent?: React.ReactElement; verticalAnchor?: ValueOrAccessor; x?: number; y?: number; dx?: StringOrNumberOrCallback; dy?: StringOrNumberOrCallback; } const defaultStyles = { fill: "#252525", fontSize: 14, fontFamily: "'Gill Sans', 'Gill Sans MT', 'Ser­avek', 'Trebuchet MS', sans-serif", stroke: "transparent", }; const getPosition = (props, dimension) => { if (!props.datum) { return 0; } const scaledPoint = Helpers.scalePoint(props, props.datum); return scaledPoint[dimension]; }; const getFontSize = (style) => { const baseSize = style && style.fontSize; if (typeof baseSize === "number") { return baseSize; } else if (baseSize === undefined || baseSize === null) { return defaultStyles.fontSize; } else if (typeof baseSize === "string") { const fontSize = Number(baseSize.replace("px", "")); if (!isNaN(fontSize)) { return fontSize; } Log.warn("fontSize should be expressed as a number of pixels"); return defaultStyles.fontSize; } return defaultStyles.fontSize; }; const getSingleValue = (prop: T | T[], index = 0): T => { return Array.isArray(prop) ? prop[index] || prop[0] : prop; }; const shouldUseMultilineBackgrounds = (props) => { const { backgroundStyle, backgroundPadding } = props; return ( (Array.isArray(backgroundStyle) && !isEmpty(backgroundStyle)) || (Array.isArray(backgroundPadding) && !isEmpty(backgroundPadding)) ); }; const getStyles = (style, props) => { if (props.disableInlineStyles) { const baseStyles = Helpers.evaluateStyle(style, props); return { // Font size is necessary to calculate the y position of the label fontSize: getFontSize(baseStyles), }; } const getSingleStyle = (s) => { const baseStyles = Helpers.evaluateStyle( s ? defaults({}, s, defaultStyles) : defaultStyles, props, ); return Object.assign({}, baseStyles, { fontSize: getFontSize(baseStyles) }); }; return Array.isArray(style) && !isEmpty(style) ? style.map((s) => getSingleStyle(s)) : getSingleStyle(style); }; const getBackgroundStyles = (style, props) => { if (!style) { return undefined; } return Array.isArray(style) && !isEmpty(style) ? style.map((s) => Helpers.evaluateStyle(s, props)) : Helpers.evaluateStyle(style, props); }; const getBackgroundPadding = (props) => { if (props.backgroundPadding && Array.isArray(props.backgroundPadding)) { return props.backgroundPadding.map((backgroundPadding) => { const padding = Helpers.evaluateProp(backgroundPadding, props); return Helpers.getPadding(padding); }); } const padding = Helpers.evaluateProp(props.backgroundPadding, props); return Helpers.getPadding(padding); }; const getLineHeight = (props) => { const lineHeight = Helpers.evaluateProp(props.lineHeight, props); if (Array.isArray(lineHeight)) { return isEmpty(lineHeight) ? [1] : lineHeight; } return lineHeight; }; const getContent = (text, props) => { if (text === undefined || text === null) { return undefined; } if (Array.isArray(text)) { return text.map((line) => Helpers.evaluateProp(line, props)); } const child = Helpers.evaluateProp(text, props); if (child === undefined || child === null) { return undefined; } return Array.isArray(child) ? child : `${child}`.split("\n"); }; const getDy = (props, verticalAnchor, lineHeight) => { const dy = props.dy ? Helpers.evaluateProp(props.dy, props) : 0; const length = props.inline ? 1 : props.text.length; const capHeight = Helpers.evaluateProp(props.capHeight, props); const anchor = verticalAnchor ? Helpers.evaluateProp(verticalAnchor, props) : "middle"; const fontSizes = [...Array(length).keys()].map( (i) => getSingleValue(props.style, i).fontSize, ); const lineHeights = [...Array(length).keys()].map((i) => getSingleValue(lineHeight, i), ); if (anchor === "start") { return dy + (capHeight / 2 + lineHeights[0] / 2) * fontSizes[0]; } else if (props.inline) { return anchor === "end" ? dy + (capHeight / 2 - lineHeights[0] / 2) * fontSizes[0] : dy + (capHeight / 2) * fontSizes[0]; } else if (length === 1) { return anchor === "end" ? dy + (capHeight / 2 + (0.5 - length) * lineHeights[0]) * fontSizes[0] : dy + (capHeight / 2 + (0.5 - length / 2) * lineHeights[0]) * fontSizes[0]; } const allHeights = [...Array(length).keys()].reduce((memo, i) => { return ( memo + ((capHeight / 2 + (0.5 - length) * lineHeights[i]) * fontSizes[i]) / length ); }, 0); return anchor === "end" ? dy + allHeights : dy + allHeights / 2 + (capHeight / 2) * lineHeights[length - 1] * fontSizes[length - 1]; }; const getTransform = (props, x, y) => { const { polar } = props; const style = getSingleValue(props.style); const defaultAngle = polar ? LabelHelpers.getPolarAngle(props) : 0; const baseAngle = style.angle === undefined ? Helpers.evaluateProp(props.angle, props) : style.angle; const angle = baseAngle === undefined ? defaultAngle : baseAngle; const transform = props.transform || style.transform; const transformPart = transform && Helpers.evaluateProp(transform, props); const rotatePart = angle && { rotate: [angle, x, y] }; return transformPart || angle ? Style.toTransformString(transformPart, rotatePart) : undefined; }; const getXCoordinate = (calculatedProps, labelSizeWidth) => { const { direction, textAnchor, x, dx } = calculatedProps; if (direction === "rtl") { return x - labelSizeWidth; } switch (textAnchor) { case "middle": return Math.round(x - labelSizeWidth / 2); case "end": return Math.round(x - labelSizeWidth); default: // start return x + (dx || 0); } }; const getYCoordinate = (calculatedProps, textHeight) => { const { verticalAnchor, y, originalDy = 0 } = calculatedProps; const offset = y + originalDy; switch (verticalAnchor) { case "start": return Math.floor(offset); case "end": return Math.ceil(offset - textHeight); default: // middle return Math.floor(offset - textHeight / 2); } }; const getFullBackground = (calculatedProps, tspanValues) => { const { dx = 0, transform, backgroundComponent, backgroundStyle, inline, backgroundPadding, capHeight, } = calculatedProps; const textSizes = tspanValues.map((tspan) => { return tspan.textSize; }); const height = inline ? Math.max(...textSizes.map((size) => size.height)) : textSizes.reduce((memo, size, i) => { const capHeightAdjustment = i ? 0 : capHeight / 2; return ( memo + size.height * (tspanValues[i].lineHeight - capHeightAdjustment) ); }, 0); const width = inline ? textSizes.reduce((memo, size, index) => { const offset = index ? dx : 0; return memo + size.width + offset; }, 0) : Math.max(...textSizes.map((size) => size.width)); const xCoordinate = getXCoordinate(calculatedProps, width); const yCoordinate = getYCoordinate(calculatedProps, height); const backgroundProps = { key: "background", height: height + backgroundPadding.top + backgroundPadding.bottom, style: backgroundStyle, transform, width: width + backgroundPadding.left + backgroundPadding.right, x: inline ? xCoordinate - backgroundPadding.left : xCoordinate + dx - backgroundPadding.left, y: yCoordinate, }; return React.cloneElement( backgroundComponent, defaults({}, backgroundComponent.props, backgroundProps), ); }; const getInlineXOffset = (calculatedProps, textElements, index) => { const { textAnchor } = calculatedProps; const widths = textElements.map((t) => t.widthWithPadding); const totalWidth = widths.reduce((memo, width) => memo + width, 0); const centerOffset = -totalWidth / 2; switch (textAnchor) { case "start": return widths.reduce( (memo, width, i) => (i < index ? memo + width : memo), 0, ); case "end": return widths.reduce( (memo, width, i) => (i > index ? memo - width : memo), 0, ); default: // middle return widths.reduce((memo, width, i) => { const offsetWidth = i < index ? width : 0; return i === index ? memo + width / 2 : memo + offsetWidth; }, centerOffset); } }; const getChildBackgrounds = (calculatedProps, tspanValues) => { const { dy, dx, transform, backgroundStyle, backgroundPadding, backgroundComponent, inline, y, } = calculatedProps; const textElements = tspanValues.map((current, i) => { const previous = getSingleValue(tspanValues, i - 1); const labelSize = current.textSize; const totalLineHeight = current.fontSize * current.lineHeight; const textHeight = Math.ceil(totalLineHeight); const padding = getSingleValue(backgroundPadding, i); const prevPadding = getSingleValue(backgroundPadding, i - 1); const xOffset = inline ? dx || 0 : 0; const childDy = i && !inline ? previous.fontSize * previous.lineHeight + prevPadding.top + prevPadding.bottom : dy - totalLineHeight * 0.5 - (current.fontSize - current.capHeight); return { textHeight, labelSize, heightWithPadding: textHeight + padding.top + padding.bottom, widthWithPadding: labelSize.width + padding.left + padding.right + xOffset, y, fontSize: current.fontSize, dy: childDy, }; }); return textElements.map((textElement, i) => { const xCoordinate = getXCoordinate( calculatedProps, textElement.labelSize.width, ); const yCoordinate = textElements.slice(0, i + 1).reduce((prev, curr) => { return prev + curr.dy; }, y); const padding = getSingleValue(backgroundPadding, i); const height = textElement.heightWithPadding; const xCoord = inline ? getInlineXOffset(calculatedProps, textElements, i) + xCoordinate - padding.left : xCoordinate; const yCoord = inline ? getYCoordinate(calculatedProps, height) - padding.top : yCoordinate; const backgroundProps = { key: `tspan-background-${i}`, height, style: getSingleValue(backgroundStyle, i), width: textElement.widthWithPadding, transform, x: xCoord - padding.left, y: yCoord, }; return React.cloneElement( backgroundComponent, defaults({}, backgroundComponent.props, backgroundProps), ); }); }; const getBackgroundElement = (calculatedProps, tspanValues) => { return shouldUseMultilineBackgrounds(calculatedProps) ? getChildBackgrounds(calculatedProps, tspanValues) : getFullBackground(calculatedProps, tspanValues); }; const calculateSpanDy = (tspanValues, i, calculatedProps) => { const current = getSingleValue(tspanValues, i); const previous = getSingleValue(tspanValues, i - 1); const previousHeight = previous.fontSize * previous.lineHeight; const currentHeight = current.fontSize * current.lineHeight; const previousCaps = previous.fontSize - previous.capHeight; const currentCaps = current.fontSize - current.capHeight; const textHeight = previousHeight - previous.fontSize / 2 + current.fontSize / 2 - previousHeight / 2 + currentHeight / 2 - currentCaps / 2 + previousCaps / 2; return shouldUseMultilineBackgrounds(calculatedProps) ? textHeight + current.backgroundPadding.top + previous.backgroundPadding.bottom : textHeight; }; const getTSpanDy = (tspanValues, calculatedProps, i) => { const { inline } = calculatedProps; const current = getSingleValue(tspanValues, i); if (i && !inline) { return calculateSpanDy(tspanValues, i, calculatedProps); } else if (inline) { return i === 0 ? current.backgroundPadding.top : undefined; } return current.backgroundPadding.top; }; const evaluateProps = (props) => { /* Potential evaluated props are 1) text 2) style 3) everything else */ const text = getContent(props.text, props); const style = getStyles(props.style, Object.assign({}, props, { text })); const backgroundStyle = getBackgroundStyles( props.backgroundStyle, Object.assign({}, props, { text, style }), ); const backgroundPadding = getBackgroundPadding( Object.assign({}, props, { text, style, backgroundStyle }), ); const id = Helpers.evaluateProp(props.id, props); return Object.assign({}, props, { backgroundStyle, backgroundPadding, style, text, id, }); }; const getCalculatedProps = (props: T) => { const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const style = getSingleValue(props.style!); const lineHeight = getLineHeight(props); const direction = props.direction ? Helpers.evaluateProp(props.direction, props) : "inherit"; const textAnchor = props.textAnchor ? Helpers.evaluateProp(props.textAnchor, props) : style.textAnchor || "start"; const verticalAnchor = props.verticalAnchor ? Helpers.evaluateProp(props.verticalAnchor, props) : style.verticalAnchor || "middle"; const dx = props.dx ? Helpers.evaluateProp(props.dx, props) : 0; const dy = getDy(props, verticalAnchor, lineHeight); const x = props.x !== undefined ? props.x : getPosition(props, "x"); const y = props.y !== undefined ? props.y : getPosition(props, "y"); const transform = getTransform(props, x, y); return Object.assign({}, props, { ariaLabel, lineHeight, direction, textAnchor, verticalAnchor, dx, dy, originalDy: Helpers.evaluateProp(props.dy, props), transform, x, y, }); }; const renderLabel = (calculatedProps, tspanValues) => { const { ariaLabel, inline, className, title, events, direction, text, textAnchor, dx, dy, transform, x, y, desc, id, tabIndex, tspanComponent, textComponent, } = calculatedProps; const userProps = UserProps.getSafeUserProps(calculatedProps); const textProps = { "aria-label": ariaLabel, key: "text", ...events, direction, dx, x, y: y + dy, transform, className, title, desc: Helpers.evaluateProp(desc, calculatedProps), tabIndex: Helpers.evaluateProp(tabIndex, calculatedProps), id, ...userProps, }; const tspans = text.map((line, i) => { const currentStyle = tspanValues[i].style; const tspanProps = { key: `${id}-key-${i}`, x: !inline ? x : undefined, dx: inline ? dx + tspanValues[i].backgroundPadding.left : dx, dy: getTSpanDy(tspanValues, calculatedProps, i), textAnchor: currentStyle.textAnchor || textAnchor, style: currentStyle, children: line, }; return React.cloneElement(tspanComponent, tspanProps); }); return React.cloneElement(textComponent, textProps, tspans); }; const defaultProps = { backgroundComponent: , groupComponent: , direction: "inherit", textComponent: , tspanComponent: , capHeight: 0.71, // Magic number from d3. lineHeight: 1, }; export const VictoryLabel: { role: string; defaultStyles: typeof defaultStyles; } & React.FC = (initialProps) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); if (props.text === null || props.text === undefined) { return null; } const calculatedProps = getCalculatedProps(props); const { text, style, capHeight, backgroundPadding, lineHeight } = calculatedProps; const tspanValues = (text as string[]).map((line, i) => { const currentStyle = getSingleValue(style, i); const capHeightPx = TextSize.convertLengthToPixels( `${capHeight}em`, currentStyle.fontSize as number, ); const currentLineHeight = getSingleValue(lineHeight, i); return { style: currentStyle, fontSize: currentStyle.fontSize || defaultStyles.fontSize, capHeight: capHeightPx, text: line, // TODO: This looks like a bug: textSize: TextSize.approximateTextSize(line, currentStyle), lineHeight: currentLineHeight, backgroundPadding: getSingleValue(backgroundPadding, i), }; }); const label = renderLabel(calculatedProps, tspanValues); if (props.backgroundStyle) { const backgroundElement = getBackgroundElement( calculatedProps, tspanValues, ); const children = [backgroundElement, label]; const backgroundWithLabel = React.cloneElement( props.groupComponent!, {}, children, ); return props.renderInPortal ? ( {backgroundWithLabel} ) : ( backgroundWithLabel ); } return props.renderInPortal ? {label} : label; }; VictoryLabel.displayName = "VictoryLabel"; VictoryLabel.role = "label"; VictoryLabel.defaultStyles = defaultStyles; ================================================ FILE: packages/victory-core/src/victory-portal/portal-context.tsx ================================================ import React from "react"; export interface PortalContextValue { addChild: (id: string, node: React.ReactElement) => void; removeChild: (id: string) => void; children: Map; } export const PortalContext = React.createContext< PortalContextValue | undefined >(undefined); PortalContext.displayName = "PortalContext"; export const usePortalContext = () => { const context = React.useContext(PortalContext); return context; }; export interface PortalProviderProps { children?: React.ReactNode; } export const PortalProvider = ({ children }: PortalProviderProps) => { const [portalChildren, setPortalChildren] = React.useState< Map >(new Map()); const addChild = React.useCallback( (id: string, element: React.ReactElement) => { setPortalChildren((prevChildren) => { const nextChildren = new Map(prevChildren); nextChildren.set(id, element); return nextChildren; }); }, [setPortalChildren], ); const removeChild = React.useCallback( (id: string) => { setPortalChildren((prevChildren) => { const nextChildren = new Map(prevChildren); nextChildren.delete(id); return nextChildren; }); }, [setPortalChildren], ); const contextValue: PortalContextValue = React.useMemo( () => ({ addChild, removeChild, children: portalChildren, }), [addChild, removeChild, portalChildren], ); return ( {children} ); }; ================================================ FILE: packages/victory-core/src/victory-portal/portal-outlet.tsx ================================================ import React from "react"; import { usePortalContext } from "./portal-context"; export interface PortalOutletProps { as: React.ReactElement; width?: number; height?: number; viewBox?: string; preserveAspectRatio?: string; style?: React.CSSProperties; children?: (children: React.ReactElement[]) => React.ReactNode | undefined; } export const PortalOutlet = ({ as: portalComponent, ...props }: PortalOutletProps) => { const portalContext = usePortalContext(); if (!portalContext) { return null; } const children = Array.from(portalContext.children.values()); return React.cloneElement(portalComponent, props, children); }; ================================================ FILE: packages/victory-core/src/victory-portal/portal.tsx ================================================ import React from "react"; export interface PortalProps { className?: string; height?: number; style?: React.CSSProperties; viewBox?: string; width?: number; } export const Portal = React.forwardRef( (props, ref) => { return ; }, ); ================================================ FILE: packages/victory-core/src/victory-portal/victory-portal.tsx ================================================ import React, { useState } from "react"; import defaults from "lodash/defaults"; import uniqueId from "lodash/uniqueId"; import * as Log from "../victory-util/log"; import * as Helpers from "../victory-util/helpers"; import { usePortalContext } from "./portal-context"; export interface VictoryPortalProps { children: React.ReactElement; groupComponent?: React.ReactElement; } const defaultProps: Partial = { groupComponent: , }; export const VictoryPortal = (initialProps: VictoryPortalProps) => { const props = { ...defaultProps, ...initialProps }; const [id] = useState(uniqueId()); const portalContext = usePortalContext(); if (!portalContext) { const msg = "`renderInPortal` is not supported outside of `VictoryContainer`. " + "Component will be rendered in place"; Log.warn(msg); } const children = Array.isArray(props.children) ? props.children[0] : props.children; const { groupComponent } = props; const childProps = (children && children.props) || {}; const standardProps = childProps.groupComponent ? { groupComponent, standalone: false } : {}; const newProps = defaults( standardProps, childProps, Helpers.omit(props, ["children", "groupComponent"]), { key: childProps.key ?? id }, ); const child = children && React.cloneElement(children, newProps); React.useEffect(() => { portalContext?.addChild(id, child); // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.children]); React.useEffect(() => { return () => portalContext?.removeChild(id); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return portalContext ? null : child; }; VictoryPortal.role = "portal"; ================================================ FILE: packages/victory-core/src/victory-primitives/arc.tsx ================================================ /* eslint no-magic-numbers: ["error", { "ignore": [0, 1, 2, 180] }]*/ import React from "react"; import defaults from "lodash/defaults"; import * as Helpers from "../victory-util/helpers"; import { VictoryCommonPrimitiveProps } from "../victory-util/common-props"; import { Path } from "./path"; export interface ArcProps extends VictoryCommonPrimitiveProps { closedPath?: boolean; cx?: number; cy?: number; datum?: any; endAngle?: number; pathComponent?: React.ReactElement; r?: number; startAngle?: number; type?: string; } const getArcPath = (props) => { const { cx, cy, r, startAngle, endAngle, closedPath } = props; // Always draw the path as two arcs so that complete circles may be rendered. const halfAngle = Math.abs(endAngle - startAngle) / 2 + startAngle; const x1 = cx + r * Math.cos(Helpers.degreesToRadians(startAngle)); const y1 = cy - r * Math.sin(Helpers.degreesToRadians(startAngle)); const x2 = cx + r * Math.cos(Helpers.degreesToRadians(halfAngle)); const y2 = cy - r * Math.sin(Helpers.degreesToRadians(halfAngle)); const x3 = cx + r * Math.cos(Helpers.degreesToRadians(endAngle)); const y3 = cy - r * Math.sin(Helpers.degreesToRadians(endAngle)); const largerArcFlag1 = halfAngle - startAngle <= 180 ? 0 : 1; const largerArcFlag2 = endAngle - halfAngle <= 180 ? 0 : 1; const arcStart = closedPath ? ` M ${cx}, ${cy} L ${x1}, ${y1}` : `M ${x1}, ${y1}`; const arc1 = `A ${r}, ${r}, 0, ${largerArcFlag1}, 0, ${x2}, ${y2}`; const arc2 = `A ${r}, ${r}, 0, ${largerArcFlag2}, 0, ${x3}, ${y3}`; const arcEnd = closedPath ? "Z" : ""; return `${arcStart} ${arc1} ${arc2} ${arcEnd}`; }; const evaluateProps = (props) => { /** * Potential evaluated props are: * `ariaLabel` * `desc` * `id` * `style` * `tabIndex` */ const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const desc = Helpers.evaluateProp(props.desc, props); const id = Helpers.evaluateProp(props.id, props); const style = Helpers.evaluateStyle( Object.assign({ stroke: "black", fill: "none" }, props.style), props, ); const tabIndex = Helpers.evaluateProp(props.tabIndex, props); return Object.assign({}, props, { ariaLabel, desc, id, style, tabIndex }); }; const defaultProps = { pathComponent: , role: "presentation", shapeRendering: "auto", }; export const Arc = (initialProps: ArcProps) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); return React.cloneElement(props.pathComponent!, { ...props.events, "aria-label": props.ariaLabel, d: getArcPath(props), style: props.style, desc: props.desc, tabIndex: props.tabIndex, className: props.className, role: props.role, shapeRendering: props.shapeRendering, transform: props.transform, clipPath: props.clipPath, }); }; ================================================ FILE: packages/victory-core/src/victory-primitives/background.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import * as Helpers from "../victory-util/helpers"; import { VictoryCommonPrimitiveProps } from "../victory-util/common-props"; import { Rect } from "./rect"; import { Circle } from "./circle"; export interface BackgroundProps extends VictoryCommonPrimitiveProps { circleComponent?: React.ReactElement; height?: number; rectComponent?: React.ReactElement; rx?: number; ry?: number; width?: number; x?: number; y?: number; } const evaluateProps = (props) => { /** * Potential evaluated prop is: * `id` */ const id = Helpers.evaluateProp(props.id, props); return Object.assign({}, props, { id }); }; const defaultProps = { circleComponent: , rectComponent: , role: "presentation", shapeRendering: "auto", }; export const Background = (initialProps: BackgroundProps) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); return props.polar ? React.cloneElement(props.circleComponent!, { ...props.events, style: props.style, role: props.role, shapeRendering: props.shapeRendering, cx: props.x, cy: props.y, r: props.height, className: props.className, }) : React.cloneElement(props.rectComponent!, { ...props.events, style: props.style, role: props.role, shapeRendering: props.shapeRendering, x: props.x, y: props.y, rx: props.rx, ry: props.ry, width: props.width, height: props.height, className: props.className, }); }; ================================================ FILE: packages/victory-core/src/victory-primitives/border.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import * as Helpers from "../victory-util/helpers"; import { VictoryCommonPrimitiveProps } from "../victory-util/common-props"; import { Rect } from "./rect"; export interface BorderProps extends VictoryCommonPrimitiveProps { width?: number; height?: number; rectComponent?: React.ReactElement; x?: number; y?: number; } const evaluateProps = (props) => { /** * Potential evaluated props are: * `ariaLabel` * `desc` * `id` * `style` * `tabIndex` */ const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const desc = Helpers.evaluateProp(props.desc, props); const id = Helpers.evaluateProp(props.id, props); const style = Helpers.evaluateStyle( Object.assign({ fill: "none" }, props.style), props, ); const tabIndex = Helpers.evaluateProp(props.tabIndex, props); return Object.assign({}, props, { ariaLabel, desc, id, style, tabIndex }); }; const defaultProps = { rectComponent: , role: "presentation", shapeRendering: "auto", }; export const Border = (initialProps: BorderProps) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); return React.cloneElement(props.rectComponent!, { ...props.events, "aria-label": props.ariaLabel, style: props.style, desc: props.desc, tabIndex: props.tabIndex, transform: props.transform, className: props.className, role: props.role, shapeRendering: props.shapeRendering, x: props.x, y: props.y, width: props.width, height: props.height, clipPath: props.clipPath, }); }; ================================================ FILE: packages/victory-core/src/victory-primitives/circle.tsx ================================================ import React, { forwardRef } from "react"; import { evaluateProp } from "../victory-util/helpers"; import { VictoryPrimitiveShapeProps } from "./types"; export const Circle = forwardRef( (props, ref) => { /* eslint-disable-next-line @typescript-eslint/no-unused-vars -- * origin conflicts with the SVG element's origin attribute */ const { desc, id, tabIndex, origin, ...rest } = props; const svgProps: React.SVGProps = { vectorEffect: "non-scaling-stroke", id: evaluateProp(id, props)?.toString(), tabIndex: evaluateProp(tabIndex, props), ...rest, }; return desc ? ( {desc} ) : ( ); }, ); ================================================ FILE: packages/victory-core/src/victory-primitives/clip-path.test.tsx ================================================ import { render } from "@testing-library/react"; import React from "react"; import { SVGWrapper } from "../../../../test/helpers"; import { ClipPath } from "./clip-path"; describe("victory-primitives/clip-path", () => { const baseProps = { clipId: 4, clipPadding: { top: 2, bottom: 2, left: 2, right: 2, }, clipHeight: 30, clipWidth: 20, translateX: 3, translateY: 8, }; it("should render a children", () => { const { container } = render( , { wrapper: SVGWrapper }, ); expect(container.querySelector("clipPath")).toMatchInlineSnapshot(` `); }); it("should render a clipPath with the passed id", () => { const { container } = render(, { wrapper: SVGWrapper, }); expect(container.querySelector("clipPath")).toMatchInlineSnapshot(` `); }); }); ================================================ FILE: packages/victory-core/src/victory-primitives/clip-path.tsx ================================================ import React from "react"; import { VictoryCommonPrimitiveProps } from "../victory-util/common-props"; export interface ClipPathProps extends VictoryCommonPrimitiveProps { children?: React.ReactNode[] | React.ReactNode; clipId?: number | string; } export const ClipPath = (props: ClipPathProps) => ( {{props.children}} ); ================================================ FILE: packages/victory-core/src/victory-primitives/index.ts ================================================ export * from "./arc"; export * from "./background"; export * from "./border"; export * from "./circle"; export * from "./clip-path"; export * from "./line"; export * from "./line-segment"; export * from "./path"; export * from "./point"; export * from "./rect"; export * from "./text"; export * from "./tspan"; export * from "./types"; export * from "./whisker"; ================================================ FILE: packages/victory-core/src/victory-primitives/line-segment.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import * as Helpers from "../victory-util/helpers"; import { VictoryCommonPrimitiveProps } from "../victory-util/common-props"; import { Line } from "./line"; export interface LineSegmentProps extends VictoryCommonPrimitiveProps { datum?: any; lineComponent?: React.ReactElement; x1?: number; x2?: number; y1?: number; y2?: number; } const evaluateProps = (props) => { /** * Potential evaluated props are: * `ariaLabel` * `desc` * `id` * `style` * `tabIndex` */ const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const desc = Helpers.evaluateProp(props.desc, props); const id = Helpers.evaluateProp(props.id, props); const style = Helpers.evaluateStyle( Object.assign({ stroke: "black" }, props.style), props, ); const tabIndex = Helpers.evaluateProp(props.tabIndex, props); return Object.assign({}, props, { ariaLabel, desc, id, style, tabIndex }); }; const defaultProps = { lineComponent: , role: "presentation", shapeRendering: "auto", }; export const LineSegment = (initialProps: LineSegmentProps) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); return React.cloneElement(props.lineComponent!, { ...props.events, "aria-label": props.ariaLabel, style: props.style, desc: props.desc, tabIndex: props.tabIndex, className: props.className, role: props.role, shapeRendering: props.shapeRendering, x1: props.x1, x2: props.x2, y1: props.y1, y2: props.y2, transform: props.transform, clipPath: props.clipPath, }); }; ================================================ FILE: packages/victory-core/src/victory-primitives/line.test.tsx ================================================ import React from "react"; import { render } from "@testing-library/react"; import { Line } from "./line"; describe("victory-primitives/line", () => { const baseProps = { x1: 0, y1: 1, x2: 2, y2: 4, }; it("should render a line element with the correct coordinates", () => { // @ts-expect-error "baseProps has no properties in common with VictoryPrimitiveShapeProps" const { container } = render(, { wrapper: "svg" }); expect(container.querySelector("line")).toMatchInlineSnapshot(` `); }); }); ================================================ FILE: packages/victory-core/src/victory-primitives/line.tsx ================================================ import React, { forwardRef } from "react"; import { evaluateProp } from "../victory-util/helpers"; import { VictoryPrimitiveShapeProps } from "./types"; export const Line = forwardRef( (props, ref) => { /* eslint-disable-next-line @typescript-eslint/no-unused-vars -- * origin conflicts with the SVG element's origin attribute */ const { desc, id, tabIndex, origin, ...rest } = props; const svgProps: React.SVGProps = { vectorEffect: "non-scaling-stroke", id: evaluateProp(id, props)?.toString(), tabIndex: evaluateProp(tabIndex, props), ...rest, }; return desc ? ( {desc} ) : ( ); }, ); ================================================ FILE: packages/victory-core/src/victory-primitives/path.tsx ================================================ import React, { forwardRef } from "react"; import { evaluateProp } from "../victory-util/helpers"; import { VictoryPrimitiveShapeProps } from "./types"; export const Path = forwardRef( (props, ref) => { /* eslint-disable-next-line @typescript-eslint/no-unused-vars -- * origin conflicts with the SVG element's origin attribute */ const { desc, id, tabIndex, origin, ...rest } = props; const svgProps: React.SVGProps = { id: evaluateProp(id, props)?.toString(), tabIndex: evaluateProp(tabIndex, props), ...rest, }; return desc ? ( {desc} ) : ( ); }, ); ================================================ FILE: packages/victory-core/src/victory-primitives/point.test.tsx ================================================ import { render } from "@testing-library/react"; import React from "react"; import { SVGWrapper } from "../../../../test/helpers"; import { PointPathHelpers } from "../victory-util"; import { Point } from "./point"; describe("victory-primitives/point", () => { const baseProps = { x: 5, y: 10, size: 1, }; ( [ "circle", "square", "diamond", "triangleDown", "triangleUp", "plus", "minus", "star", "cross", ] as const ).forEach((symbol) => { it(`should render the appropriate symbol for "${symbol}"`, () => { const stub = jest .spyOn(PointPathHelpers, symbol) .mockImplementation(() => `${symbol} symbol`); const props = Object.assign({}, baseProps, { symbol }); const { container } = render(, { wrapper: SVGWrapper, }); const directions = container.querySelector("path")!.getAttribute("d"); expect(stub).toHaveBeenCalledTimes(1); expect(stub).toHaveBeenCalledWith(5, 10, 1); expect(directions).toEqual(`${symbol} symbol`); }); }); }); ================================================ FILE: packages/victory-core/src/victory-primitives/point.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import * as Helpers from "../victory-util/helpers"; import * as pathHelpers from "../victory-util/point-path-helpers"; import { VictoryCommonPrimitiveProps } from "../victory-util/common-props"; import * as UserProps from "../victory-util/user-props"; import { Path } from "./path"; import { ScatterSymbolType } from "./types"; export interface PointProps extends VictoryCommonPrimitiveProps { datum?: any; getPath?: (x: number, y: number, size: number) => string; pathComponent?: React.ReactElement; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type size?: number | Function; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type symbol?: ScatterSymbolType | Function; x?: number; y?: number; } const getPath = (props) => { const { x, y, size, symbol } = props; if (props.getPath) { return props.getPath(x, y, size); } const pathFunctions = pathHelpers; const symbolFunction = typeof pathFunctions[symbol] === "function" ? pathFunctions[symbol] : pathFunctions.circle; return symbolFunction(x, y, size); }; const evaluateProps = (props) => { /** * Potential evaluated props are: * `ariaLabel` * `desc` * `id` * `size` * `style` * `symbol` * `tabIndex` */ const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const desc = Helpers.evaluateProp(props.desc, props); const id = Helpers.evaluateProp(props.id, props); const size = Helpers.evaluateProp(props.size, props); const style = Helpers.evaluateStyle(props.style, props); const symbol = Helpers.evaluateProp(props.symbol, props); const tabIndex = Helpers.evaluateProp(props.tabIndex, props); return Object.assign({}, props, { ariaLabel, desc, id, size, style, symbol, tabIndex, }); }; const defaultProps = { pathComponent: , role: "presentation", shapeRendering: "auto", }; export const Point = (initialProps: PointProps) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); const userProps = UserProps.getSafeUserProps(props); return React.cloneElement(props.pathComponent!, { ...props.events, "aria-label": props.ariaLabel, d: getPath(props), style: props.style, desc: props.desc, tabIndex: props.tabIndex, role: props.role, shapeRendering: props.shapeRendering, className: props.className, transform: props.transform, clipPath: props.clipPath, ...userProps, }); }; ================================================ FILE: packages/victory-core/src/victory-primitives/rect.tsx ================================================ import React, { forwardRef } from "react"; import { evaluateProp } from "../victory-util/helpers"; import { VictoryPrimitiveShapeProps } from "./types"; export const Rect = forwardRef( (props, ref) => { /* eslint-disable-next-line @typescript-eslint/no-unused-vars -- * origin conflicts with the SVG element's origin attribute */ const { desc, id, tabIndex, origin, ...rest } = props; const svgProps: React.SVGProps = { vectorEffect: "non-scaling-stroke", id: evaluateProp(id, props)?.toString(), tabIndex: evaluateProp(tabIndex, props), ...rest, }; return desc ? ( {desc} ) : ( ); }, ); ================================================ FILE: packages/victory-core/src/victory-primitives/text.tsx ================================================ import React from "react"; import { evaluateProp } from "../victory-util/helpers"; import { VictoryCommonPrimitiveProps } from "../victory-util/common-props"; export interface TextProps extends VictoryCommonPrimitiveProps { children?: React.ReactNode; desc?: string; title?: string; } export const Text = (props: TextProps) => { /* eslint-disable-next-line @typescript-eslint/no-unused-vars -- * origin conflicts with the SVG element's origin attribute */ const { children, desc, id, origin, tabIndex, title, ...rest } = props; const svgProps: React.SVGProps = { id: evaluateProp(id, props)?.toString(), tabIndex: evaluateProp(tabIndex, props), ...rest, }; return ( {title && {title}} {desc && {desc}} {children} ); }; ================================================ FILE: packages/victory-core/src/victory-primitives/tspan.tsx ================================================ import React from "react"; import { evaluateProp } from "../victory-util/helpers"; import { VictoryCommonPrimitiveProps } from "../victory-util/common-props"; export const TSpan = (props: VictoryCommonPrimitiveProps) => { /* eslint-disable-next-line @typescript-eslint/no-unused-vars -- * origin conflicts with the SVG element's origin attribute */ const { desc, id, tabIndex, origin, ...rest } = props; const svgProps: React.SVGProps = { id: evaluateProp(id, props)?.toString(), tabIndex: evaluateProp(tabIndex, props), ...rest, }; return ; }; ================================================ FILE: packages/victory-core/src/victory-primitives/types.ts ================================================ import { VictoryCommonPrimitiveProps } from "../victory-util/common-props"; export type ScatterSymbolType = | "circle" | "cross" | "diamond" | "plus" | "minus" | "square" | "star" | "triangleDown" | "triangleUp"; export interface VictoryPrimitiveShapeProps extends VictoryCommonPrimitiveProps { desc?: string; rx?: number; ry?: number; } ================================================ FILE: packages/victory-core/src/victory-primitives/whisker.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import * as Helpers from "../victory-util/helpers"; import { VictoryCommonPrimitiveProps } from "../victory-util/common-props"; import { Line } from "./line"; export type WhiskerAxes = { x1?: number; x2?: number; y1?: number; y2?: number; }; export interface WhiskerProps extends VictoryCommonPrimitiveProps { groupComponent?: React.ReactElement; lineComponent?: React.ReactElement; majorWhisker?: WhiskerAxes; minorWhisker?: WhiskerAxes; } const evaluateProps = (props) => { /** * Potential evaluated props are: * `ariaLabel` * `desc` * `id` * `style` * `tabIndex` */ const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const desc = Helpers.evaluateProp(props.desc, props); const id = Helpers.evaluateProp(props.id, props); const style = Helpers.evaluateStyle(props.style, props); const tabIndex = Helpers.evaluateProp(props.tabIndex, props); return Object.assign({}, props, { ariaLabel, desc, id, style, tabIndex }); }; const defaultProps = { groupComponent: , lineComponent: , role: "presentation", shapeRendering: "auto", }; export const Whisker = (initialProps: WhiskerProps) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); const { ariaLabel, groupComponent, lineComponent, events, className, majorWhisker, minorWhisker, transform, clipPath, role, shapeRendering, style, desc, tabIndex, } = props; const baseProps = { ...events, style, desc, tabIndex, className, transform, clipPath, role, shapeRendering, }; return React.cloneElement(groupComponent, {}, [ React.cloneElement( lineComponent, Object.assign( { key: "major-whisker", "aria-label": ariaLabel }, baseProps, majorWhisker, ), ), React.cloneElement( lineComponent, Object.assign( { key: "minor-whisker", "aria-label": ariaLabel }, baseProps, minorWhisker, ), ), ]); }; ================================================ FILE: packages/victory-core/src/victory-theme/clean.tsx ================================================ import { VictoryThemeDefinition } from "./victory-theme"; // * // * Colors // * const gray = { white: "#FFFFFF", "50": "#FAFAFA", "100": "#F2F2F2", "200": "#E8E8E8", "300": "#E0E0E0", "400": "#D1D1D1", "500": "#757575", "600": "#5C5C5C", "700": "#424242", "800": "#333333", "900": "#292929", black: "#0F0F0F", }; const yellow = { "100": "#FFEAB6", "300": "#FFD66E", "500": "#FCB400", "900": "#B87503", }; const orange = { "100": "#FEE2D5", "300": "#FFA981", "500": "#FF6F2C", "700": "#FF4E1B", "900": "#D74D26", }; const red = { "100": "#FFDCE5", "300": "#FF9EB7", "500": "#F82B60", "700": "#D31A3D", "900": "#BA1E45", }; const purple = { "100": "#EDE3FE", "300": "#CDB0FF", "500": "#8B46FF", "900": "#6B1CB0", }; const blue = { "100": "#CFDFFF", "300": "#9CC7FF", "500": "#2D7FF9", "700": "#0056B3", "900": "#2750AE", }; const cyan = { "100": "#D0F0FD", "300": "#77D1F3", "500": "#18BFFF", "900": "#0B76B7", }; const teal = { "100": "#C2F5E9", "300": "#72DDC3", "500": "#20D9D2", "900": "#06A09B", }; const green = { "100": "#D1F7C4", "300": "#93E088", "500": "#20C933", "700": "#1B9B2A", "900": "#338A17", }; const colors = { blue: blue["500"], cyan: cyan["500"], green: green["500"], yellow: yellow["500"], orange: orange["500"], red: red["500"], purple: purple["500"], teal: teal["500"], }; const colorScale = Object.values(colors); const grayscale = [ gray["100"], gray["300"], gray["500"], gray["700"], gray["900"], ]; const warm = [ yellow["300"], yellow["500"], orange["500"], orange["900"], red["500"], ]; const cool = [ purple["500"], blue["500"], cyan["500"], teal["500"], green["500"], ]; const heatmap = [ green["900"], green["500"], yellow["500"], orange["500"], red["500"], ]; const redPalette = Object.values(red); const greenPalette = Object.values(green); const bluePalette = Object.values(blue); const defaultColor = blue["500"]; // * // * Typography // * const sansSerif = "'Inter', 'Helvetica Neue', 'Seravek', 'Helvetica', sans-serif"; const letterSpacing = "normal"; const fontSize = 12; // * // * Layout // * const padding = 8; const baseProps = { width: 450, height: 300, padding: 60, colorScale, }; // * // * Labels // * const baseLabelStyles = { fontFamily: sansSerif, fontSize, fontWeight: 300, letterSpacing, padding, fill: gray["900"], stroke: "transparent", }; const centeredLabelStyles = Object.assign( { textAnchor: "middle" }, baseLabelStyles, ); // * // * Strokes // * const strokeDasharray = "10, 5"; const strokeLinecap = "round"; const strokeLinejoin = "round"; const borderRadius = 1; // * // * Theme // * export const clean: VictoryThemeDefinition = { palette: { colors, grayscale, qualitative: colorScale, heatmap, warm, cool, red: redPalette, green: greenPalette, blue: bluePalette, }, area: Object.assign( { style: { data: { fill: defaultColor, strokeWidth: 2, fillOpacity: 0.5, }, labels: baseLabelStyles, }, }, baseProps, ), axis: Object.assign( { style: { axis: { fill: "transparent", stroke: gray["500"], strokeWidth: 1, strokeLinecap, strokeLinejoin, }, axisLabel: Object.assign({}, centeredLabelStyles, { padding: 35, stroke: "transparent", }), grid: { fill: "none", stroke: "none", painterEvents: "painted", }, ticks: { fill: "transparent", size: 5, stroke: "transparent", }, tickLabels: baseLabelStyles, }, }, baseProps, ), polarAxis: Object.assign({ style: { axis: { stroke: gray["500"], }, grid: { stroke: gray["400"], strokeDasharray, strokeLinecap, strokeLinejoin, pointerEvents: "painted", }, ticks: { fill: "transparent", size: 5, stroke: gray["400"], strokeWidth: 1, strokeLinecap, strokeLinejoin, }, tickLabels: baseLabelStyles, }, }), polarDependentAxis: Object.assign({ style: { axis: { stroke: gray["500"], }, grid: { stroke: gray["400"], strokeDasharray, strokeLinecap, strokeLinejoin, pointerEvents: "painted", }, ticks: { fill: "transparent", size: 5, stroke: gray["300"], strokeWidth: 1, strokeLinecap, strokeLinejoin, }, tickLabels: baseLabelStyles, }, }), bar: Object.assign( { style: { data: { fill: defaultColor, padding, strokeWidth: 1, fillOpacity: 0.5, }, labels: baseLabelStyles, }, cornerRadius: { top: borderRadius }, }, baseProps, ), boxplot: Object.assign( { style: { max: { padding, stroke: gray["400"], strokeWidth: 2 }, maxLabels: Object.assign({}, baseLabelStyles, { padding: 3 }), median: { padding, stroke: gray.white, strokeWidth: 2 }, medianLabels: Object.assign({}, baseLabelStyles, { padding: 3 }), min: { padding, stroke: gray["400"], strokeWidth: 2 }, minLabels: Object.assign({}, baseLabelStyles, { padding: 3 }), q1: { padding, fill: colorScale[0], rx: borderRadius, strokeWidth: 2 }, q1Labels: Object.assign({}, baseLabelStyles, { padding: 3 }), q3: { padding, fill: colorScale[1], rx: borderRadius }, q3Labels: Object.assign({}, baseLabelStyles, { padding: 3 }), }, boxWidth: 20, }, baseProps, ), candlestick: Object.assign( { style: { data: { stroke: gray["300"], strokeWidth: 0, rx: borderRadius, }, labels: Object.assign({}, baseLabelStyles, { padding: 5 }), }, candleColors: { positive: green["500"], negative: red["500"], }, wickStrokeWidth: 2, }, baseProps, ), chart: baseProps, errorbar: Object.assign( { borderWidth: 8, style: { data: { fill: "transparent", opacity: 1, stroke: gray["700"], strokeWidth: 2, strokeLinecap, }, labels: baseLabelStyles, }, }, baseProps, ), group: Object.assign( { colorScale, }, baseProps, ), histogram: Object.assign( { style: { data: { fill: cyan["500"], fillOpacity: 0.5, }, labels: baseLabelStyles, }, binSpacing: 4, cornerRadius: { top: borderRadius }, }, baseProps, ), label: baseLabelStyles, legend: { colorScale, gutter: 24, borderPadding: 10, orientation: "horizontal", titleOrientation: "top", centerTitle: true, style: { data: { type: "circle", }, labels: { ...baseLabelStyles, fontSize: 12 }, title: Object.assign({}, baseLabelStyles, { padding, fontSize: 16 }), border: { stroke: gray["200"], strokeWidth: 2, padding: 16 }, }, }, line: Object.assign( { style: { data: { fill: "transparent", opacity: 1, stroke: defaultColor, strokeWidth: 2, strokeLinecap, strokeLinejoin, }, labels: baseLabelStyles, }, }, baseProps, ), pie: Object.assign( { style: { parent: { backgroundColor: gray.white, }, data: { padding, stroke: gray.white, strokeWidth: 1, }, labels: { ...baseLabelStyles, padding: 20, fill: gray["600"], fontSize: 10, }, }, colorScale, cornerRadius: borderRadius, }, baseProps, ), scatter: Object.assign( { style: { data: { fill: defaultColor, opacity: 1, stroke: "transparent", strokeWidth: 0, }, labels: { ...baseLabelStyles, padding: 20, }, }, }, baseProps, ), stack: Object.assign( { colorScale, }, baseProps, ), tooltip: { style: Object.assign({}, baseLabelStyles, { padding: 0, pointerEvents: "none", }), flyoutStyle: { stroke: gray["300"], strokeWidth: 2, fill: gray.white, pointerEvents: "none", }, flyoutPadding: { top: 8, bottom: 8, left: 16, right: 16 }, cornerRadius: borderRadius, pointerLength: 4, }, voronoi: Object.assign( { style: { data: { fill: blue["100"], stroke: defaultColor, strokeWidth: 2, }, labels: Object.assign({}, baseLabelStyles, { padding: 5, pointerEvents: "none", }), flyout: { stroke: gray["900"], strokeWidth: 1, fill: gray["100"], pointerEvents: "none", }, padding: { left: 2, bottom: 2 }, }, }, baseProps, ), }; ================================================ FILE: packages/victory-core/src/victory-theme/grayscale.tsx ================================================ import { VictoryThemeDefinition } from "./types"; // * // * Colors // * const colors = { blue: "#4F7DA1", pink: "#E2A37F", teal: "#00796B", purple: "#DF948A", green: "#8BC34A", orange: "#F4511E", cyan: "#006064", red: "#DF5A49", yellow: "#FFF59D", }; const colorScale = [ "#252525", "#525252", "#737373", "#969696", "#bdbdbd", "#d9d9d9", "#f0f0f0", ]; const charcoal = "#252525"; const grey = "#969696"; const qualitative = [ "#334D5C", "#45B29D", "#EFC94C", "#E27A3F", "#DF5A49", "#4F7DA1", "#55DBC1", "#EFDA97", "#E2A37F", "#DF948A", ]; const heatmap = ["#428517", "#77D200", "#D6D305", "#EC8E19", "#C92B05"]; const warm = ["#940031", "#C43343", "#DC5429", "#FF821D", "#FFAF55"]; const cool = ["#2746B9", "#0B69D4", "#2794DB", "#31BB76", "#60E83B"]; const red = ["#FCAE91", "#FB6A4A", "#DE2D26", "#A50F15", "#750B0E"]; const green = ["#354722", "#466631", "#649146", "#8AB25C", "#A9C97E"]; const blue = ["#002C61", "#004B8F", "#006BC9", "#3795E5", "#65B4F4"]; // * // * Typography // * const sansSerif = "'Gill Sans', 'Seravek', 'Trebuchet MS', sans-serif"; const letterSpacing = "normal"; const fontSize = 14; // * // * Layout // * const baseProps = { width: 450, height: 300, padding: 50, colorScale, }; // * // * Labels // * const baseLabelStyles = { fontFamily: sansSerif, fontSize, letterSpacing, padding: 10, fill: charcoal, stroke: "transparent", }; const centeredLabelStyles = Object.assign( { textAnchor: "middle" }, baseLabelStyles, ); // * // * Strokes // * const strokeLinecap = "round"; const strokeLinejoin = "round"; export const grayscale: VictoryThemeDefinition = { palette: { colors, grayscale: colorScale, qualitative, heatmap, warm, cool, red, green, blue, }, area: Object.assign( { style: { data: { fill: charcoal, }, labels: baseLabelStyles, }, }, baseProps, ), axis: Object.assign( { style: { axis: { fill: "transparent", stroke: charcoal, strokeWidth: 1, strokeLinecap, strokeLinejoin, }, axisLabel: Object.assign({}, centeredLabelStyles, { padding: 25, }), grid: { fill: "none", stroke: "none", pointerEvents: "painted", }, ticks: { fill: "transparent", size: 1, stroke: "transparent", }, tickLabels: baseLabelStyles, }, }, baseProps, ), bar: Object.assign( { style: { data: { fill: charcoal, padding: 8, strokeWidth: 0, }, labels: baseLabelStyles, }, }, baseProps, ), boxplot: Object.assign( { style: { max: { padding: 8, stroke: charcoal, strokeWidth: 1 }, maxLabels: Object.assign({}, baseLabelStyles, { padding: 3 }), median: { padding: 8, stroke: charcoal, strokeWidth: 1 }, medianLabels: Object.assign({}, baseLabelStyles, { padding: 3 }), min: { padding: 8, stroke: charcoal, strokeWidth: 1 }, minLabels: Object.assign({}, baseLabelStyles, { padding: 3 }), q1: { padding: 8, fill: grey }, q1Labels: Object.assign({}, baseLabelStyles, { padding: 3 }), q3: { padding: 8, fill: grey }, q3Labels: Object.assign({}, baseLabelStyles, { padding: 3 }), }, boxWidth: 20, }, baseProps, ), candlestick: Object.assign( { style: { data: { stroke: charcoal, strokeWidth: 1, }, labels: Object.assign({}, baseLabelStyles, { padding: 5 }), }, candleColors: { positive: "#ffffff", negative: charcoal, }, }, baseProps, ), chart: baseProps, errorbar: Object.assign( { borderWidth: 8, style: { data: { fill: "transparent", stroke: charcoal, strokeWidth: 2, }, labels: baseLabelStyles, }, }, baseProps, ), group: Object.assign( { colorScale, }, baseProps, ), histogram: Object.assign( { style: { data: { fill: grey, stroke: charcoal, strokeWidth: 2, }, labels: baseLabelStyles, }, }, baseProps, ), legend: { colorScale, gutter: 10, orientation: "vertical", titleOrientation: "top", style: { data: { type: "circle", }, labels: baseLabelStyles, title: Object.assign({}, baseLabelStyles, { padding: 5 }), }, }, line: Object.assign( { style: { data: { fill: "transparent", stroke: charcoal, strokeWidth: 2, }, labels: baseLabelStyles, }, }, baseProps, ), pie: { style: { data: { padding: 10, stroke: "transparent", strokeWidth: 1, }, labels: Object.assign({}, baseLabelStyles, { padding: 20 }), }, colorScale, width: 400, height: 400, padding: 50, }, scatter: Object.assign( { style: { data: { fill: charcoal, stroke: "transparent", strokeWidth: 0, }, labels: baseLabelStyles, }, }, baseProps, ), stack: Object.assign( { colorScale, }, baseProps, ), tooltip: { style: Object.assign({}, baseLabelStyles, { padding: 0, pointerEvents: "none", }), flyoutStyle: { stroke: charcoal, strokeWidth: 1, fill: "#f0f0f0", pointerEvents: "none", }, flyoutPadding: 5, cornerRadius: 5, pointerLength: 10, }, voronoi: Object.assign( { style: { data: { fill: "transparent", stroke: "transparent", strokeWidth: 0, }, labels: Object.assign({}, baseLabelStyles, { padding: 5, pointerEvents: "none", }), flyout: { stroke: charcoal, strokeWidth: 1, fill: "#f0f0f0", pointerEvents: "none", }, }, }, baseProps, ), }; ================================================ FILE: packages/victory-core/src/victory-theme/material.tsx ================================================ import { VictoryThemeDefinition, VictoryThemePalette } from "./types"; // * // * Colors // * const yellow200 = "#FFF59D"; const deepOrange600 = "#F4511E"; const lime300 = "#DCE775"; const lightGreen500 = "#8BC34A"; const teal700 = "#00796B"; const cyan900 = "#006064"; const colorScale = [ deepOrange600, yellow200, lime300, lightGreen500, teal700, cyan900, ]; const blueGrey50 = "#ECEFF1"; const blueGrey300 = "#90A4AE"; const blueGrey700 = "#455A64"; const grey900 = "#212121"; const colors: VictoryThemePalette = { blue: "#4F7DA1", pink: "#E2A37F", teal: teal700, purple: "#DF948A", green: lightGreen500, orange: deepOrange600, cyan: cyan900, red: "#DF5A49", yellow: yellow200, }; const grayscale = [blueGrey50, blueGrey300, blueGrey700, grey900]; const qualitative = [ "#334D5C", "#45B29D", "#EFC94C", "#E27A3F", "#DF5A49", "#4F7DA1", "#55DBC1", "#EFDA97", "#E2A37F", "#DF948A", ]; const heatmap = ["#428517", "#77D200", "#D6D305", "#EC8E19", "#C92B05"]; const warm = ["#940031", "#C43343", "#DC5429", "#FF821D", "#FFAF55"]; const cool = ["#2746B9", "#0B69D4", "#2794DB", "#31BB76", "#60E83B"]; const red = ["#FCAE91", "#FB6A4A", "#DE2D26", "#A50F15", "#750B0E"]; const green = ["#354722", "#466631", "#649146", "#8AB25C", "#A9C97E"]; const blue = ["#002C61", "#004B8F", "#006BC9", "#3795E5", "#65B4F4"]; // * // * Typography // * const sansSerif = "'Helvetica Neue', 'Helvetica', sans-serif"; const letterSpacing = "normal"; const fontSize = 12; // * // * Layout // * const padding = 8; const baseProps = { width: 350, height: 350, padding: 50, }; // * // * Labels // * const baseLabelStyles = { fontFamily: sansSerif, fontSize, letterSpacing, padding, fill: blueGrey700, stroke: "transparent", strokeWidth: 0, }; const centeredLabelStyles = Object.assign( { textAnchor: "middle" }, baseLabelStyles, ); // * // * Strokes // * const strokeDasharray = "10, 5"; const strokeLinecap = "round"; const strokeLinejoin = "round"; export const material: VictoryThemeDefinition = { palette: { colors, grayscale, qualitative, heatmap, warm, cool, red, green, blue, }, area: Object.assign( { style: { data: { fill: grey900, }, labels: baseLabelStyles, }, }, baseProps, ), axis: Object.assign( { style: { axis: { fill: "transparent", stroke: blueGrey300, strokeWidth: 2, strokeLinecap, strokeLinejoin, }, axisLabel: Object.assign({}, centeredLabelStyles, { padding, stroke: "transparent", }), grid: { fill: "none", stroke: blueGrey50, strokeDasharray, strokeLinecap, strokeLinejoin, pointerEvents: "painted", }, ticks: { fill: "transparent", size: 5, stroke: blueGrey300, strokeWidth: 1, strokeLinecap, strokeLinejoin, }, tickLabels: Object.assign({}, baseLabelStyles, { fill: blueGrey700, }), }, }, baseProps, ), polarDependentAxis: Object.assign({ style: { ticks: { fill: "transparent", size: 1, stroke: "transparent", }, }, }), bar: Object.assign( { style: { data: { fill: blueGrey700, padding, strokeWidth: 0, }, labels: baseLabelStyles, }, }, baseProps, ), boxplot: Object.assign( { style: { max: { padding, stroke: blueGrey700, strokeWidth: 1 }, maxLabels: Object.assign({}, baseLabelStyles, { padding: 3 }), median: { padding, stroke: blueGrey700, strokeWidth: 1 }, medianLabels: Object.assign({}, baseLabelStyles, { padding: 3 }), min: { padding, stroke: blueGrey700, strokeWidth: 1 }, minLabels: Object.assign({}, baseLabelStyles, { padding: 3 }), q1: { padding, fill: blueGrey700 }, q1Labels: Object.assign({}, baseLabelStyles, { padding: 3 }), q3: { padding, fill: blueGrey700 }, q3Labels: Object.assign({}, baseLabelStyles, { padding: 3 }), }, boxWidth: 20, }, baseProps, ), candlestick: Object.assign( { style: { data: { stroke: blueGrey700, }, labels: Object.assign({}, baseLabelStyles, { padding: 5 }), }, candleColors: { positive: "#ffffff", negative: blueGrey700, }, }, baseProps, ), chart: baseProps, errorbar: Object.assign( { borderWidth: 8, style: { data: { fill: "transparent", opacity: 1, stroke: blueGrey700, strokeWidth: 2, }, labels: baseLabelStyles, }, }, baseProps, ), group: Object.assign( { colorScale, }, baseProps, ), histogram: Object.assign( { style: { data: { fill: blueGrey700, stroke: grey900, strokeWidth: 2, }, labels: baseLabelStyles, }, }, baseProps, ), legend: { colorScale, gutter: 10, orientation: "vertical", titleOrientation: "top", style: { data: { type: "circle", }, labels: baseLabelStyles, title: Object.assign({}, baseLabelStyles, { padding: 5 }), }, }, line: Object.assign( { style: { data: { fill: "transparent", opacity: 1, stroke: blueGrey700, strokeWidth: 2, }, labels: baseLabelStyles, }, }, baseProps, ), pie: Object.assign( { colorScale, style: { data: { padding, stroke: blueGrey50, strokeWidth: 1, }, labels: Object.assign({}, baseLabelStyles, { padding: 20 }), }, }, baseProps, ), scatter: Object.assign( { style: { data: { fill: blueGrey700, opacity: 1, stroke: "transparent", strokeWidth: 0, }, labels: baseLabelStyles, }, }, baseProps, ), stack: Object.assign( { colorScale, }, baseProps, ), tooltip: { style: Object.assign({}, baseLabelStyles, { padding: 0, pointerEvents: "none", }), flyoutStyle: { stroke: grey900, strokeWidth: 1, fill: "#f0f0f0", pointerEvents: "none", }, flyoutPadding: 5, cornerRadius: 5, pointerLength: 10, }, voronoi: Object.assign( { style: { data: { fill: "transparent", stroke: "transparent", strokeWidth: 0, }, labels: Object.assign({}, baseLabelStyles, { padding: 5, pointerEvents: "none", }), flyout: { stroke: grey900, strokeWidth: 1, fill: "#f0f0f0", pointerEvents: "none", }, }, }, baseProps, ), }; ================================================ FILE: packages/victory-core/src/victory-theme/types.ts ================================================ import * as React from "react"; import { VictoryCommonThemeProps, VictoryDatableProps, } from "../victory-util/common-props"; import { NumberOrCallback, OrientationOrCallback, PaddingOrCallback, StringOrNumberOrCallback, } from "../types/callbacks"; import { DomainPropType } from "../types/prop-types"; export type BlockProps = { top?: number; bottom?: number; left?: number; right?: number; }; export type PaddingProps = number | BlockProps; export type OrientationTypes = "top" | "bottom" | "left" | "right"; export type VictoryStyleObject = { [K in keyof React.CSSProperties]: StringOrNumberOrCallback; }; export type LabelProps = React.CSSProperties & { angle?: number; verticalAnchor?: VerticalAnchorType; }; export type VictoryLabelStyleObject = { [K in keyof LabelProps]: StringOrNumberOrCallback; }; /** * Style interface used in components/themeing */ export interface VictoryStyleInterface { parent?: VictoryStyleObject; data?: VictoryStyleObject; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; border?: VictoryStyleObject; } export type TickProps = React.CSSProperties & { size?: number }; export type VictoryTickStyleObject = { [K in keyof TickProps]: StringOrNumberOrCallback; }; export interface VictoryAxisCommonProps { axisComponent?: React.ReactElement; axisLabelComponent?: React.ReactElement; axisValue?: number | string | object | Date; dependentAxis?: boolean; domain?: DomainPropType; gridComponent?: React.ReactElement; invertAxis?: boolean; style?: { parent?: VictoryStyleObject; axis?: VictoryStyleObject; axisLabel?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; grid?: VictoryStyleObject; ticks?: VictoryTickStyleObject; tickLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; tickComponent?: React.ReactElement; tickCount?: number; tickLabelComponent?: React.ReactElement; tickFormat?: | any[] | { (tick: any, index: number, ticks: any[]): string | number }; tickValues?: any[]; } export type VerticalAnchorType = "start" | "middle" | "end"; // #region Victory Theme // Note: Many SVG attributes are missed in CSSProperties interface type VictoryThemePaletteKeys = | "blue" | "pink" | "teal" | "purple" | "green" | "orange" | "cyan" | "red" | "yellow"; export type VictoryThemePalette = { [key in VictoryThemePaletteKeys]?: string; }; export interface VictoryThemeDefinition { palette?: { colors?: VictoryThemePalette; grayscale?: string[]; qualitative?: string[]; heatmap?: string[]; warm?: string[]; cool?: string[]; red?: string[]; blue?: string[]; green?: string[]; }; area?: { style?: { data?: VictoryStyleObject; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; } & VictoryCommonThemeProps & VictoryDatableProps; axis?: { style?: { axis?: VictoryStyleObject; axisLabel?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; grid?: VictoryStyleObject; ticks?: VictoryTickStyleObject; tickLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; offsetX?: number; offsetY?: number; } & VictoryCommonThemeProps; bar?: { style?: { data?: VictoryStyleObject; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; } & VictoryCommonThemeProps & VictoryDatableProps; boxplot?: { style?: { max?: VictoryStyleObject; maxLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; median?: VictoryStyleObject; medianLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; min?: VictoryStyleObject; minLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; q1?: VictoryStyleObject; q1Labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; q3?: VictoryStyleObject; q3Labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; boxWidth?: number; } & VictoryCommonThemeProps; candlestick?: { style?: { data?: VictoryStyleObject; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; candleColors?: { positive?: string; negative?: string; }; } & VictoryCommonThemeProps; chart?: VictoryCommonThemeProps; dependentAxis?: { style?: { axis?: VictoryStyleObject; axisLabel?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; grid?: VictoryStyleObject; ticks?: VictoryTickStyleObject; tickLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; offsetX?: number; offsetY?: number; orientation?: OrientationTypes; } & VictoryCommonThemeProps; errorbar?: { borderWidth?: number; style?: { data?: VictoryStyleObject; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; } & VictoryCommonThemeProps; group?: { style?: { data?: VictoryStyleObject; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; } & VictoryCommonThemeProps; histogram?: { style?: { data?: VictoryStyleObject; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; } & VictoryCommonThemeProps; independentAxis?: { style?: { axis?: VictoryStyleObject; axisLabel?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; grid?: VictoryStyleObject; ticks?: VictoryTickStyleObject; tickLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; offsetX?: number; offsetY?: number; orientation?: OrientationTypes; } & VictoryCommonThemeProps; label?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; legend?: { gutter?: number | BlockProps; rowGutter?: number | BlockProps; orientation?: string; titleOrientation?: string; style?: { data?: VictoryStyleObject & { type?: string; }; border?: VictoryStyleObject; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; title?: VictoryLabelStyleObject; }; itemsPerRow?: number; x?: number; y?: number; centerTitle?: boolean; borderPadding?: number | BlockProps; } & VictoryCommonThemeProps; line?: { style?: { data?: VictoryStyleObject; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; } & VictoryCommonThemeProps & VictoryDatableProps; pie?: { style?: { data?: VictoryStyleObject; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; } & VictoryCommonThemeProps & VictoryDatableProps; polarAxis?: { style?: { axis?: VictoryStyleObject; axisLabel?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; grid?: VictoryStyleObject; ticks?: VictoryTickStyleObject; tickLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; } & VictoryCommonThemeProps; polarDependentAxis?: { style?: { axis?: VictoryStyleObject; axisLabel?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; grid?: VictoryStyleObject; ticks?: VictoryTickStyleObject; tickLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; } & VictoryCommonThemeProps; polarIndependentAxis?: { style?: { axis?: VictoryStyleObject; axisLabel?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; grid?: VictoryStyleObject; ticks?: VictoryTickStyleObject; tickLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; } & VictoryCommonThemeProps; scatter?: { style?: { data?: VictoryStyleObject; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; } & VictoryCommonThemeProps & VictoryDatableProps; stack?: VictoryCommonThemeProps; tooltip?: { style?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; flyoutStyle?: VictoryStyleObject; cornerRadius?: NumberOrCallback; pointerLength?: NumberOrCallback; flyoutPadding?: PaddingOrCallback; flyoutWidth?: NumberOrCallback; flyoutHeight?: NumberOrCallback; orientation?: OrientationOrCallback; pointerOrientation?: OrientationOrCallback; }; voronoi?: { style?: { data?: VictoryStyleObject; labels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; flyout?: VictoryStyleObject; }; } & VictoryCommonThemeProps & VictoryDatableProps; } ================================================ FILE: packages/victory-core/src/victory-theme/victory-theme.tsx ================================================ import { grayscale } from "./grayscale"; import { material } from "./material"; import { clean } from "./clean"; export * from "./types"; export const VictoryTheme = { grayscale, material, clean }; ================================================ FILE: packages/victory-core/src/victory-transition/victory-transition.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import pick from "lodash/pick"; import isEqual from "react-fast-compare"; import { VictoryAnimation } from "../victory-animation/victory-animation"; import * as Collection from "../victory-util/collection"; import * as Helpers from "../victory-util/helpers"; import TimerContext from "../victory-util/timer-context"; import * as Transitions from "../victory-util/transitions"; import Timer from "../victory-util/timer"; type VictoryTransitionChild = React.ReactElement< // Props: { polar?: boolean; domain?: number[] | { x: number[]; y: number[] }; groupComponent?: React.ReactElement; }, // Type: { new (props: any): React.Component; continuous?: boolean; } >; interface VictoryTransitionProps { animate?: boolean | any; animationWhitelist?: string[]; children: VictoryTransitionChild; } interface VictoryTransitionState { oldProps?: VictoryTransitionProps | null; nextProps?: VictoryTransitionProps; nodesShouldLoad?: boolean; nodesDoneLoad?: boolean; nodesWillExit?: boolean; nodesWillEnter?: boolean; nodesShouldEnter?: boolean; childrenTransitions?: unknown; } export interface VictoryTransition { context: React.ContextType; } export class VictoryTransition extends React.Component< VictoryTransitionProps, VictoryTransitionState > { static displayName = "VictoryTransition"; static contextType = TimerContext; private continuous: boolean; private timer: Timer; private transitionProps: any; constructor(props, context) { super(props, context); this.state = { nodesShouldLoad: false, nodesDoneLoad: false, }; const child = this.props.children; const polar = child.props.polar; this.continuous = !polar && child.type && child.type.continuous === true; this.timer = this.context.transitionTimer; } componentDidMount() { this.setState({ nodesShouldLoad: true }); } shouldComponentUpdate(nextProps: VictoryTransitionProps) { if (!isEqual(this.props, nextProps)) { this.timer.bypassAnimation(); this.setState(this.getTransitionState(this.props, nextProps), () => this.timer.resumeAnimation(), ); } return true; } componentWillUnmount() { this.timer.stop(); } private getTransitionState( props: VictoryTransitionProps, nextProps: VictoryTransitionProps, ): VictoryTransitionState { const { animate } = props; if (!animate) { return {}; } else if (animate.parentState) { const state = animate.parentState; const oldProps = state.nodesWillExit ? props : null; return { oldProps, nextProps }; } const oldChildren = React.Children.toArray(props.children); const nextChildren = React.Children.toArray(nextProps.children); const { nodesWillExit, nodesWillEnter, childrenTransitions, nodesShouldEnter, } = Transitions.getInitialTransitionState(oldChildren, nextChildren); return { nodesWillExit, nodesWillEnter, childrenTransitions, nodesShouldEnter, oldProps: nodesWillExit ? props : null, nextProps, }; } private getDomainFromChildren( props: VictoryTransitionProps, axis: "x" | "y", ) { const getChildDomains = (children) => { return children.reduce((memo, child) => { if (child.type && Helpers.isFunction(child.type.getDomain)) { const childDomain = child.props && child.type.getDomain(child.props, axis); return childDomain ? memo.concat(childDomain) : memo; } else if (child.props && child.props.children) { return memo.concat( getChildDomains(React.Children.toArray(child.props.children)), ); } return memo; }, []); }; const child = React.Children.toArray( props.children, )[0] as VictoryTransitionChild; const childProps: any = child.props || {}; const domain = Array.isArray(childProps.domain) ? childProps.domain : childProps.domain && childProps.domain[axis]; if (!childProps.children && domain) { return domain; } const childDomains = getChildDomains([child]); return childDomains.length === 0 ? [0, 1] : [ Collection.getMinValue(childDomains), Collection.getMaxValue(childDomains), ]; } pickProps() { if (!this.state) { return this.props; } return this.state.nodesWillExit ? this.state.oldProps || this.props : this.props; } private pickDomainProps(props: VictoryTransitionProps) { const parentState = props.animate?.parentState; if (parentState && parentState.nodesWillExit) { return this.continuous || parentState.continuous ? parentState.nextProps || this.state.nextProps || props : props; } return this.continuous && this.state.nodesWillExit ? this.state.nextProps || props : props; } getClipWidth(props, child) { const getDefaultClipWidth = () => { const range = Helpers.getRange(child.props, "x"); return range ? Math.abs(range[1] - range[0]) : props.width; }; const clipWidth = this.transitionProps ? this.transitionProps.clipWidth : undefined; return clipWidth !== undefined ? clipWidth : getDefaultClipWidth(); } render() { const props = this.pickProps(); const getTransitionProps = this.props.animate?.getTransitions ? this.props.animate.getTransitions : Transitions.getTransitionPropsFactory(props, this.state, (newState) => this.setState(newState), ); const child = React.Children.toArray( props.children, )[0] as VictoryTransitionChild; const transitionProps = getTransitionProps(child); this.transitionProps = transitionProps; const domain = { x: this.getDomainFromChildren(this.pickDomainProps(props), "x"), y: this.getDomainFromChildren(props, "y"), }; const clipWidth = this.getClipWidth(props, child); const combinedProps = defaults( { domain, clipWidth }, transitionProps, child.props, ); const animationWhitelist = props.animationWhitelist || []; const whitelist = animationWhitelist.concat(["clipWidth"]); const propsToAnimate = whitelist.length ? pick(combinedProps, whitelist) : combinedProps; return ( {(newProps) => { if (child.props.groupComponent) { const groupComponent = this.continuous ? React.cloneElement(child.props.groupComponent, { clipWidth: newProps.clipWidth || 0, }) : child.props.groupComponent; return React.cloneElement( child, defaults( { animate: null, animating: true, groupComponent }, newProps, combinedProps, ), ); } return React.cloneElement( child, defaults( { animate: null, animating: true }, newProps, combinedProps, ), ); }} ); } } ================================================ FILE: packages/victory-core/src/victory-util/add-events.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import isEmpty from "lodash/isEmpty"; import pick from "lodash/pick"; import isEqual from "react-fast-compare"; import { VictoryLabelableProps } from "../types/prop-types"; import { VictoryTransition } from "../victory-transition/victory-transition"; import { VictoryCommonProps, VictoryDatableProps } from "./common-props"; import { difference } from "./collection"; import type { ComponentEvent } from "./events"; import * as Events from "./events"; import { isFunction, isNil } from "./helpers"; // DISCLAIMER: // This file is not currently tested, and it is first on the list of files // to refactor in our current refactoring effort. Please do not make changes // to this file without manual testing and/or refactoring and adding tests. const datumHasXandY = (datum) => { return !isNil(datum._x) && !isNil(datum._y); }; // used for checking state changes. Expected components can be passed in via options const defaultComponents: NonNullable = [ { name: "parent", index: "parent" }, { name: "data" }, { name: "labels" }, ]; export type MixinOptions = { components?: Array<{ name: string; index?: string | number; }>; }; export interface EventMixinCommonProps extends VictoryCommonProps, VictoryDatableProps, VictoryLabelableProps {} /** * These methods will be implemented by the Mixin, * and are accessible to the Wrapped Component. * * To make your Wrapped Component type-safe, use "interface merging" like so: * @example * interface MyComponent extends EventsMixinClass {} * class MyComponent extends React.Component { ... } */ export interface EventsMixinClass { renderContainer( component: React.ReactElement, children: React.ReactElement | React.ReactElement[], ): React.ReactElement; cacheValues(this: TThis, obj: Partial): void; getEventState: typeof Events.getEventState; renderData(props: TProps, shouldRenderDatum?: () => boolean); renderContinuousData(props: TProps); animateComponent( props: TProps, defaultAnimationWhitelist: string[], ): React.ReactElement; getComponentProps( component: React.ReactNode, type: string, index: string | number, ): TProps; dataKeys: string[]; } /** * These fields are calculated by the Mixin */ export interface EventMixinCalculatedValues { componentEvents: Array; getSharedEventState: (key: string, value: string) => unknown; baseProps: Record; dataKeys: string[]; hasEvents: unknown; events: unknown; } /** * These are the common roles that we care about internally. */ export type VictoryComponentCommonRole = | "container" | "group" | "histogram" | "label" | "line" | "portal" | "stack" | "tooltip" | "voronoi"; /** * A component can have any "role", * but there are certain ones that we actually care about internally */ export type VictoryComponentRole = VictoryComponentCommonRole | string; /** * Static component fields used by Victory for common behavior */ export interface VictoryComponentConfiguration { getBaseProps?(props: TProps): EventMixinCalculatedValues["baseProps"]; role?: VictoryComponentRole; expectedComponents?: Array; getChildren?: ( props: TProps, childComponents?: Array, calculatedProps?: TProps, ) => void; animationWhitelist?: Array; } /** * This represents the class itself, including static fields */ export interface WrappedComponentClass extends VictoryComponentConfiguration { new (props: TProps): React.Component; } export function addEvents< TBase extends WrappedComponentClass, TProps extends EventMixinCommonProps, >(WrappedComponent: TBase, options: MixinOptions = {}) { // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface AddEventsMixin extends EventMixinCalculatedValues {} // @ts-expect-error "TS2545: A mixin class must have a constructor with a single rest parameter of type 'any[]'." class AddEventsMixin extends WrappedComponent implements EventsMixinClass { constructor(props: TProps) { super(props); this.cacheValues(this.getCalculatedValues(props)); } state = {}; getEventState = Events.getEventState.bind(this); getScopedEvents = Events.getScopedEvents.bind(this); getEvents = (p, target, eventKey) => { return Events.getEvents.call( this, p, target, eventKey, this.getScopedEvents, ); }; externalMutations = this.getExternalMutations(this.props); calculatedState = this.getStateChanges(this.props); globalEvents = {}; prevGlobalEventKeys: string[] = []; boundGlobalEvents = {}; shouldComponentUpdate(nextProps: TProps) { const externalMutations = this.getExternalMutations(nextProps); // @ts-expect-error "Property 'animating' does not exist on type EventMixinCommonProps" const animating = this.props.animating || this.props.animate; const newMutation = !isEqual(externalMutations, this.externalMutations); if (animating || newMutation) { this.cacheValues(this.getCalculatedValues(nextProps)); this.externalMutations = externalMutations; this.applyExternalMutations(nextProps, externalMutations); return true; } const calculatedState = this.getStateChanges(nextProps); if (!isEqual(this.calculatedState, calculatedState)) { this.cacheValues(this.getCalculatedValues(nextProps)); return true; } if (!isEqual(this.props, nextProps)) { this.cacheValues(this.getCalculatedValues(nextProps)); return true; } return false; } componentDidMount() { const globalEventKeys = Object.keys(this.globalEvents); globalEventKeys.forEach((key) => this.addGlobalListener(key)); this.prevGlobalEventKeys = globalEventKeys; } componentDidUpdate(prevProps) { const calculatedState = this.getStateChanges(prevProps); this.calculatedState = calculatedState; const globalEventKeys = Object.keys(this.globalEvents); const removedGlobalEventKeys = difference( this.prevGlobalEventKeys, globalEventKeys, ); removedGlobalEventKeys.forEach((key) => this.removeGlobalListener(key)); const addedGlobalEventKeys = difference( globalEventKeys, this.prevGlobalEventKeys, ); addedGlobalEventKeys.forEach((key) => this.addGlobalListener(key)); this.prevGlobalEventKeys = globalEventKeys; } componentWillUnmount() { this.prevGlobalEventKeys.forEach((key) => this.removeGlobalListener(key)); } addGlobalListener(key) { const boundListener = (event) => { const listener = this.globalEvents[key]; return listener && listener(Events.emulateReactEvent(event)); }; this.boundGlobalEvents[key] = boundListener; window.addEventListener( Events.getGlobalEventNameFromKey(key), boundListener, ); } removeGlobalListener(key) { window.removeEventListener( Events.getGlobalEventNameFromKey(key), this.boundGlobalEvents[key], ); } // compile all state changes from own and parent state. Order doesn't matter, as any state // state change should trigger a re-render getStateChanges(props) { if (!this.hasEvents) { return {}; } const getState = (key, type) => { const result = defaults( {}, this.getEventState(key, type), this.getSharedEventState(key, type), ); return isEmpty(result) ? undefined : result; }; const components = options.components || defaultComponents; const stateChanges = components .map((component) => { if (!props.standalone && component.name === "parent") { // don't check for changes on parent props for non-standalone components return undefined; } return component.index !== undefined ? getState(component.index, component.name) : this.dataKeys .map((key) => getState(key, component.name)) .filter(Boolean); }) .filter(Boolean); return stateChanges; } applyExternalMutations(props, externalMutations) { if (!isEmpty(externalMutations)) { const callbacks = props.externalEventMutations.reduce( (memo, mutation) => isFunction(mutation.callback) ? memo.concat(mutation.callback) : memo, [] as Array<() => void>, ); const compiledCallbacks = callbacks.length ? () => { callbacks.forEach((c) => c()); } : undefined; this.setState(externalMutations, compiledCallbacks); } } getCalculatedValues(props): EventMixinCalculatedValues { const { sharedEvents } = props; const components = WrappedComponent.expectedComponents; const componentEvents = Events.getComponentEvents(props, components); const getSharedEventState = sharedEvents && isFunction(sharedEvents.getEventState) ? sharedEvents.getEventState : () => undefined; const baseProps = this.getBaseProps(props, getSharedEventState); const dataKeys = Object.keys(baseProps).filter((key) => key !== "parent"); const hasEvents = props.events || props.sharedEvents || componentEvents; const events = this.getAllEvents(props); return { componentEvents, getSharedEventState, baseProps, dataKeys, hasEvents, events, }; } getExternalMutations(props: TProps) { const { sharedEvents, externalEventMutations } = props; return isEmpty(externalEventMutations) || sharedEvents ? undefined : Events.getExternalMutations( externalEventMutations, this.baseProps, this.state, ); } cacheValues(obj) { Object.keys(obj).forEach((key) => { this[key] = obj[key]; }); } getBaseProps(props, getSharedEventState): this["baseProps"] { const getSharedEventStateFunction = getSharedEventState || this.getSharedEventState.bind(this); const sharedParentState = getSharedEventStateFunction("parent", "parent"); const parentState = this.getEventState("parent", "parent"); const baseParentProps = defaults({}, parentState, sharedParentState); const parentPropsList = baseParentProps.parentControlledProps; const parentProps = parentPropsList ? pick(baseParentProps, parentPropsList) : {}; const modifiedProps = defaults({}, parentProps, props); return typeof WrappedComponent.getBaseProps === "function" ? WrappedComponent.getBaseProps(modifiedProps) : {}; } getAllEvents(props) { if (Array.isArray(this.componentEvents)) { return Array.isArray(props.events) ? this.componentEvents.concat(...props.events) : this.componentEvents; } return props.events; } getComponentProps( component: React.ReactNode, type: string, index: string | number, ) { const name = this.props.name || WrappedComponent.role; const key = (this.dataKeys && this.dataKeys[index]) || index; const id = `${name}-${type}-${key}`; const baseProps = (this.baseProps[key] && this.baseProps[key][type]) || this.baseProps[key]; if (!baseProps && !this.hasEvents) { return undefined; } const currentProps = component && typeof component === "object" && "props" in component ? component.props : undefined; if (this.hasEvents) { const baseEvents = this.getEvents(this.props, type, key); const componentProps = defaults( { index, key: id }, this.getEventState(key, type), this.getSharedEventState(key, type), currentProps, baseProps, { id }, ); const events = defaults( {}, Events.getPartialEvents(baseEvents, key, componentProps), componentProps.events, ); return Object.assign({}, componentProps, { events }); } return defaults({ index, key: id }, currentProps, baseProps, { id }); } renderContainer(component, children) { const isContainer = component.type && component.type.role === "container"; const parentProps = isContainer ? this.getComponentProps(component, "parent", "parent") : {}; if (parentProps.events) { this.globalEvents = Events.getGlobalEvents(parentProps.events); parentProps.events = Events.omitGlobalEvents(parentProps.events); } return React.cloneElement(component, parentProps, children); } animateComponent( props: TProps, defaultAnimationWhitelist: string[], ): React.ReactElement { const animationWhitelist = (typeof props.animate === "object" && props.animate?.animationWhitelist) || defaultAnimationWhitelist; const Comp = this.constructor; return ( ); } // Used by `VictoryLine` and `VictoryArea` renderContinuousData(props: TProps) { const { dataComponent, labelComponent, groupComponent } = props; const dataKeys = this.dataKeys.filter((value) => value !== "all"); const labelComponents = dataKeys.reduce((memo, key) => { let newMemo = memo; const labelProps = this.getComponentProps( labelComponent, "labels", key, ); if ( labelProps && labelProps.text !== undefined && labelProps.text !== null ) { newMemo = newMemo.concat( React.cloneElement(labelComponent!, labelProps), ); } return newMemo; }, [] as React.ReactElement[]); const dataProps = this.getComponentProps(dataComponent, "data", "all"); const children = [ React.cloneElement(dataComponent!, dataProps), ...labelComponents, ]; return this.renderContainer(groupComponent, children); } renderData(props, shouldRenderDatum = datumHasXandY) { const { dataComponent, labelComponent, groupComponent } = props; const dataComponents = this.dataKeys.reduce( (validDataComponents, _dataKey, index) => { const dataProps = this.getComponentProps( dataComponent, "data", index, ); if (shouldRenderDatum(dataProps.datum)) { validDataComponents.push( React.cloneElement(dataComponent, dataProps), ); } return validDataComponents; }, [] as React.ReactElement[], ); const labelComponents = this.dataKeys .map((_dataKey, index) => { const labelProps = this.getComponentProps( labelComponent, "labels", index, ); if (labelProps.text !== undefined && labelProps.text !== null) { return React.cloneElement(labelComponent, labelProps); } return undefined; }) .filter(Boolean); const children = [...dataComponents, ...labelComponents]; return this.renderContainer(groupComponent, children); } } return AddEventsMixin; } ================================================ FILE: packages/victory-core/src/victory-util/axis.test.tsx ================================================ /* eslint-disable react/no-multi-comp */ import React from "react"; import * as Axis from "./axis"; import * as Scale from "./scale"; describe("helpers/axis", () => { describe("isVertical", () => { it("returns true when the orientation is vertical", () => { const props = { orientation: "left" }; const verticalResult = Axis.isVertical(props); expect(verticalResult).toEqual(true); }); it("returns false when the orientation is horizontal", () => { const props = { orientation: "bottom" }; const verticalResult = Axis.isVertical(props); expect(verticalResult).toEqual(false); }); }); describe("getDomain", () => { it("determines a domain from tickValues", () => { const props = { tickValues: [1, 2, 3] }; const domainResult = Axis.getDomain(props); expect(domainResult).toEqual([1, 3]); }); it("determines a domain from string tick values", () => { const props = { tickValues: ["a", "b", "c", "d"] }; const domainResult = Axis.getDomain(props); expect(domainResult).toEqual([1, 4]); }); it("reverses a domain from tickValues when the axis is vertical", () => { const props = { tickValues: [1, 2, 3], dependentAxis: true }; const domainResult = Axis.getDomain(props); expect(domainResult).toEqual([3, 1]); }); it("determines a domain from props", () => { const props = { domain: [1, 2] }; const domainResult = Axis.getDomain(props); expect(domainResult).toEqual([1, 2]); }); it("calculates a domain from a single tickValue", () => { const props = { tickValues: [1] }; const domainResult = Axis.getDomain(props); const verySmallNumber = Math.pow(10, -10); expect(domainResult).toEqual([1 - verySmallNumber, 1 + verySmallNumber]); }); it("returns undefined if the given axis doesn't match this axis", () => { const props = { domain: [1, 3] }; const domainResultX = Axis.getDomain(props, "x"); expect(domainResultX).toEqual([1, 3]); const domainResultY = Axis.getDomain(props, "y"); expect(domainResultY).toBeUndefined(); }); }); describe("getAxis", () => { it("determines the axis based on type (dependent / independent)", () => { expect(Axis.getAxis({ dependentAxis: true })).toEqual("y"); expect(Axis.getAxis({})).toEqual("x"); }); }); describe("getAxisComponent", () => { const MockVictoryAxis = (props) =>
; MockVictoryAxis.getAxis = (props) => (props.dependentAxis ? "y" : "x"); MockVictoryAxis.role = "axis"; const MockVictoryBar = (props) =>
; const dependentAxis = ; const independentAxis = ; const bar = ; beforeEach(() => { jest.spyOn(dependentAxis.type, "getAxis"); }); afterEach(() => { jest.clearAllMocks(); }); it("returns the independent axis when called with 'x'", () => { const childComponents = [dependentAxis, independentAxis, bar]; const componentResult = Axis.getAxisComponent(childComponents, "x"); expect(dependentAxis.type.getAxis).toBeCalledWith(dependentAxis.props); expect(independentAxis.type.getAxis).toBeCalledWith( independentAxis.props, ); expect(componentResult).toEqual(independentAxis); }); }); describe("getTickFormat", () => { const scale = Scale.getBaseScale({ scale: { x: "linear" } }, "x"); const ticks = [1, 2, 3, 4, 5]; beforeEach(() => { jest.spyOn(scale, "tickFormat"); }); afterEach(() => { jest.clearAllMocks(); }); it("returns tickFormat function from props", () => { const props = { tickFormat: (x) => x * 5 }; const tickProps = { scale, ticks }; const formatResult = Axis.getTickFormat(props, tickProps); expect(scale.tickFormat).not.toHaveBeenCalled(); expect(formatResult).toEqual(props.tickFormat); }); it("converts tickFormat array from props to a function", () => { const props = { tickFormat: [1, 2, 3, 4, 5] }; const tickProps = { scale, ticks }; const formatResult = Axis.getTickFormat(props, tickProps); expect(scale.tickFormat).not.toHaveBeenCalled(); expect(formatResult).toBeInstanceOf(Function); }); it("converts tickFormat string array from props to a function", () => { const props = { tickValues: ["cats", "dogs", "birds"] }; const tickProps = { scale, ticks }; const formatResult = Axis.getTickFormat(props, tickProps); expect(scale.tickFormat).not.toHaveBeenCalled(); expect(formatResult).toBeInstanceOf(Function); }); it("calculates a tick format from scale", () => { const props = {}; const tickProps = { scale, ticks }; const formatResult = Axis.getTickFormat(props, tickProps); expect(formatResult).toBeInstanceOf(Function); }); }); describe("getTicks", () => { const scale = Scale.getBaseScale({ scale: { x: "linear" } }, "x"); beforeEach(() => { jest.spyOn(scale, "ticks"); }); afterEach(() => { jest.clearAllMocks(); }); it("returns tickValues from props", () => { const props = { tickValues: [1, 2, 3] }; const tickResult = Axis.getTicks(props, scale); expect(tickResult).toEqual(props.tickValues); }); it("returns converts string tickValues to numbers", () => { const props = { tickValues: ["a", "b", "c", "d"] }; const tickResult = Axis.getTicks(props, scale); expect(tickResult).toEqual([1, 2, 3, 4]); }); it("calculates tickValues from scale.ticks()", () => { const props = { tickCount: 5 }; Axis.getTicks(props, scale); expect(scale.ticks).toBeCalledWith(5); }); // TODO: This is failing, but I'm not sure whether it's an issue with the test or the code it.skip("calculates tickValues from scale.ticks(), and removes zero if axes cross", () => { const props = { tickCount: 5, crossAxis: true }; const tickResult = Axis.getTicks(props, scale); expect(scale.ticks).toBeCalledWith(5); expect(tickResult).not.toContain(0); }); }); }); ================================================ FILE: packages/victory-core/src/victory-util/axis.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import isObject from "lodash/isObject"; import uniq from "lodash/uniq"; import orderBy from "lodash/orderBy"; import * as Collection from "./collection"; import * as Domain from "./domain"; import * as Helpers from "./helpers"; import { D3Scale } from "../types/prop-types"; /** * Returns the first argument it receives * @param value The value to return * @returns The value given */ function identity(value: T): T { return value; } /** * Returns the axis (x or y) of a particular axis component * @param {Object} props: the props object. * @param {Boolean} horizontal: true for horizontal charts * @returns {String} the dimension appropriate for the axis given its props */ export function getAxis(props) { const { dependentAxis } = props; return dependentAxis ? "y" : "x"; } /** * Returns all axis components that pass a given predicate * @param {Array} childComponents: an array of children * @param {Function} predicate: a predicate function that will be called with each * @returns {Array} all axis components that pass the given predicate or [] */ export function findAxisComponents(childComponents, predicate?) { const predicateFunction = predicate || identity; const findAxes = (children) => { return children.reduce((memo, child) => { if ( child.type && child.type.role === "axis" && predicateFunction(child) ) { return memo.concat(child); } else if (child.props && child.props.children) { return memo.concat( findAxes(React.Children.toArray(child.props.children)), ); } return memo; }, []); }; return findAxes(childComponents); } /** * Returns a single axis component of the desired axis type (x or y) * @param {Array} childComponents: an array of children * @param {String} axis: desired axis either "x" or "y". * @returns {ReactComponent} an axis component of the desired axis or undefined */ export function getAxisComponent(childComponents, axis) { const matchesAxis = (component) => { const type = component.type.getAxis(component.props); return type === axis; }; return findAxisComponents(childComponents, matchesAxis)[0]; } /** * Returns all axis components of the desired axis type (x or y) along with any * parent components excluding VictoryChart * @param {Array} childComponents: an optional array of children. * @param {String} type: desired axis either "dependent" or "independent". * @returns {ReactComponent} an axis component of the desired type or undefined */ export function getAxisComponentsWithParent(childComponents, type) { const matchesType = (child) => { return type === "dependent" ? child.props.dependentAxis : !child.props.dependentAxis; }; const findComponents = (children) => { return children.reduce((memo, child) => { if (child.type && child.type.role === "axis" && matchesType(child)) { return memo.concat(child); } else if (child.props && child.props.children) { const childAxis = findComponents( React.Children.toArray(child.props.children), ); return childAxis.length > 0 ? memo.concat(child) : memo; } return memo; }, []); }; return findComponents(childComponents); } export function getOrigin(domain) { const getSingleOrigin = (d) => { const domainMin = Math.min(...d); const domainMax = Math.max(...d); return domainMax < 0 ? domainMax : Math.max(0, domainMin); }; return { x: Collection.containsDates(domain.x) ? new Date(Math.min(...domain.x)) : getSingleOrigin(domain.x), y: Collection.containsDates(domain.y) ? new Date(Math.min(...domain.y)) : getSingleOrigin(domain.y), }; } export function getOriginSign(origin, domain) { const getSign = () => { return origin <= 0 && Math.max(...domain) <= 0 ? "negative" : "positive"; }; return Collection.containsDates(domain) ? "positive" : getSign(); } /** * @param {Object} props: axis component props * @returns {Boolean} true when the axis is vertical */ export function isVertical(props) { const orientation = props.orientation || (props.dependentAxis ? "left" : "bottom"); const vertical = { top: false, bottom: false, left: true, right: true }; return vertical[orientation]; } /** * @param {Object} props: axis component props * @returns {Boolean} true when tickValues contain strings */ export function stringTicks(props) { return ( props.tickValues !== undefined && Collection.containsStrings(props.tickValues) ); } function getDefaultTickFormat(props) { const { tickValues } = props; const axis = getAxis(props); const stringMap = props.stringMap && props.stringMap[axis]; const fallbackFormat = tickValues && !Collection.containsDates(tickValues) ? (x) => x : undefined; if (!stringMap) { return stringTicks(props) ? (x, index) => tickValues[index] : fallbackFormat; } const invertedStringMap = stringMap && Helpers.invert(stringMap); const tickValueArray = orderBy(Object.values(stringMap), (n) => n); const dataNames = tickValueArray.map((tick: any) => invertedStringMap[tick]); // string ticks should have one tick of padding at the beginning const dataTicks = ["", ...dataNames, ""]; return (x) => dataTicks[x]; } function getStringTicks(props) { const axis = getAxis(props); const stringMap = props.stringMap && props.stringMap[axis]; const categories = Array.isArray(props.categories) ? props.categories : props.categories && props.categories[axis]; const ticksFromCategories = categories && Collection.containsOnlyStrings(categories) ? categories.map((tick) => stringMap[tick]) : undefined; const ticksFromStringMap = stringMap && Object.values(stringMap); return ticksFromCategories && ticksFromCategories.length !== 0 ? ticksFromCategories : ticksFromStringMap; } function getTickArray(props) { const { tickValues, tickFormat } = props; if (tickValues?.length === 0) { return []; } const axis = getAxis(props); const stringMap = props.stringMap && props.stringMap[axis]; const getTicksFromFormat = () => { if (!tickFormat || !Array.isArray(tickFormat)) { return undefined; } return Collection.containsStrings(tickFormat) ? tickFormat.map((t, i) => i) : tickFormat; }; let ticks = tickValues; if (stringMap) { ticks = getStringTicks(props); } if (tickValues && Collection.containsStrings(tickValues)) { ticks = stringMap ? tickValues.map((tick) => stringMap[tick]) : Helpers.range(1, tickValues.length + 1); } const tickArray = ticks ? uniq(ticks) : getTicksFromFormat(); const buildTickArray = (arr: number[]) => { const newTickArray = [] as Array<{ value: number; index: number }>; const domain = (props.domain && props.domain[axis]) || props.domain; if (arr) { arr.forEach((t, index) => { if (Array.isArray(domain)) { if ( t >= Collection.getMinValue(domain).valueOf() && t <= Collection.getMaxValue(domain).valueOf() ) { newTickArray.push({ value: t, index, }); } } else { newTickArray.push({ value: t, index, }); } }); return newTickArray; } return undefined; }; return Array.isArray(tickArray) && tickArray.length ? buildTickArray(tickArray) : undefined; } export function getTickFormat(props, scale) { const { tickFormat } = props; const axis = getAxis(props); const stringMap = props.stringMap && props.stringMap[axis]; if (!tickFormat) { const defaultTickFormat = getDefaultTickFormat(props); // If there is no user-provided tick format, we use d3's tickFormat function // by default. This changed the default formatting for some scale types when // we upgraded to d3-scale@4.. const scaleTickFormat = scale.tickFormat && Helpers.isFunction(scale.tickFormat) ? scale.tickFormat() : (x) => x; return defaultTickFormat || scaleTickFormat; } else if (tickFormat && Array.isArray(tickFormat)) { const tickArray = getTickArray(props); const tickArrayIndices = tickArray?.map((v) => v.index); const filteredTickFormat = tickFormat.filter((t, index) => tickArrayIndices?.includes(index), ); return (x, index) => filteredTickFormat[index]; } else if (tickFormat && Helpers.isFunction(tickFormat)) { const applyStringTicks = (tick, index, ticks) => { const invertedStringMap = Helpers.invert(stringMap); const stringTickArray = ticks.map((t) => invertedStringMap[t]); return props.tickFormat(invertedStringMap[tick], index, stringTickArray); }; return stringMap ? applyStringTicks : tickFormat; } return (x) => x; } function downsampleTicks(ticks: number[], tickCount: number) { if (!tickCount || !Array.isArray(ticks) || ticks.length <= tickCount) { return ticks; } const k = Math.floor(ticks.length / tickCount); return ticks.filter((d, i) => i % k === 0); } export function getTicks(props, scale: D3Scale, filterZero = false) { const { tickCount } = props; const tickArray = getTickArray(props); if (tickArray?.length === 0) { return [""]; } const tickValues = tickArray ? tickArray.map((v) => v.value) : undefined; if (tickValues) { return downsampleTicks(tickValues, tickCount); } else if (scale.ticks && Helpers.isFunction(scale.ticks)) { // eslint-disable-next-line no-magic-numbers const defaultTickCount = tickCount || 5; const scaleTicks = scale.ticks(defaultTickCount); const scaledTickArray = Array.isArray(scaleTicks) && scaleTicks.length ? scaleTicks : scale.domain(); const ticks = downsampleTicks(scaledTickArray, tickCount); if (filterZero) { const filteredTicks = ticks.filter((value) => value !== 0); return filteredTicks.length ? filteredTicks : ticks; } return ticks; } return scale.domain(); } /** * Returns a domain based tickValues * @param {Object} props: the props object * @param {String} axis: either x or y * @returns {Array} returns a domain from tickValues */ function getDomainFromData(props, axis) { const { polar, startAngle = 0, endAngle = 360 } = props; const tickArray = getTickArray(props); const tickValues = tickArray && tickArray?.length !== 0 ? tickArray.map((v) => v.value) : undefined; if (!Array.isArray(tickValues)) { return undefined; } const minDomain = Domain.getMinFromProps(props, axis); const maxDomain = Domain.getMaxFromProps(props, axis); const tickStrings = stringTicks(props); const ticks = tickValues.map((value) => Number(value)); const defaultMin = tickStrings ? 1 : Collection.getMinValue(ticks); const defaultMax = tickStrings ? tickValues.length : Collection.getMaxValue(ticks); const min = minDomain !== undefined ? minDomain : defaultMin; const max = maxDomain !== undefined ? maxDomain : defaultMax; const initialDomain = Domain.getDomainFromMinMax(min, max); const domain = polar && axis === "x" && Math.abs(startAngle - endAngle) === 360 ? Domain.getSymmetricDomain(initialDomain, ticks) : initialDomain; if (isVertical(props) && !polar) { domain.reverse(); } return domain; } // exposed for use by VictoryChart export function getDomain(props, axis?) { const inherentAxis = getAxis(props); if (axis && axis !== inherentAxis) { return undefined; } return Domain.createDomainFunction(getDomainFromData)(props, inherentAxis); } export function getAxisValue(props, axis) { if (!props.axisValue) { return undefined; } const scaleAxis = axis === "x" ? "y" : "x"; const scale = isObject(props.scale) && Helpers.isFunction(props.scale[scaleAxis]) ? props.scale[scaleAxis] : undefined; if (!scale) { return undefined; } const stringMapAxis = axis === "x" ? "y" : "x"; const stringMap = isObject(props.stringMap) && props.stringMap[stringMapAxis]; const axisValue = stringMap && typeof props.axisValue === "string" ? stringMap[props.axisValue] : props.axisValue; return scale(axisValue); } export function modifyProps(props, fallbackProps) { if (!isObject(props.theme)) { return Helpers.modifyProps(props, fallbackProps, "axis"); } let role = "axis"; if (props.dependentAxis && props.theme.dependentAxis) { role = "dependentAxis"; } else if (!props.dependentAxis && props.theme.independentAxis) { role = "independentAxis"; } if (role === "axis") { return Helpers.modifyProps(props, fallbackProps, "axis"); } const axisTheme = defaults({}, props.theme[role], props.theme.axis); const theme = Object.assign({}, props.theme, { axis: axisTheme }); return Helpers.modifyProps( Object.assign({}, props, { theme }), fallbackProps, "axis", ); } ================================================ FILE: packages/victory-core/src/victory-util/collection.test.ts ================================================ import * as Collection from "./collection"; describe("victory-util/collection", () => { describe("containsStrings", () => { it("handles empty argument", () => { // @ts-expect-error "Method expects 1 argument" expect(Collection.containsStrings()).toEqual(false); }); it("handles empty array", () => { expect(Collection.containsStrings([])).toEqual(false); }); it("returns false for collections of non-strings", () => { expect(Collection.containsStrings([0, 1])).toEqual(false); expect(Collection.containsStrings([undefined, null, NaN])).toEqual(false); expect(Collection.containsStrings([{}, { a: "foo" }])).toEqual(false); }); it("returns false for collections with strings", () => { expect(Collection.containsStrings(["hello"])).toEqual(true); expect(Collection.containsStrings(["hello", "there"])).toEqual(true); expect(Collection.containsStrings([0, "hello", {}, null])).toEqual(true); }); }); describe("containsOnlyStrings", () => { it("handles empty argument", () => { // @ts-expect-error "Method expects 1 argument" expect(Collection.containsOnlyStrings()).toEqual(false); }); it("handles empty array", () => { expect(Collection.containsOnlyStrings([])).toEqual(false); }); it("returns false for collections of non-strings", () => { expect(Collection.containsOnlyStrings([0, 1])).toEqual(false); expect(Collection.containsOnlyStrings([undefined, null, NaN])).toEqual( false, ); expect(Collection.containsOnlyStrings([{}, { a: "foo" }])).toEqual(false); }); it("returns false for collections with some strings", () => { expect(Collection.containsOnlyStrings(["hello", 0])).toEqual(false); expect(Collection.containsOnlyStrings(["hello", ["not me"]])).toEqual( false, ); expect(Collection.containsOnlyStrings([0, "hello", {}, null])).toEqual( false, ); }); it("returns true for collections with only strings", () => { expect(Collection.containsOnlyStrings(["hello"])).toEqual(true); expect(Collection.containsOnlyStrings(["hello", "there"])).toEqual(true); }); }); describe("difference", () => { it("handles empty arguments", () => { // @ts-expect-error "Method expects 2 arguments" expect(Collection.difference()).toEqual([]); }); it("handles empty arrays", () => { expect(Collection.difference([], [])).toEqual([]); }); it("returns an empty array if there are no differences", () => { expect(Collection.difference([1, 2], [1, 2])).toEqual([]); }); it("returns the difference between two arrays", () => { expect(Collection.difference([1, 2], [2, 3])).toEqual([1]); }); it("returns the difference between two unequal arrays", () => { expect(Collection.difference([1, 2, 3, 4, 5], [5, 2, 10])).toEqual([ 1, 3, 4, ]); }); }); describe("isArrayOfArrays", () => { it("handles empty argument", () => { // @ts-expect-error "Method expects 1 argument" expect(Collection.isArrayOfArrays()).toEqual(false); }); it("handles empty array", () => { expect(Collection.isArrayOfArrays([])).toEqual(false); }); it("returns false for collections of non-arrays", () => { expect(Collection.isArrayOfArrays([1])).toEqual(false); expect(Collection.isArrayOfArrays([{}])).toEqual(false); expect(Collection.isArrayOfArrays(["a"])).toEqual(false); }); it("returns false for mixed collections", () => { expect(Collection.isArrayOfArrays([[], 1, {}])).toEqual(false); expect(Collection.isArrayOfArrays([1, [], {}])).toEqual(false); expect(Collection.isArrayOfArrays([1, {}, []])).toEqual(false); }); it("returns true for collections of arrays", () => { expect(Collection.isArrayOfArrays([[]])).toEqual(true); expect(Collection.isArrayOfArrays([[{}]])).toEqual(true); expect(Collection.isArrayOfArrays([[[]]])).toEqual(true); expect(Collection.isArrayOfArrays([[], []])).toEqual(true); }); }); describe("removeUndefined", () => { it("handles empty array", () => { expect(Collection.removeUndefined([])).toEqual([]); }); it("does not filter non-undefineds", () => { const testArray = [0, 1, "a", {}, false, null, NaN]; expect(Collection.removeUndefined(testArray)).toEqual(testArray); }); it("filters out undefineds", () => { const testArray = [ undefined, 0, undefined, {}, false, null, NaN, undefined, ]; const expectedArray = [0, {}, false, null, NaN]; expect(Collection.removeUndefined(testArray)).toEqual(expectedArray); }); }); describe("getMaxValue", () => { it("returns a date if array contains dates", () => { const array = [new Date(2016, 3, 6), new Date(2017, 5, 3), 10]; expect(Collection.getMaxValue(array)).toEqual(new Date(2017, 5, 3)); }); it("returns a number if array does not contain dates", () => { const array = [3, 8, 10]; expect(Collection.getMaxValue(array)).toEqual(10); }); it("allows values to be concated and returns the appropriate number", () => { const array = [3, 8, 10]; expect(Collection.getMaxValue(array, 1, 20)).toEqual(20); }); }); describe("getMinValue", () => { it("returns a date if array contains dates", () => { const array = [ new Date(2016, 3, 6), new Date(2017, 5, 3), new Date(2015, 11, 4), ]; expect(Collection.getMinValue(array)).toEqual(new Date(2015, 11, 4)); }); it("returns a number if array does not contain dates", () => { const array = [3, 8, 10]; expect(Collection.getMinValue(array)).toEqual(3); }); it("allows values to be concated and returns the appropriate number", () => { const array = [3, 8, 10]; expect(Collection.getMinValue(array, 1, 20)).toEqual(1); }); }); }); ================================================ FILE: packages/victory-core/src/victory-util/collection.tsx ================================================ function isNonEmptyArray( collection: Array | unknown, ): collection is Array { return Array.isArray(collection) && collection.length > 0; } export function containsStrings(collection: Array) { return ( Array.isArray(collection) && collection.some((value) => typeof value === "string") ); } export function containsDates(collection: Array): boolean { return ( Array.isArray(collection) && collection.some((value) => value instanceof Date) ); } export function containsNumbers(collection) { return ( Array.isArray(collection) && collection.some((value) => typeof value === "number") ); } export function containsOnlyStrings( collection: Array | unknown, ): collection is Array { return ( isNonEmptyArray(collection) && collection.every((value) => typeof value === "string") ); } /** * Creates an array of array values not included in the other given arrays * @param a The array to inspect * @param b The values to exclude * @returns The new array of filtered values */ export function difference(a: Array, b: Array): Array { if (a && b) { return a.filter((value) => !b.includes(value)); } return []; } export function isArrayOfArrays( collection: Array | Array> | unknown, ): collection is Array> { return ( isNonEmptyArray(collection) && (collection as Array>).every(Array.isArray) ); } export function removeUndefined(arr) { return arr.filter((el) => el !== undefined); } export function getMaxValue( arr: Array, ...values: Array ): number | Date { const array = arr.concat(values) as number[]; return containsDates(array) ? new Date(Math.max(...array)) // Dates will be coerced to numbers : Math.max(...array); } export function getMinValue( arr: Array, ...values: Array ): number | Date { const array = arr.concat(values) as number[]; return containsDates(array) ? new Date(Math.min(...array)) // Dates will be coerced to numbers : Math.min(...array); } ================================================ FILE: packages/victory-core/src/victory-util/common-props.tsx ================================================ import * as React from "react"; import { OriginType } from "../victory-label/victory-label"; import { PaddingProps, VictoryThemeDefinition } from "../victory-theme/types"; import { AnimatePropTypeInterface, CategoryPropType, ColorScalePropType, D3Scale, DataGetterPropType, DomainPaddingPropType, DomainPropType, EventCallbackInterface, RangePropType, ScalePropType, SortOrderPropType, StringOrNumberOrList, } from "../types/prop-types"; import { NumberOrCallback, StringOrCallback } from "../types/callbacks"; export interface VictoryDatableProps { categories?: CategoryPropType; data?: readonly any[]; dataComponent?: React.ReactElement; domain?: DomainPropType; domainPadding?: DomainPaddingPropType; samples?: number; sortKey?: DataGetterPropType; sortOrder?: SortOrderPropType; x?: DataGetterPropType; y?: DataGetterPropType; y0?: DataGetterPropType; } export interface VictoryCommonThemeProps { animate?: boolean | AnimatePropTypeInterface; colorScale?: ColorScalePropType; containerComponent?: React.ReactElement; disableInlineStyles?: boolean; domainPadding?: DomainPaddingPropType; externalEventMutations?: EventCallbackInterface< string | string[], StringOrNumberOrList >[]; groupComponent?: React.ReactElement; height?: number; horizontal?: boolean; maxDomain?: number | { x?: number; y?: number }; minDomain?: number | { x?: number; y?: number }; name?: string; origin?: OriginType; padding?: PaddingProps; polar?: boolean; range?: RangePropType; scale?: | ScalePropType | D3Scale | { x?: ScalePropType | D3Scale; y?: ScalePropType | D3Scale; }; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type sharedEvents?: { events: any[]; getEventState: Function }; singleQuadrantDomainPadding?: boolean | { x?: boolean; y?: boolean }; standalone?: boolean; width?: number; } export interface VictoryCommonProps extends VictoryCommonThemeProps { theme?: VictoryThemeDefinition; } export interface VictoryCommonPrimitiveProps { active?: boolean; ariaLabel?: StringOrCallback; className?: string; clipPath?: string; data?: any; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type desc?: string | Function; disableInlineStyles?: boolean; events?: object; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type id?: number | string | Function; index?: number | string; origin?: OriginType; polar?: boolean; role?: string; scale?: any; shapeRendering?: string; style?: any; tabIndex?: NumberOrCallback; transform?: string; } ================================================ FILE: packages/victory-core/src/victory-util/data.test.tsx ================================================ /* eslint no-unused-expressions: 0, max-nested-callbacks: 0 */ import React from "react"; import { fromJS } from "immutable"; import * as Data from "./data"; import { VictoryPortal } from "../victory-portal/victory-portal"; const immutableDataTest = { createData: (data) => fromJS(data), testLabel: "data in immutable", }; const dataTest = { createData: (data) => data, testLabel: "data in js", }; describe("victory-util/data", () => { describe("createStringMap", () => { const tickValues = ["one", "two", "three"]; const categories = ["red", "green", "blue"]; it("returns a string map from strings in tickValues", () => { const props = { tickValues }; const stringMap = Data.createStringMap(props, "x"); expect(stringMap).toEqual({ one: 1, two: 2, three: 3 }); }); it("returns a string map from strings in categories", () => { const props = { categories }; const stringMap = Data.createStringMap(props, "x"); expect(stringMap).toEqual({ red: 1, green: 2, blue: 3 }); }); [dataTest, immutableDataTest].forEach(({ createData, testLabel }) => { describe(`returning string maps with ${testLabel}`, () => { const data = createData([ { x: "one", y: 1 }, { x: "red", y: 2 }, { x: "cat", y: 3 }, ]); it("returns a string map from strings in data", () => { const props = { data }; const stringMap = Data.createStringMap(props, "x"); expect(stringMap).toEqual({ one: 1, red: 2, cat: 3 }); }); it("a unique set of values is returned from multiple sources", () => { const props = { tickValues, data }; const stringMap = Data.createStringMap(props, "x"); expect(stringMap).toEqual({ one: 1, two: 2, three: 3, red: 4, cat: 5, }); }); }); }); }); [dataTest, immutableDataTest].forEach(({ createData, testLabel }) => { describe(`getStringsFromData with ${testLabel}`, () => { it("returns an array of strings from a data prop", () => { const props = { data: createData([ { x: "one", y: 1 }, { x: "red", y: 2 }, { x: "cat", y: 3 }, ]), }; const dataStrings = Data.getStringsFromData(props, "x"); expect(dataStrings).toEqual(["one", "red", "cat"]); }); it("returns an array of strings from array-type data", () => { const props = { data: createData([ ["one", 1], ["red", 2], ["cat", 3], ]), x: 0, y: 1, }; const dataStrings = Data.getStringsFromData(props, "x"); expect(dataStrings).toEqual(["one", "red", "cat"]); }); it("only returns strings, if data is mixed", () => { const props = { data: createData([ { x: 1, y: 1 }, { x: "three", y: 3 }, ]), }; expect(Data.getStringsFromData(props, "x")).toEqual(["three"]); }); it("returns an empty array when no strings are present", () => { const props = { data: createData([ { x: 1, y: 1 }, { x: 3, y: 3 }, ]), }; expect(Data.getStringsFromData(props, "x")).toEqual([]); }); it("returns an empty array when the data prop is undefined", () => { expect(Data.getStringsFromData({}, "x")).toEqual([]); }); }); }); describe("getStringsFromAxes", () => { it("returns an array of strings when tickValues is an array", () => { const props = { tickValues: [1, "three", 5] }; expect(Data.getStringsFromAxes(props, "x")).toEqual(["three"]); }); it("returns an array of strings when tickValues is an object", () => { const props = { tickValues: { x: [1, "three", 5] } }; expect(Data.getStringsFromAxes(props, "x")).toEqual(["three"]); }); it("returns an empty array when a given axis is not defined", () => { const props = { tickValues: { y: [1, "three", 5] } }; expect(Data.getStringsFromAxes(props, "x")).toEqual([]); }); it("returns an empty array when no strings are present", () => { const props = { tickValues: [1, 3, 5] }; expect(Data.getStringsFromAxes(props, "x")).toEqual([]); }); it("returns an empty array when the tickValues prop is undefined", () => { expect(Data.getStringsFromAxes({}, "x")).toEqual([]); }); }); describe("getStringsFromCategories", () => { it("returns an empty array when no strings are present", () => { const props = { categories: [1, 3, 5] }; expect(Data.getStringsFromCategories(props, "x")).toEqual([]); }); it("returns an empty array when the category prop is undefined", () => { expect(Data.getStringsFromCategories({}, "x")).toEqual([]); }); }); [dataTest, immutableDataTest].forEach(({ createData, testLabel }) => { describe(`formatData with ${testLabel}`, () => { it("formats a single dataset", () => { const dataset = [ { _x: 1, _y: 3, x: 1, y: 3 }, { _x: 2, _y: 5, x: 2, y: 5 }, ]; const props = { data: createData(dataset) }; const formatted = Data.formatData(dataset, props); expect(Array.isArray(formatted)).toBeTruthy(); expect(Object.keys(formatted[0])).toEqual( expect.arrayContaining(["_x", "_y", "x", "y"]), ); }); }); }); describe("getData", () => { [dataTest, immutableDataTest].forEach(({ createData, testLabel }) => { describe(`with ${testLabel}`, () => { it("formats and returns the data prop", () => { const data = createData([ { x: "kittens", y: 3 }, { x: "cats", y: 5 }, ]); const props = { data, x: "x", y: "y" }; const expectedReturnWithEventKeys = [ { _x: 1, x: "kittens", xName: "kittens", _y: 3, y: 3 }, { _x: 2, x: "cats", xName: "cats", _y: 5, y: 5 }, ]; const returnData = Data.getData(props); expect(returnData).toEqual(expectedReturnWithEventKeys); }); it("uses the event key when it is passed in", () => { const data = createData([ { x: 2, y: 2, eventKey: 13 }, { x: 1, y: 3, eventKey: 21 }, { x: 3, y: 1, eventKey: 11 }, ]); const returnData = Data.getData({ data }); expect(returnData).toEqual([ { _x: 2, x: 2, _y: 2, y: 2, eventKey: 13 }, { _x: 1, x: 1, _y: 3, y: 3, eventKey: 21 }, { _x: 3, x: 3, _y: 1, y: 1, eventKey: 11 }, ]); }); it("uses a custom event key when it is passed in", () => { const data = createData([ { x: 2, y: 2, myEventKey: 3 }, { x: 1, y: 3, myEventKey: 2 }, { x: 3, y: 1, myEventKey: 1 }, ]); const returnData = Data.getData({ data, eventKey: "myEventKey" }); expect(returnData).toEqual([ { _x: 2, x: 2, _y: 2, y: 2, eventKey: 3, myEventKey: 3 }, { _x: 1, x: 1, _y: 3, y: 3, eventKey: 2, myEventKey: 2 }, { _x: 3, x: 3, _y: 1, y: 1, eventKey: 1, myEventKey: 1 }, ]); }); it("uses a eventKey functions", () => { const data = createData([ { x: 2, y: 2 }, { x: 1, y: 3 }, { x: 3, y: 1 }, ]); const returnData = Data.getData({ data, eventKey: (d) => d.x }); expect(returnData).toEqual([ { _x: 2, x: 2, _y: 2, y: 2, eventKey: 2 }, { _x: 1, x: 1, _y: 3, y: 3, eventKey: 1 }, { _x: 3, x: 3, _y: 1, y: 1, eventKey: 3 }, ]); }); it("uses a eventKey functions with index", () => { const data = createData([ { x: 2, y: 2 }, { x: 1, y: 3 }, { x: 3, y: 1 }, ]); const returnData = Data.getData({ data, eventKey: (d, i) => i }); expect(returnData).toEqual([ { _x: 2, x: 2, _y: 2, y: 2, eventKey: 0 }, { _x: 1, x: 1, _y: 3, y: 3, eventKey: 1 }, { _x: 3, x: 3, _y: 1, y: 1, eventKey: 2 }, ]); }); it("does not sort data when sort key not passed", () => { const data = createData([ { x: 2, y: 2 }, { x: 1, y: 3 }, { x: 3, y: 1 }, ]); const returnData = Data.getData({ data }); expect(returnData).toEqual([ { _x: 2, x: 2, _y: 2, y: 2 }, { _x: 1, x: 1, _y: 3, y: 3 }, { _x: 3, x: 3, _y: 1, y: 1 }, ]); }); it("sorts data according to sort key", () => { const data = createData([ { x: 1, y: 1, order: 2 }, { x: 3, y: 3, order: 1 }, { x: 2, y: 2, order: 3 }, ]); const returnData = Data.getData({ data, sortKey: "order" }); expect(returnData).toEqual([ { _x: 3, x: 3, _y: 3, y: 3, order: 1 }, { _x: 1, x: 1, _y: 1, y: 1, order: 2 }, { _x: 2, x: 2, _y: 2, y: 2, order: 3 }, ]); }); it("sorts data according to sort key and sort order", () => { const data = createData([ { x: 1, y: 1, order: 2 }, { x: 3, y: 3, order: 1 }, { x: 2, y: 2, order: 3 }, ]); const returnData = Data.getData({ data, sortKey: "order", sortOrder: "descending", }); expect(returnData).toEqual([ { _x: 2, x: 2, _y: 2, y: 2, order: 3 }, { _x: 1, x: 1, _y: 1, y: 1, order: 2 }, { _x: 3, x: 3, _y: 3, y: 3, order: 1 }, ]); }); // Ensures previous VictoryLine api for sortKey prop stays consistent it("sorts data according to evaluated sort key when sort key is x or y", () => { const data = createData([ { _x: 2, x: 10, _y: 2, y: 10 }, { _x: 1, x: 20, _y: 3, y: 20 }, { _x: 3, x: 30, _y: 1, y: 30 }, ]); const returnDataX = Data.getData({ data, sortKey: "x" }); expect(returnDataX).toEqual([ { _x: 1, x: 20, _y: 3, y: 20 }, { _x: 2, x: 10, _y: 2, y: 10 }, { _x: 3, x: 30, _y: 1, y: 30 }, ]); const returnDataY = Data.getData({ data, sortKey: "y" }); expect(returnDataY).toEqual([ { _x: 3, x: 30, _y: 1, y: 30 }, { _x: 2, x: 10, _y: 2, y: 10 }, { _x: 1, x: 20, _y: 3, y: 20 }, ]); }); }); }); it("generates a dataset from domain", () => { const generatedReturn = [ { x: 0, y: 0 }, { x: 10, y: 10 }, ]; const props = { x: "x", y: "y", domain: { x: [0, 10], y: [0, 10] } }; const returnData = Data.generateData(props); expect(returnData).toEqual(generatedReturn); }); it("generates a dataset from negative domain", () => { const generatedReturn = [ { x: -10, y: 0 }, { x: 10, y: 10 }, ]; const props = { x: "x", y: "y", domain: { x: [-10, 10], y: [0, 10] } }; const returnData = Data.generateData(props); expect(returnData).toEqual(generatedReturn); }); it("generates a dataset from domain and samples", () => { const generatedReturn = [ { x: 0, y: 0 }, { x: 5, y: 5 }, { x: 10, y: 10 }, ]; const props = { x: "x", y: "y", domain: { x: [0, 10], y: [0, 10] }, samples: 2, }; const returnData = Data.generateData(props); expect(returnData).toEqual(generatedReturn); }); }); describe("isDataComponent", () => { class TestDataComponent extends React.Component { static role = "area"; } it("returns true when a component has a static role matching a whitelist", () => { expect(Data.isDataComponent()).toBe(true); }); it("returns false when a component has a role that does not match the whitelist", () => { // eslint-disable-next-line react/no-multi-comp class TestFooComponent extends React.Component { static role = "foo"; } expect(Data.isDataComponent()).toBe(false); }); it("returns true when a data component is wrapped in VictoryPortal", () => { expect( Data.isDataComponent( , ), ).toBe(true); }); }); }); ================================================ FILE: packages/victory-core/src/victory-util/data.ts ================================================ import React from "react"; import isEmpty from "lodash/isEmpty"; import isEqual from "lodash/isEqual"; import isPlainObject from "lodash/isPlainObject"; import isUndefined from "lodash/isUndefined"; import omitBy from "lodash/omitBy"; import orderBy from "lodash/orderBy"; import property from "lodash/property"; import uniq from "lodash/uniq"; import * as Helpers from "./helpers"; import * as Collection from "./collection"; import * as Scale from "./scale"; import * as Immutable from "./immutable"; // Private Functions function parseDatum(datum) { const immutableDatumWhitelist = { errorX: true, errorY: true, }; return Immutable.isImmutable(datum) ? Immutable.shallowToJS(datum, immutableDatumWhitelist) : datum; } function getLength(data: unknown[]) { return Immutable.isIterable(data) ? data.size : data.length; } // Returns generated data for a given axis based on domain and sample from props function generateDataArray(props, axis) { const propsDomain = isPlainObject(props.domain) ? props.domain[axis] : props.domain; const domain = propsDomain || Scale.getBaseScale(props, axis).domain(); const samples = props.samples || 1; const domainMax = Math.max(...domain); const domainMin = Math.min(...domain); const step = (domainMax - domainMin) / samples; const values = Helpers.range(domainMin, domainMax, step); return values[values.length - 1] === domainMax ? values : values.concat(domainMax); } // Returns sorted data. If no sort keys are provided, data is returned unaltered. function sortData(dataset, sortKey, sortOrder = "ascending") { if (!sortKey) { return dataset; } // Ensures previous VictoryLine api for sortKey prop stays consistent let formattedSortKey = sortKey; if (sortKey === "x" || sortKey === "y") { formattedSortKey = `_${sortKey}`; } const order = sortOrder === "ascending" ? "asc" : "desc"; return orderBy(dataset, formattedSortKey, order); } // This method will remove data points that break certain scales. (log scale only) function cleanData(dataset, props) { const smallNumber = 1 / Number.MAX_SAFE_INTEGER; const scaleType = { x: Scale.getScaleType(props, "x"), y: Scale.getScaleType(props, "y"), }; if (scaleType.x !== "log" && scaleType.y !== "log") { return dataset; } const rules = (datum, axis) => { return scaleType[axis] === "log" ? datum[`_${axis}`] !== 0 : true; }; const sanitize = (datum) => { const _x = rules(datum, "x") ? datum._x : smallNumber; const _y = rules(datum, "y") ? datum._y : smallNumber; const _y0 = rules(datum, "y0") ? datum._y0 : smallNumber; return Object.assign({}, datum, { _x, _y, _y0 }); }; return dataset.map((datum) => { if (rules(datum, "x") && rules(datum, "y") && rules(datum, "y0")) { return datum; } return sanitize(datum); }); } // Returns a data accessor given an eventKey prop function getEventKey(key) { // creates a data accessor function // given a property key, path, array index, or null for identity. if (Helpers.isFunction(key)) { return key; } else if (key === null || key === undefined) { return () => undefined; } // otherwise, assume it is an array index, property key or path (_.property handles all three) return property(key); } // Returns data with an eventKey prop added to each datum function addEventKeys(props, data) { const hasEventKeyAccessor = !!props.eventKey; const eventKeyAccessor = getEventKey(props.eventKey); return data.map((datum, index) => { if (datum.eventKey !== undefined) { return datum; } else if (hasEventKeyAccessor) { const eventKey = eventKeyAccessor(datum, index); return eventKey !== undefined ? Object.assign({ eventKey }, datum) : datum; } return datum; }); } // Exported Functions // This method will remove data points that fall outside of the desired domain (non-continuous charts only) export function formatDataFromDomain(dataset, domain, defaultBaseline?) { const exists = (val) => val !== undefined; const minDomainX = Collection.getMinValue(domain.x); const maxDomainX = Collection.getMaxValue(domain.x); const minDomainY = Collection.getMinValue(domain.y); const maxDomainY = Collection.getMaxValue(domain.y); const underMin = (min) => (val) => exists(val) && val < min; const overMax = (max) => (val) => exists(val) && val > max; const isUnderMinX = underMin(minDomainX); const isUnderMinY = underMin(minDomainY); const isOverMaxX = overMax(maxDomainX); const isOverMaxY = overMax(maxDomainY); return dataset.map((datum) => { let { _x, _y, _y0, _y1 } = datum; // single x point less than min domain if (isUnderMinX(_x) || isOverMaxX(_x)) _x = null; const baseline = exists(_y0) ? _y0 : defaultBaseline; const value = exists(_y1) ? _y1 : _y; if (!exists(value)) return datum; // value only and less than min domain or greater than max domain if (!exists(baseline) && (isUnderMinY(value) || isOverMaxY(value))) _y = null; // baseline and value are both less than min domain or both greater than max domain if ( (isUnderMinY(baseline) && isUnderMinY(value)) || (isOverMaxY(baseline) && isOverMaxY(value)) ) _y = _y0 = _y1 = null; // baseline and value with only baseline below min, set baseline to minDomainY if (isUnderMinY(baseline) && !isUnderMinY(value)) _y0 = minDomainY; // baseline and value with only baseline above max, set baseline to maxDomainY if (isOverMaxY(baseline) && !isOverMaxY(value)) _y0 = maxDomainY; return Object.assign({}, datum, omitBy({ _x, _y, _y0, _y1 }, isUndefined)); }); } /** * Returns an object mapping string data to numeric data * @param {Object} props: the props object * @param {String} axis: the current axis * @returns {Object} an object mapping string data to numeric data */ export function createStringMap(props, axis) { const stringsFromAxes = getStringsFromAxes(props, axis); const stringsFromCategories = getStringsFromCategories(props, axis); const stringsFromData = getStringsFromData(props, axis); const allStrings = uniq([ ...stringsFromAxes, ...stringsFromCategories, ...stringsFromData, ]); return allStrings.length === 0 ? null : allStrings.reduce((memo, string, index) => { memo[string] = index + 1; return memo; }, {}); } /** * Reduces the size of a data array, such that it is <= maxPoints. * @param {Array} data: an array of data; must be sorted * @param {Number} maxPoints: maximum number of data points to return * @param {Number} startingIndex: the index of the data[0] *in the entire dataset*; this function assumes `data` param is a subset of larger dataset that has been zoommed * @returns {Array} an array of data, a subset of data param */ export function downsample(data, maxPoints, startingIndex = 0) { // ensures that the downampling of data while zooming looks good. const dataLength = getLength(data); if (dataLength > maxPoints) { // limit k to powers of 2, e.g. 64, 128, 256 // so that the same points will be chosen reliably, reducing flicker on zoom const k = Math.pow(2, Math.ceil(Math.log2(dataLength / maxPoints))); return data.filter( // ensure modulo is always calculated from same reference: i + startingIndex (d, i) => (i + startingIndex) % k === 0, ); } return data; } /** * Returns formatted data. Data accessors are applied, and string values are replaced. * @param {Array} dataset: the original domain * @param {Object} props: the props object * @param {Array} expectedKeys: an array of expected data keys * @returns {Array} the formatted data */ export function formatData( dataset: any[], props: any, expectedKeys?: string[], ) { const isArrayOrIterable = Array.isArray(dataset) || Immutable.isIterable(dataset); if (!isArrayOrIterable || getLength(dataset) < 1) { return []; } const defaultKeys = ["x", "y", "y0"]; // TODO: We shouldn’t mutate the expectedKeys param here, // but we need to figure out why changing it causes regressions in tests. // eslint-disable-next-line no-param-reassign expectedKeys = Array.isArray(expectedKeys) ? expectedKeys : defaultKeys; const createAccessor = (name) => { return Helpers.createAccessor( props[name] !== undefined ? props[name] : name, ); }; const accessor = expectedKeys.reduce((memo, type) => { memo[type] = createAccessor(type); return memo; }, {}); const preformattedData = isEqual(expectedKeys, defaultKeys) && props.x === "_x" && props.y === "_y" && props.y0 === "_y0"; let stringMap; if (preformattedData === false) { // stringMap is not required if the data is preformatted stringMap = { x: expectedKeys.indexOf("x") !== -1 ? createStringMap(props, "x") : undefined, y: expectedKeys.indexOf("y") !== -1 ? createStringMap(props, "y") : undefined, y0: expectedKeys.indexOf("y0") !== -1 ? createStringMap(props, "y") : undefined, }; } const data = preformattedData ? dataset : dataset.reduce((dataArr, datum, index) => { const parsedDatum = parseDatum(datum); const fallbackValues = { x: index, y: parsedDatum }; const processedValues = expectedKeys!.reduce((memo, type) => { const processedValue = accessor[type](parsedDatum); const value = processedValue !== undefined ? processedValue : fallbackValues[type]; if (value !== undefined) { if (typeof value === "string" && stringMap[type]) { memo[`${type}Name`] = value; memo[`_${type}`] = stringMap[type][value]; } else { memo[`_${type}`] = value; } } return memo; }, {}); const formattedDatum = Object.assign({}, processedValues, parsedDatum); if (!isEmpty(formattedDatum)) { dataArr.push(formattedDatum); } return dataArr; }, []); const sortedData = sortData(data, props.sortKey, props.sortOrder); const cleanedData = cleanData(sortedData, props); return addEventKeys(props, cleanedData); } /** * Returns generated x and y data based on domain and sample from props * @param {Object} props: the props object * @returns {Array} an array of data */ export function generateData(props) { const xValues = generateDataArray(props, "x"); const yValues = generateDataArray(props, "y"); const values = xValues.map((x, i) => { return { x, y: yValues[i] }; }); return values; } /** * Returns an array of categories for a given axis * @param {Object} props: the props object * @param {String} axis: the current axis * @returns {Array} an array of categories */ export function getCategories(props, axis) { return props.categories && !Array.isArray(props.categories) ? props.categories[axis] : props.categories; } /** * Returns an array of formatted data * @param {Object} props: the props object * @returns {Array} an array of data */ export function getData(props) { return props.data ? formatData(props.data, props) : formatData(generateData(props), props); } /** * Returns an array of strings from axis tickValues for a given axis * @param {Object} props: the props object * @param {String} axis: the current axis * @returns {Array} an array of strings */ export function getStringsFromAxes(props, axis) { const { tickValues, tickFormat } = props; let tickValueArray; if (!tickValues || (!Array.isArray(tickValues) && !tickValues[axis])) { tickValueArray = tickFormat && Array.isArray(tickFormat) ? tickFormat : []; } else { tickValueArray = tickValues[axis] || tickValues; } return tickValueArray.filter((val) => typeof val === "string"); } /** * Returns an array of strings from categories for a given axis * @param {Object} props: the props object * @param {String} axis: the current axis * @returns {Array} an array of strings */ export function getStringsFromCategories(props, axis) { if (!props.categories) { return []; } const categories = getCategories(props, axis); const categoryStrings = categories && categories.filter((val) => typeof val === "string"); return categoryStrings ? Collection.removeUndefined(categoryStrings) : []; } /** * Returns an array of strings from data * @param {Object} props: the props object * @param {String} axis: the current axis * @returns {Array} an array of strings */ export function getStringsFromData(props, axis) { const isArrayOrIterable = Array.isArray(props.data) || Immutable.isIterable(props.data); if (!isArrayOrIterable) { return []; } const key = props[axis] === undefined ? axis : props[axis]; const accessor = Helpers.createAccessor(key); // support immutable data const data = props.data.reduce((memo, d) => { memo.push(parseDatum(d)); return memo; }, []); const sortedData = sortData(data, props.sortKey, props.sortOrder); const dataStrings = sortedData .reduce((dataArr, datum) => { const parsedDatum = parseDatum(datum); dataArr.push(accessor(parsedDatum)); return dataArr; }, []) .filter((datum) => typeof datum === "string"); // return a unique set of strings return dataStrings.reduce((prev, curr) => { if (curr !== undefined && curr !== null && prev.indexOf(curr) === -1) { prev.push(curr); } return prev; }, []); } /** * Checks whether a given component can be used to calculate data * @param {Component} component: a React component instance * @returns {Boolean} Returns true if the given component has a role included in the whitelist */ export function isDataComponent(component) { const getRole = (child) => { return child && child.type ? child.type.role : ""; }; let role = getRole(component); if (role === "portal") { const children = React.Children.toArray(component.props.children); role = children.length ? getRole(children[0]) : ""; } const whitelist = [ "area", "bar", "boxplot", "candlestick", "errorbar", "group", "histogram", "line", "pie", "scatter", "stack", "voronoi", ]; return whitelist.includes(role); } ================================================ FILE: packages/victory-core/src/victory-util/default-transitions.ts ================================================ import { AnimatePropTypeInterface } from "../types/prop-types"; export function continuousTransitions(): AnimatePropTypeInterface { return { onLoad: { duration: 2000, }, onExit: { duration: 500, }, onEnter: { duration: 500, }, }; } export function continuousPolarTransitions(): AnimatePropTypeInterface { return { onLoad: { duration: 2000, before: () => ({ _y: 0, _y1: 0, _y0: 0 }), after: (datum) => ({ _y: datum._y, _y1: datum._y1, _y0: datum._y0 }), }, onExit: { duration: 500, before: (datum, index, data) => { const adjacent = (attr) => { const adj = index === 0 ? data[index + 1] : data[index - 1]; return adj[attr]; }; return { _x: adjacent("_x"), _y: adjacent("_y"), _y0: adjacent("_y0"), }; }, }, onEnter: { duration: 500, before: (datum, index, data) => { const adjacent = (attr) => { const adj = index === 0 ? data[index + 1] : data[index - 1]; return adj[attr]; }; return { _x: adjacent("_x"), _y: adjacent("_y"), _y0: adjacent("_y0"), }; }, after: (datum) => ({ _x: datum._x, _y: datum._y, _y1: datum._y1, _y0: datum._y0, }), }, }; } export function discreteTransitions(): AnimatePropTypeInterface { return { onLoad: { duration: 2000, before: () => ({ opacity: 0 }), after: (datum) => datum, }, onExit: { duration: 600, before: () => ({ opacity: 0 }), }, onEnter: { duration: 600, before: () => ({ opacity: 0 }), after: (datum) => datum, }, }; } ================================================ FILE: packages/victory-core/src/victory-util/domain.test.tsx ================================================ import React from "react"; import { VictoryPortal } from "../victory-portal/victory-portal"; import * as Domain from "./domain"; describe("victory-util/domain", () => { describe("createDomainFunction", () => { it("returns a function equivalent to getDomain when no props are given", () => { const props = { x: "x", y: "y", domain: { y: [1, 2] }, data: [ { x: 1, y: 3 }, { x: 3, y: 5 }, ], }; const domainGetter = Domain.createDomainFunction(); expect(domainGetter(props, "x")).toEqual(Domain.getDomain(props, "x")); }); it("returns a function that uses a custom getDomainFromData function when given", () => { const props = { x: "x", y: "y", data: [ { x: 1, y: 3 }, { x: 3, y: 5 }, ], }; const getDomainFromData = () => [0, 10]; const domainGetter = Domain.createDomainFunction(getDomainFromData); expect(domainGetter(props, "x")).toEqual([0, 10]); }); it("returns a function that uses a custom formatDomain function when given", () => { const props = { domain: [0, 1] }; const formatDomain = () => [0, 10]; const domainGetter = Domain.createDomainFunction(null, formatDomain); expect(domainGetter(props, "x")).toEqual([0, 10]); }); }); describe("formatDomain", () => { const baseProps = { width: 100, height: 100, padding: 0 }; it("returns the domain when no domain padding is specified", () => { const domain = [0, 1]; const paddedDomain = Domain.formatDomain(domain, baseProps, "x"); expect(paddedDomain).toEqual(domain); }); it("pads the domain a particular number of pixels", () => { const domain = [0, 100]; const padding = [5, 10, 20]; padding.forEach((pad) => { const domainPadding = { x: pad }; const props = { ...baseProps, domainPadding }; const paddedDomain = Domain.formatDomain(domain, props, "x"); const adjustedDomain = domain[1] + pad; const adjustedPercent = adjustedDomain / (baseProps.width - baseProps.padding); const totalPadding = adjustedPercent * pad; expect(paddedDomain).toEqual([0, domain[1] + totalPadding]); }); }); it("filters zero from the domain for log scales", () => { const verySmallNumber = 1 / Number.MAX_SAFE_INTEGER; const props = { scale: { y: "log" } }; const formattedDomain = Domain.formatDomain([0, 1], props, "y"); expect(formattedDomain).toEqual([verySmallNumber, 1]); }); }); describe("getDomain", () => { it("gets the domain from props if they exist", () => { const props = { domain: [1, 2] }; const resultDomain = Domain.getDomain(props, "x"); expect(resultDomain).toEqual(props.domain); }); it("gets the domain from data if props don't exist for a particular axis", () => { const props = { x: "x", y: "y", domain: { y: [1, 2] }, data: [ { x: 1, y: 3 }, { x: 3, y: 5 }, ], }; const resultDomain = Domain.getDomain(props, "x"); expect(resultDomain).toEqual([1, 3]); }); }); describe("getDomainFromCategories", () => { it("calculates a domain from categories for the independent axis", () => { const props = { categories: [1, 2, 3] }; const domainResult = Domain.getDomainFromCategories(props, "x"); expect(domainResult).toEqual([1, 3]); }); it("calculates a domain from categories for the dependent axis", () => { const props = { categories: { y: [1, 2, 3] } }; const domainResult = Domain.getDomainFromCategories(props, "y"); expect(domainResult).toEqual([1, 3]); }); it("calculates a domain from string categories", () => { const props = { categories: { x: ["cats", "kittens"] } }; const domainResult = Domain.getDomainFromCategories(props, "x"); expect(domainResult).toEqual([1, 2]); }); }); describe("getDomainFromData", () => { it("returns a domain from a dataset", () => { const dataset = [ { _x: 1, _y: 3 }, { _x: 3, _y: 5 }, ]; const resultDomain = Domain.getDomainFromData({}, "x", dataset); expect(resultDomain).toEqual([1, 3]); }); }); describe("getDomainFromProps", () => { it("gets the domain from a domain array", () => { const props = { domain: [1, 2] }; const resultDomain = Domain.getDomainFromProps(props, "x"); expect(resultDomain).toEqual(props.domain); }); it("gets the domain from a domain object", () => { const props = { domain: { x: [1, 2] } }; const resultDomain = Domain.getDomainFromProps(props, "x"); expect(resultDomain).toEqual(props.domain.x); }); it("returns undefined if the domain props is not given", () => { expect(Domain.getDomainFromProps({}, "x")).toBeUndefined(); }); it("returns undefined if the domain for a given axis is not defined", () => { const props = { domain: { y: [1, 2] } }; const resultDomain = Domain.getDomainFromProps(props, "x"); expect(resultDomain).toBeUndefined(); }); it("returns a domain from minDomain and maxDomain if both are defined", () => { const props = { minDomain: 1, maxDomain: 10 }; const resultDomain = Domain.getDomainFromProps(props, "x"); expect(resultDomain).toEqual([1, 10]); }); it("returns an adjusted domain if minDomain equals maxDomain", () => { const props = { minDomain: 1, maxDomain: 1 }; const verySmallNumber = Math.pow(10, -10); const resultDomain = Domain.getDomainFromProps(props, "x"); expect(resultDomain).toEqual([1 - verySmallNumber, 1 + verySmallNumber]); }); it("returns undefined if only minDomain is defined", () => { const props = { minDomain: 1 }; const resultDomain = Domain.getDomainFromProps(props, "x"); expect(resultDomain).toBeUndefined(); }); }); describe("getDomainFromMinMax", () => { it("returns a min max array when min and max are given", () => { const min = 1; const max = 2; const resultDomain = Domain.getDomainFromMinMax(min, max); expect(resultDomain).toEqual([min, max]); }); it("returns an adjusted domain if min equals max", () => { const min = 1; const max = 1; const verySmallNumber = Math.pow(10, -10); const resultDomain = Domain.getDomainFromMinMax(min, max); expect(resultDomain).toEqual([1 - verySmallNumber, 1 + verySmallNumber]); }); it("returns a positive domain if min and max are both zero", () => { const min = 0; const max = 0; const verySmallNumber = Math.pow(10, -10); const resultDomain = Domain.getDomainFromMinMax(min, max); expect(resultDomain).toEqual([0, 2 * verySmallNumber]); }); it("returns an adjusted date domain if min equals max", () => { const min = new Date(1980, 1, 1); const max = new Date(1980, 1, 1); const resultDomain = Domain.getDomainFromMinMax(min, max); expect(resultDomain).toEqual([ new Date(Number(min) - 1), new Date(Number(max) + 1), ]); }); }); describe("getDomainWithZero", () => { it("ensures that the domain includes zero for the dependent axis", () => { const props = { data: [ { x: 1, y: 3 }, { x: 3, y: 5 }, ], }; const resultDomain = Domain.getDomainWithZero(props, "y"); expect(resultDomain).toEqual([0, 5]); }); it("allows minimum domain values less than zero", () => { const props = { data: [ { x: 1, y: -3 }, { x: 3, y: 5 }, ], }; const resultDomain = Domain.getDomainWithZero(props, "y"); expect(resultDomain).toEqual([-3, 5]); }); it("allows explicit y0 values in props.data to set the minimum domain", () => { const props = { data: [ { x: 1, y: 3, y0: 2 }, { x: 3, y: 5, y0: 3 }, ], }; const resultDomain = Domain.getDomainWithZero(props, "y"); expect(resultDomain).toEqual([2, 5]); }); it("handles negative y0 values", () => { const props = { data: [ { x: 1, y: -3, y0: -7 }, { x: 3, y: -5, y0: -7 }, ], }; const resultDomain = Domain.getDomainWithZero(props, "y"); expect(resultDomain).toEqual([-7, -3]); }); it("respects props.minDomain when present", () => { const props = { data: [ { x: 1, y: 3, y0: 2 }, { x: 3, y: 5, y0: 2 }, ], minDomain: { y: 4 }, }; const resultDomain = Domain.getDomainWithZero(props, "y"); expect(resultDomain).toEqual([4, 5]); }); it("does not force the independent domain to include zero", () => { const props = { data: [ { x: 1, y: 3 }, { x: 3, y: 5 }, ], }; const resultDomain = Domain.getDomainWithZero(props, "x"); expect(resultDomain).toEqual([1, 3]); }); }); describe("getMaxFromProps", () => { it("returns maxDomain from props as an object", () => { const props = { maxDomain: { x: 3 } }; const maxDomain = Domain.getMaxFromProps(props, "x"); expect(maxDomain).toEqual(props.maxDomain.x); }); it("returns maxDomain from props as a number", () => { const props = { maxDomain: 3 }; const maxDomain = Domain.getMaxFromProps(props, "x"); expect(maxDomain).toEqual(props.maxDomain); }); it("returns undefined when maxDomain is not defined for a given axis", () => { const props = { maxDomain: { y: 3 } }; const maxDomain = Domain.getMaxFromProps(props, "x"); expect(maxDomain).toBeUndefined(); }); }); describe("getMinFromProps", () => { it("returns minDomain from props as an object", () => { const props = { minDomain: { x: 3 } }; const minDomain = Domain.getMinFromProps(props, "x"); expect(minDomain).toEqual(props.minDomain.x); }); it("returns minDomain from props as a number", () => { const props = { minDomain: 3 }; const minDomain = Domain.getMinFromProps(props, "x"); expect(minDomain).toEqual(props.minDomain); }); it("returns undefined when minDomain is not defined for a given axis", () => { const props = { minDomain: { y: 3 } }; const minDomain = Domain.getMinFromProps(props, "x"); expect(minDomain).toBeUndefined(); }); }); describe("getSymmetricDomain", () => { it("pads the domain by a value determined by data spacing", () => { const domain = [0, 10]; const data = [2, 4, 6, 8]; const resultDomain = Domain.getSymmetricDomain(domain, data); expect(resultDomain).toEqual([0, 12]); }); }); describe("isDomainComponent", () => { class TestDomainComponent extends React.Component { static role = "area"; } it("returns true when a component has a static role matching a whitelist", () => { expect(Domain.isDomainComponent()).toBe(true); }); it("returns false when a component has a role that does not match the whitelist", () => { // eslint-disable-next-line react/no-multi-comp class TestFooComponent extends React.Component { static role = "foo"; } expect(Domain.isDomainComponent()).toBe(false); }); it("returns true when a domain component is wrapped in VictoryPortal", () => { expect( Domain.isDomainComponent( , ), ).toBe(true); }); }); }); ================================================ FILE: packages/victory-core/src/victory-util/domain.ts ================================================ import React from "react"; import isDate from "lodash/isDate"; import isPlainObject from "lodash/isPlainObject"; import sortedUniq from "lodash/sortedUniq"; import * as Data from "./data"; import * as Scale from "./scale"; import * as Helpers from "./helpers"; import * as Collection from "./collection"; // Private Methods function cleanDomain(domain, props, axis) { const scaleType = Scale.getScaleType(props, axis); if (scaleType !== "log") { return domain; } const rules = (dom) => { const almostZero = dom[0] < 0 || dom[1] < 0 ? -1 / Number.MAX_SAFE_INTEGER : 1 / Number.MAX_SAFE_INTEGER; const domainOne = dom[0] === 0 ? almostZero : dom[0]; const domainTwo = dom[1] === 0 ? almostZero : dom[1]; return [domainOne, domainTwo]; }; return rules(domain); } function getDomainPadding(props, axis) { const formatPadding = (padding) => { return Array.isArray(padding) ? { left: padding[0], right: padding[1] } : { left: padding, right: padding }; }; return isPlainObject(props.domainPadding) ? formatPadding(props.domainPadding[axis]) : formatPadding(props.domainPadding); } function getFlatData(dataset, axis: "x" | "y") { const axisKey = `_${axis}`; return dataset.flat().map((datum: any) => { return datum[axisKey] && datum[axisKey][1] !== undefined ? datum[axisKey][1] : datum[axisKey]; }); } function getExtremeFromData(dataset, axis, type = "min") { const getExtreme = (arr) => type === "max" ? Math.max(...arr) : Math.min(...arr); const initialValue = type === "max" ? -Infinity : Infinity; let containsDate = false; const result = dataset.flat().reduce((memo: number, datum: any) => { const current0 = datum[`_${axis}0`] !== undefined ? datum[`_${axis}0`] : datum[`_${axis}`]; const current1 = datum[`_${axis}1`] !== undefined ? datum[`_${axis}1`] : datum[`_${axis}`]; const current = getExtreme([current0, current1]); containsDate = containsDate || current0 instanceof Date || current1 instanceof Date; return getExtreme([memo, current]); }, initialValue); return containsDate ? new Date(result) : result; } function padDomain(domain, props, axis) { if (!props.domainPadding) { return domain; } const minDomain = getMinFromProps(props, axis); const maxDomain = getMaxFromProps(props, axis); const padding = getDomainPadding(props, axis); if (!padding.left && !padding.right) { return domain; } const min = Collection.getMinValue(domain); const max = Collection.getMaxValue(domain); const currentAxis = Helpers.getCurrentAxis(axis, props.horizontal); const range = Helpers.getRange(props, currentAxis); const rangeExtent = Math.abs(range[0] - range[1]); const paddedRangeExtent = Math.max( rangeExtent - padding.left - padding.right, 1, ); const paddedDomainExtent = (Math.abs(max.valueOf() - min.valueOf()) / paddedRangeExtent) * rangeExtent; const simplePadding = { left: (paddedDomainExtent * padding.left) / rangeExtent, right: (paddedDomainExtent * padding.right) / rangeExtent, }; let paddedDomain = { min: min.valueOf() - simplePadding.left, max: max.valueOf() + simplePadding.right, }; const singleQuadrantDomainPadding = isPlainObject( props.singleQuadrantDomainPadding, ) ? props.singleQuadrantDomainPadding[axis] : props.singleQuadrantDomainPadding; const addsQuadrants = (min.valueOf() >= 0 && paddedDomain.min <= 0) || (max.valueOf() <= 0 && paddedDomain.max >= 0); const adjust = (val, type) => { const coerce = (type === "min" && min.valueOf() >= 0 && val <= 0) || (type === "max" && max.valueOf() <= 0 && val >= 0); return coerce ? 0 : val; }; if (addsQuadrants && singleQuadrantDomainPadding !== false) { // Naive initial padding calculation const initialPadding = { // @ts-expect-error `max/min` might be dates left: (Math.abs(max - min) * padding.left) / rangeExtent, // @ts-expect-error `max/min` might be dates right: (Math.abs(max - min) * padding.right) / rangeExtent, }; // Adjust the domain by the initial padding const adjustedDomain = { min: adjust(min.valueOf() - initialPadding.left, "min"), max: adjust(max.valueOf() + initialPadding.right, "max"), }; // re-calculate padding, taking the adjusted domain into account const finalPadding = { left: (Math.abs(adjustedDomain.max - adjustedDomain.min) * padding.left) / rangeExtent, right: (Math.abs(adjustedDomain.max - adjustedDomain.min) * padding.right) / rangeExtent, }; // Adjust the domain by the final padding paddedDomain = { min: adjust(min.valueOf() - finalPadding.left, "min"), max: adjust(max.valueOf() + finalPadding.right, "max"), }; } // default to minDomain / maxDomain if they exist const finalDomain = { min: minDomain !== undefined ? minDomain : paddedDomain.min, max: maxDomain !== undefined ? maxDomain : paddedDomain.max, }; return min instanceof Date || max instanceof Date ? getDomainFromMinMax(new Date(finalDomain.min), new Date(finalDomain.max)) : getDomainFromMinMax(finalDomain.min, finalDomain.max); } // Public Methods /** * Returns a getDomain function * @param {Function} getDomainFromDataFunction: a function that takes props and axis and * returns a domain based on data * @param {Function} formatDomainFunction: a function that takes domain, props, and axis and * returns a formatted domain * @returns {Function} a function that takes props and axis and returns a formatted domain */ export function createDomainFunction( getDomainFromDataFunction?, formatDomainFunction?, ) { const getDomainFromDataFn = Helpers.isFunction(getDomainFromDataFunction) ? getDomainFromDataFunction : getDomainFromData; const formatDomainFn = Helpers.isFunction(formatDomainFunction) ? formatDomainFunction : formatDomain; return (props, axis) => { const propsDomain = getDomainFromProps(props, axis); if (propsDomain) { return formatDomainFn(propsDomain, props, axis); } const categories = Data.getCategories(props, axis); const domain = categories ? getDomainFromCategories(props, axis, categories) : getDomainFromDataFn(props, axis); return domain ? formatDomainFn(domain, props, axis) : undefined; }; } /** * Returns a formatted domain. * @param {Array} domain: a domain in the form of a two element array * @param {Object} props: the props object * @param {String} axis: the current axis * @returns {Array} a domain in the form of a two element array */ export function formatDomain(domain, props, axis) { return cleanDomain(padDomain(domain, props, axis), props, axis); } /** * Returns a domain for a given axis based on props, category, or data * @param {Object} props: the props object * @param {String} axis: the current axis * @returns {Array} the domain for the given axis */ export function getDomain(props, axis) { return createDomainFunction()(props, axis); } /** * Returns a domain based on categories if they exist * @param {Object} props: the props object * @param {String} axis: the current axis * @param {Array} categories: an array of categories corresponding to a given axis * @returns {Array|undefined} returns a domain from categories or undefined */ export function getDomainFromCategories(props, axis, categories?) { const categoriesArray = categories || Data.getCategories(props, axis); const { polar, startAngle = 0, endAngle = 360 } = props; if (!categoriesArray) { return undefined; } const minDomain = getMinFromProps(props, axis); const maxDomain = getMaxFromProps(props, axis); const stringArray = Collection.containsStrings(categoriesArray) ? Data.getStringsFromCategories(props, axis) : []; const stringMap = stringArray.length === 0 ? null : stringArray.reduce((memo, string, index) => { memo[string] = index + 1; return memo; }, {}); const categoryValues = stringMap ? categoriesArray.map((value) => stringMap[value]) : categoriesArray; const min = minDomain !== undefined ? minDomain : Collection.getMinValue(categoryValues); const max = maxDomain !== undefined ? maxDomain : Collection.getMaxValue(categoryValues); const categoryDomain = getDomainFromMinMax(min, max); return polar && axis === "x" && Math.abs(startAngle - endAngle) === 360 ? getSymmetricDomain(categoryDomain, categoryValues) : categoryDomain; } /** * Returns a domain from a dataset for a given axis * @param {Object} props: the props object * @param {String} axis: the current axis * @param {Array} dataset: an array of data * @returns {Array} the domain based on data */ export function getDomainFromData(props, axis, dataset) { const datasetArray = dataset || Data.getData(props); const { polar, startAngle = 0, endAngle = 360 } = props; const minDomain = getMinFromProps(props, axis); const maxDomain = getMaxFromProps(props, axis); if (datasetArray.length < 1) { return minDomain !== undefined && maxDomain !== undefined ? getDomainFromMinMax(minDomain, maxDomain) : undefined; } const min = minDomain !== undefined ? minDomain : getExtremeFromData(datasetArray, axis, "min"); const max = maxDomain !== undefined ? maxDomain : getExtremeFromData(datasetArray, axis, "max"); const domain = getDomainFromMinMax(min, max); return polar && axis === "x" && Math.abs(startAngle - endAngle) === 360 ? getSymmetricDomain(domain, getFlatData(datasetArray, axis)) : domain; } /** * Returns a domain in the form of a two element array given a min and max value. * @param {Number|Date} min: the props object * @param {Number|Date} max: the current axis * @returns {Array} the minDomain based on props */ export function getDomainFromMinMax(min, max) { const getSinglePointDomain = (val) => { // d3-scale does not properly resolve very small differences. const verySmallNumber = // eslint-disable-next-line no-magic-numbers val === 0 ? 2 * Math.pow(10, -10) : Math.pow(10, -10); const verySmallDate = 1; const minVal = val instanceof Date ? new Date(Number(val) - verySmallDate) : Number(val) - verySmallNumber; const maxVal = val instanceof Date ? new Date(Number(val) + verySmallDate) : Number(val) + verySmallNumber; return val === 0 ? [0, maxVal] : [minVal, maxVal]; }; return Number(min) === Number(max) ? getSinglePointDomain(max) : [min, max]; } /** * Returns a the domain for a given axis if domain is given in props * @param {Object} props: the props object * @param {String} axis: the current axis * @returns {Array|undefined} the domain based on props */ export function getDomainFromProps(props, axis) { const minDomain = getMinFromProps(props, axis); const maxDomain = getMaxFromProps(props, axis); if (isPlainObject(props.domain) && props.domain[axis]) { return props.domain[axis]; } else if (Array.isArray(props.domain)) { return props.domain; } else if (minDomain !== undefined && maxDomain !== undefined) { return getDomainFromMinMax(minDomain, maxDomain); } return undefined; } /** * Returns a domain for a given axis. This method forces the domain to include * zero unless the domain is explicitly specified in props. * @param {Object} props: the props object * @param {String} axis: the current axis * @returns {Array} the domain for the given axis */ export function getDomainWithZero(props, axis) { const propsDomain = getDomainFromProps(props, axis); if (propsDomain) { return propsDomain; } const dataset = Data.getData(props); const y0Min = dataset.reduce( (min, datum) => (datum._y0 < min ? datum._y0 : min), Infinity, ); const ensureZero = (domain) => { if (axis === "x") { return domain; } const defaultMin = y0Min !== Infinity ? y0Min : 0; const maxDomainProp = getMaxFromProps(props, axis); const minDomainProp = getMinFromProps(props, axis); const max = maxDomainProp !== undefined ? maxDomainProp : Collection.getMaxValue(domain, defaultMin); const min = minDomainProp !== undefined ? minDomainProp : Collection.getMinValue(domain, defaultMin); return getDomainFromMinMax(min, max); }; const getDomainFunction = () => { return getDomainFromData(props, axis, dataset); }; const formatDomainFunction = (domain) => { return formatDomain(ensureZero(domain), props, axis); }; return createDomainFunction(getDomainFunction, formatDomainFunction)( props, axis, ); } /** * Returns the maxDomain from props if it exists * @param {Object} props: the props object * @param {String} axis: the current axis * @returns {Number|Date|undefined} the maxDomain based on props */ export function getMaxFromProps(props, axis) { if (isPlainObject(props.maxDomain) && props.maxDomain[axis] !== undefined) { return props.maxDomain[axis]; } return typeof props.maxDomain === "number" || isDate(props.maxDomain) ? props.maxDomain : undefined; } /** * Returns the minDomain from props if it exists * @param {Object} props: the props object * @param {String} axis: the current axis * @returns {Number|Date|undefined} the minDomain based on props */ export function getMinFromProps(props, axis) { if (isPlainObject(props.minDomain) && props.minDomain[axis] !== undefined) { return props.minDomain[axis]; } return typeof props.minDomain === "number" || isDate(props.minDomain) ? props.minDomain : undefined; } /** * Returns a symmetrically padded domain for polar charts * @param {Array} domain: the original domain * @param {Array} values: a flat array of values corresponding to either tickValues, or data values * for a given dimension i.e. only x values. * @returns {Array} the symmetric domain */ export function getSymmetricDomain(domain, values: number[]) { const processedData = sortedUniq(values.sort((a, b) => a - b)); const step = processedData[1] - processedData[0]; return [domain[0], domain[1] + step]; } /** * Checks whether a given component can be used to calculate domain * @param {Component} component: a React component instance * @returns {Boolean} Returns true if the given component has a role included in the whitelist */ export function isDomainComponent(component) { const getRole = (child) => { return child && child.type ? child.type.role : ""; }; let role = getRole(component); if (role === "portal") { const children = React.Children.toArray(component.props.children); role = children.length ? getRole(children[0]) : ""; } const whitelist = [ "area", "axis", "bar", "boxplot", "candlestick", "errorbar", "group", "histogram", "line", "pie", "scatter", "stack", "voronoi", ]; return whitelist.includes(role); } ================================================ FILE: packages/victory-core/src/victory-util/events.test.ts ================================================ import * as Events from "./events"; describe("victory-util/events", () => { describe("getPartialEvents", () => { it("returns a set of new event functions with partially applied arguments", () => { const events = { onClick: (evt, childProps, index) => { return { evt, childProps, index }; }, }; const evt = {} as React.SyntheticEvent; const index = "TEST_INDEX"; const childProps = { style: { fill: "green" } }; const result = Events.getPartialEvents(events, index, childProps); expect(Object.keys(result)).toEqual(["onClick"]); expect(Object.keys(result.onClick(evt))).toEqual([ "evt", "childProps", "index", ]); expect(result.onClick(evt).index).toEqual(index); expect(result.onClick(evt).childProps).toEqual(childProps); }); }); describe("getEvents", () => { let fake; beforeEach(() => { fake = { props: { events: [ { target: "data", eventHandlers: { onClick: () => { return { mutation: () => { return { foo: "foo" }; }, }; }, }, }, ], }, baseProps: { 0: { data: { foo: "bar" }, }, }, setState: (x) => x, state: {}, }; jest.spyOn(fake, "setState"); }); afterEach(() => { jest.clearAllMocks(); }); it("returns new functions that call set state", () => { const getScopedEvents = Events.getScopedEvents.bind(fake); const getBoundEvents = Events.getEvents.bind(fake); const index = 0; const result = getBoundEvents(fake.props, "data", index, getScopedEvents); expect(Object.keys(result)).toEqual(expect.arrayContaining(["onClick"])); const partialEvents = Events.getPartialEvents(result, index, {}); expect(Object.keys(partialEvents)).toEqual( expect.arrayContaining(["onClick"]), ); partialEvents.onClick({} as React.SyntheticEvent); expect(fake.setState).toReturnWith({ [index]: { data: { foo: "foo" }, }, }); }); }); }); ================================================ FILE: packages/victory-core/src/victory-util/events.ts ================================================ import React from "react"; import isEmpty from "lodash/isEmpty"; import pickBy from "lodash/pickBy"; import omitBy from "lodash/omitBy"; import uniq from "lodash/uniq"; import type { EventMixinCalculatedValues } from "./add-events"; import { isFunction } from "./helpers"; const GLOBAL_EVENT_REGEX = /^onGlobal(.*)$/; type ComponentEventKey = string | number; export interface ComponentEvent { target?: "parent" | string; eventKey?: ComponentEventKey | ComponentEventKey[]; eventHandlers: ComponentEventHandlers; } // Normally we'd use Template Literal Types, but we're avoiding it to maximize TS compatibility with TS < 4.1 export type ComponentEventName = string; // `on${Capitalize}`; export interface ComponentEventHandlers { [k: ComponentEventName]: ComponentEventHandler; } export type ComponentEventHandler = ( evt: React.SyntheticEvent, childProps: unknown, eventKey: ComponentEventKey, eventName: ComponentEventName, ) => UpdatedProps; export type UpdatedProps = any; interface ComponentWithEvents extends EventMixinCalculatedValues { state; setState; } /* Returns all own and shared events that should be attached to a single target element, * i.e. an individual bar specified by target: "data", eventKey: [index]. * Returned events are scoped to the appropriate state. Either that of the component itself * (i.e. VictoryBar) in the case of own events, or that of the parent component * (i.e. VictoryChart) in the case of shared events */ // eslint-disable-next-line max-params export function getEvents( this: ComponentWithEvents, props, target?, eventKey?, // eslint-disable-next-line no-shadow getScopedEvents?, ) { // Returns all events that apply to a particular target element const getEventsByTarget = (events: Array) => { const getSelectedEvents = () => { const targetEvents = events.reduce((memo, event) => { if (event.target !== undefined) { const matchesTarget = Array.isArray(event.target) ? event.target.includes(target) : `${event.target}` === `${target}`; return matchesTarget ? memo.concat(event) : memo; } return memo.concat(event); }, [] as ComponentEvent[]); if (eventKey !== undefined && target !== "parent") { return targetEvents.filter((obj) => { const targetKeys = obj.eventKey; const useKey = (key) => (key ? `${key}` === `${eventKey}` : true); return Array.isArray(targetKeys) ? targetKeys.some((k) => useKey(k)) : useKey(targetKeys); }); } return targetEvents; }; const selectedEvents = getSelectedEvents(); return ( Array.isArray(selectedEvents) && selectedEvents.reduce( (memo, event) => { return event ? Object.assign(memo, event.eventHandlers) : memo; }, {} as ComponentEvent["eventHandlers"], ) ); }; /* Returns all events from props and defaultEvents from components. Events handlers * specified in props will override handlers for the same event if they are also * specified in defaultEvents of a sub-component */ const getAllEvents = () => { // Mandatory usage: `getEvents.bind(this)` if (Array.isArray(this.componentEvents)) { return Array.isArray(props.events) ? this.componentEvents.concat(...props.events) : this.componentEvents; } return props.events; }; const allEvents = getAllEvents(); const ownEvents = allEvents && isFunction(getScopedEvents) ? getScopedEvents(getEventsByTarget(allEvents), target) : undefined; if (!props.sharedEvents) { return ownEvents; } const getSharedEvents = props.sharedEvents.getEvents; const sharedEvents = props.sharedEvents.events && getSharedEvents(getEventsByTarget(props.sharedEvents.events), target); return Object.assign({}, sharedEvents, ownEvents); } /* Returns a modified events object where each event handler is replaced by a new * function that calls the original handler and then calls setState with the return * of the original event handler assigned to state property that maps to the target * element. */ // eslint-disable-next-line max-params export function getScopedEvents( this: ComponentWithEvents, events, namespace, childType, baseProps, ) { if (isEmpty(events)) { return {}; } // Mandatory usage: `getScopedEvents.bind(this)` const newBaseProps = baseProps || this.baseProps; // returns the original base props or base state of a given target element const getTargetProps = (identifier, type) => { const { childName, target, key } = identifier; const baseType = type === "props" ? newBaseProps : this.state || {}; const base = childName === undefined || childName === null || !baseType[childName] ? baseType : baseType[childName]; return key === "parent" ? base.parent : base[key] && base[key][target]; }; // Returns the state object with the mutation caused by a given eventReturn // applied to the appropriate property on the state object const parseEvent = (eventReturn, eventKey) => { const childNames = namespace === "parent" ? eventReturn.childName : eventReturn.childName || childType; const target = eventReturn.target || namespace; // returns all eventKeys to modify for a targeted childName const getKeys = (childName) => { if (target === "parent") { return "parent"; } if (eventReturn.eventKey === "all") { return newBaseProps[childName] ? Object.keys(newBaseProps[childName]).filter( (value) => value !== "parent", ) : Object.keys(newBaseProps).filter((value) => value !== "parent"); } else if (eventReturn.eventKey === undefined && eventKey === "parent") { return newBaseProps[childName] ? Object.keys(newBaseProps[childName]) : Object.keys(newBaseProps); } return eventReturn.eventKey !== undefined ? eventReturn.eventKey : eventKey; }; // returns the state object with mutated props applied for a single key const getMutationObject = (key, childName) => { const baseState = this.state || {}; if (!isFunction(eventReturn.mutation)) { return baseState; } const mutationTargetProps = getTargetProps( { childName, key, target }, "props", ); const mutationTargetState = getTargetProps( { childName, key, target }, "state", ); const mutatedProps = eventReturn.mutation( Object.assign({}, mutationTargetProps, mutationTargetState), newBaseProps, ); const childState = baseState[childName] || {}; const filterState = (state) => { if (state[key] && state[key][target]) { delete state[key][target]; } if (state[key] && !Object.keys(state[key]).length) { delete state[key]; } return state; }; const extendState = (state) => { return target === "parent" ? Object.assign(state, { [key]: Object.assign(state[key] || {}, mutatedProps), }) : Object.assign(state, { [key]: Object.assign(state[key] || {}, { [target]: mutatedProps, }), }); }; const updateState = (state) => { return mutatedProps ? extendState(state) : filterState(state); }; return childName !== undefined && childName !== null ? Object.assign(baseState, { [childName]: updateState(childState) }) : updateState(baseState); }; // returns entire mutated state for a given childName const getReturnByChild = (childName) => { const mutationKeys = getKeys(childName); return Array.isArray(mutationKeys) ? mutationKeys.reduce((memo, key) => { return Object.assign(memo, getMutationObject(key, childName)); }, {}) : getMutationObject(mutationKeys, childName); }; // returns an entire mutated state for all children const allChildNames = childNames === "all" ? Object.keys(newBaseProps).filter((value) => value !== "parent") : childNames; return Array.isArray(allChildNames) ? allChildNames.reduce((memo, childName) => { return Object.assign(memo, getReturnByChild(childName)); }, {}) : getReturnByChild(allChildNames); }; // Parses an array of event returns into a single state mutation const parseEventReturn = (eventReturn, eventKey) => { return Array.isArray(eventReturn) ? eventReturn.reduce( (memo, props) => Object.assign({}, memo, parseEvent(props, eventKey)), {}, ) : parseEvent(eventReturn, eventKey); }; const compileCallbacks = (eventReturn) => { const getCallback = (obj) => isFunction(obj.callback) && obj.callback; const callbacks = Array.isArray(eventReturn) ? eventReturn.map((evtObj) => getCallback(evtObj)) : [getCallback(eventReturn)]; const callbackArray = callbacks.filter((callback) => callback !== false); return callbackArray.length ? () => callbackArray.forEach((callback) => callback()) : undefined; }; // A function that calls a particular event handler, parses its return // into a state mutation, and calls setState // eslint-disable-next-line max-params const onEvent = (evt, childProps, eventKey, eventName) => { const eventReturn = events[eventName](evt, childProps, eventKey, this); if (!isEmpty(eventReturn)) { const callbacks = compileCallbacks(eventReturn); this.setState(parseEventReturn(eventReturn, eventKey), callbacks); } }; // returns a new events object with enhanced event handlers return Object.keys(events).reduce((memo, event) => { memo[event] = onEvent; return memo; }, {}); } /* * Returns a partially applied event handler for a specific target element * This allows event handlers to have access to props controlling each element */ export function getPartialEvents( events: ComponentEventHandlers, eventKey: ComponentEventKey, childProps: unknown, ): PartialEvents { if (!events) return {}; return Object.keys(events).reduce((memo, eventName) => { const appliedEvent = (evt) => events[eventName](evt, childProps, eventKey, eventName); memo[eventName] = appliedEvent; return memo; }, {} as PartialEvents); } export interface PartialEvents { [eventName: ComponentEventName]: (evt: React.SyntheticEvent) => UpdatedProps; } /* Returns the property of the state object corresponding to event changes for * a particular element */ // eslint-disable-next-line max-params export function getEventState( this: ComponentWithEvents, eventKey: ComponentEventKey, namespace: string, childType?: string, ) { // Mandatory usage: `getEventState.bind(this)` const state = this.state || {}; if (!childType) { return eventKey === "parent" ? (state[eventKey] && state[eventKey][namespace]) || state[eventKey] : state[eventKey] && state[eventKey][namespace]; } return ( state[childType] && state[childType][eventKey] && state[childType][eventKey][namespace] ); } /** * Returns a set of all mutations for shared events * * @param {Array} mutations an array of mutations objects * @param {Object} baseProps an object that describes all props for children of VictorySharedEvents * @param {Object} baseState an object that describes state for children of VictorySharedEvents * @param {Array} childNames an array of childNames * * @return {Object} a object describing all mutations for VictorySharedEvents */ // eslint-disable-next-line max-params export function getExternalMutationsWithChildren( mutations, baseProps = {}, baseState = {}, childNames, ) { return childNames.reduce((memo, childName) => { const childState = baseState[childName]; const mutation = getExternalMutations( mutations, baseProps[childName], baseState[childName], childName, ); memo[childName] = mutation ? mutation : childState; return pickBy(memo, (v) => !isEmpty(v)); }, {}); } /** * Returns a set of all mutations for a component * * @param {Array} mutations an array of mutations objects * @param {Object} baseProps a props object (scoped to a childName when used by shared events) * @param {Object} baseState a state object (scoped to a childName when used by shared events) * @param {String} childName an optional childName * * @return {Object} a object describing mutations for a given component */ // eslint-disable-next-line max-params export function getExternalMutations( mutations, baseProps = {}, baseState = {}, childName?, ) { const eventKeys = Object.keys(baseProps); return eventKeys.reduce((memo, eventKey) => { const keyState = baseState[eventKey] || {}; const keyProps = baseProps[eventKey] || {}; if (eventKey === "parent") { const identifier = { eventKey, target: "parent" }; const mutation = getExternalMutation( mutations, keyProps, keyState, identifier, ); memo[eventKey] = mutation !== undefined ? Object.assign({}, keyState, mutation) : keyState; } else { // use keys from both state and props so that elements not intially included in baseProps // will be used. (i.e. labels) const targets = uniq(Object.keys(keyProps).concat(Object.keys(keyState))); memo[eventKey] = targets.reduce((m, target) => { const identifier = { eventKey, target, childName }; const mutation = getExternalMutation( mutations, keyProps[target], keyState[target], identifier, ); m[target] = mutation !== undefined ? Object.assign({}, keyState[target], mutation) : keyState[target]; return pickBy(m, (v) => !isEmpty(v)); }, {}); } return pickBy(memo, (v) => !isEmpty(v)); }, {}); } /** * Returns a set of mutations for a particular element given scoped baseProps and baseState * * @param {Array} mutations an array of mutations objects * @param {Object} baseProps a props object (scoped the element specified by the identifier) * @param {Object} baseState a state object (scoped the element specified by the identifier) * @param {Object} identifier { eventKey, target, childName } * * @return {Object | undefined} a object describing mutations for a given element, or undefined */ // eslint-disable-next-line max-params export function getExternalMutation( mutations, baseProps, baseState, identifier, ) { const filterMutations = (mutation, type) => { if (typeof mutation[type] === "string") { return mutation[type] === "all" || mutation[type] === identifier[type]; } else if (Array.isArray(mutation[type])) { // coerce arrays to strings before matching const stringArray = mutation[type].map((m) => `${m}`); return stringArray.includes(identifier[type]); } return false; }; let scopedMutations = Array.isArray(mutations) ? mutations : [mutations]; if (identifier.childName) { scopedMutations = mutations.filter((m) => filterMutations(m, "childName")); } // find any mutation objects that match the target const targetMutations = scopedMutations.filter((m) => filterMutations(m, "target"), ); if (isEmpty(targetMutations)) { return undefined; } const keyMutations = targetMutations.filter((m) => filterMutations(m, "eventKey"), ); if (isEmpty(keyMutations)) { return undefined; } return keyMutations.reduce((memo, curr) => { const mutationFunction = curr && isFunction(curr.mutation) ? curr.mutation : () => undefined; const currentMutation = mutationFunction( Object.assign({}, baseProps, baseState), ); return Object.assign({}, memo, currentMutation); }, {}); } /* Returns an array of defaultEvents from sub-components of a given component. * i.e. any static `defaultEvents` on `labelComponent` will be returned */ export function getComponentEvents(props, components) { const events = Array.isArray(components) && components.reduce((memo, componentName) => { const component = props[componentName]; const defaultEvents = component && component.type && component.type.defaultEvents; const componentEvents = isFunction(defaultEvents) ? defaultEvents(component.props) : defaultEvents; return Array.isArray(componentEvents) ? memo.concat(...componentEvents) : memo; }, [] as ComponentEvent[]); return events && events.length ? events : undefined; } export function getGlobalEventNameFromKey(key) { const match = key.match(GLOBAL_EVENT_REGEX); return match && match[1] && match[1].toLowerCase(); } export const getGlobalEvents = (events) => pickBy(events, (_, key) => GLOBAL_EVENT_REGEX.test(key)); export const omitGlobalEvents = (events) => omitBy(events, (_, key) => GLOBAL_EVENT_REGEX.test(key)); export const emulateReactEvent = (event) => Object.assign(event, { nativeEvent: event }); ================================================ FILE: packages/victory-core/src/victory-util/helpers.test.ts ================================================ import * as Helpers from "./helpers"; describe("victory-util/helpers", () => { describe("invert", () => { it("inverts a given object", () => { const data = { x: 3, y: "2", z: 1 }; const invertedData = Helpers.invert(data); expect(invertedData).toEqual({ 3: "x", 2: "y", 1: "z" }); }); it("handles duplicate values by taking the last key", () => { const data = { x: 3, y: 2, z: 1, a: 2 }; const invertedData = Helpers.invert(data); expect(invertedData).toEqual({ 3: "x", 2: "a", 1: "z" }); }); }); describe("omit", () => { const data = { x: 3, y: 2, z: 1 }; it("removes omitted keys and preserves all others", () => { const newData = Helpers.omit(data, ["x"]); // @ts-expect-error This property is deleted, as expected expect(newData.x).toBeUndefined(); expect(newData.y).toEqual(2); expect(newData.z).toEqual(1); }); it("creates a copy of the original object", () => { const newData = Helpers.omit(data, []); newData.x = 10; expect(data.x).toEqual(3); expect(newData.x).toEqual(10); }); it("defaults to an empty object", () => { // @ts-expect-error This should complain const newData = Helpers.omit(); expect(newData).toEqual({}); }); it("defaults to simple shallow copy", () => { const newData = Helpers.omit(data); expect(newData).toEqual(data); }); }); describe("modifyProps", () => { it("defaults to an empty object", () => { expect(Helpers.modifyProps({})).toEqual({}); }); it("removes the theme role's style", () => { const role = "legend"; const props = { theme: { legend: { style: { color: "blue", }, data: 42, }, }, }; const fallbackProps = {}; const modifiedProps = { ...props, data: 42, }; expect(Helpers.modifyProps(props, fallbackProps, role)).toEqual( modifiedProps, ); }); it("uses fallbackProps", () => { const props = { x: 2, y: 3 }; const fallbackProps = { x: 12, y: 13, z: 14 }; const modifiedProps = { x: 2, y: 3, z: 14 }; expect(Helpers.modifyProps(props, fallbackProps)).toEqual(modifiedProps); }); }); describe("evaluateProp", () => { const data = { x: 3, y: 2 }; it("evaluates functional props", () => { const testProp = (datum) => (datum.y > 0 ? "red" : "blue"); expect(Helpers.evaluateProp(testProp, data)).toEqual("red"); }); it("doesn't alter non-functional props", () => { const testProp = "blue"; expect(Helpers.evaluateProp(testProp, data)).toEqual("blue"); }); }); describe("evaluateStyle", () => { const data = { x: 3, y: 2 }; it("evaluates functional styles, without altering others", () => { const style = { color: (datum) => (datum.y > 0 ? "red" : "blue"), size: 5, }; expect(Helpers.evaluateStyle(style, data)).toEqual({ color: "red", size: 5, }); }); it("returns no styles if disableInlineStyles is true", () => { const style = { color: "blue", }; const props = { disableInlineStyles: true, }; expect(Helpers.evaluateStyle(style, props)).toEqual({}); }); }); describe("getRange", () => { const props = { width: 100, height: 200, padding: 0, }; it("returns a range based on props and axis", () => { const x = Helpers.getRange(props, "x"); expect(Array.isArray(x)).toBe(true); expect(x).toHaveLength(2); expect(x).toEqual([0, 100]); const y = Helpers.getRange(props, "y"); expect(Array.isArray(y)).toBe(true); expect(y).toHaveLength(2); expect(y).toEqual([200, 0]); }); }); describe("getStyles", () => { const defaultStyles = { parent: { border: "black" }, data: { fill: "blue", stroke: "black" }, labels: { fontSize: 10, fontFamily: "Helvetica" }, }; it("merges styles", () => { const style = { data: { fill: "red" }, labels: { fontSize: 12 } }; const styles = Helpers.getStyles(style, defaultStyles); expect(styles.parent).toEqual({ border: "black", width: "100%", height: "100%", }); expect(styles.data).toEqual({ fill: "red", stroke: "black" }); expect(styles.labels).toEqual({ fontSize: 12, fontFamily: "Helvetica", }); }); }); describe("getPadding", () => { it("sets padding from a single number", () => { const props = { padding: 40 }; expect(Helpers.getPadding(props.padding)).toEqual({ top: 40, bottom: 40, left: 40, right: 40, }); }); it("sets padding from a complete object", () => { const props = { padding: { top: 20, bottom: 40, left: 60, right: 80 }, }; expect(Helpers.getPadding(props.padding)).toEqual(props.padding); }); it("fills missing values with 0", () => { const props = { padding: { top: 40, bottom: 40 }, }; expect(Helpers.getPadding(props.padding)).toEqual({ top: 40, bottom: 40, left: 0, right: 0, }); }); }); describe("createAccessor", () => { it("creates a valid object accessor from a property key", () => { const accessor = Helpers.createAccessor("k"); expect(accessor({ k: 42 })).toEqual(42); }); it("creates a valid array accessor from an index", () => { const accessor = Helpers.createAccessor(2); expect(accessor([3, 4, 5])).toEqual(5); }); it("creates a valid array accessor from a deeply nested path", () => { const accessor = Helpers.createAccessor("x.y[0].0.z"); expect(accessor({ x: { y: [[{ z: 1987 }]] } })).toEqual(1987); }); it("creates a value (passthrough) accessor from null/undefined", () => { const nullAccessor = Helpers.createAccessor(null); const undefinedAccessor = Helpers.createAccessor(undefined); expect(nullAccessor("ok")).toEqual("ok"); expect(undefinedAccessor(14)).toEqual(14); }); }); describe("range", () => { it("returns an array of integers", () => { expect(Helpers.range(4)).toEqual([0, 1, 2, 3]); }); it("returns an array of integers for negative n", () => { expect(Helpers.range(-4)).toEqual([0, -1, -2, -3]); }); it("returns an array of integers from start to end", () => { expect(Helpers.range(1, 5)).toEqual([1, 2, 3, 4]); }); it("returns an array of integers from negative start to end", () => { expect(Helpers.range(-5, 5)).toEqual([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]); }); it("returns an array of integers from start to negative end", () => { expect(Helpers.range(5, -5)).toEqual([5, 4, 3, 2, 1, 0, -1, -2, -3, -4]); }); it("returns an array of integers using an increment", () => { expect(Helpers.range(0, 20, 5)).toEqual([0, 5, 10, 15]); }); it("returns an array of integers using an increment and negative start", () => { expect(Helpers.range(-10, 20, 5)).toEqual([-10, -5, 0, 5, 10, 15]); }); it("returns an array of numbers from a floating point increment", () => { expect(Helpers.range(0, 1, 0.2).length).toEqual(5); }); it("should parse non-integer values", () => { expect(Helpers.range(4.7)).toEqual([0, 1, 2, 3, 4]); }); it("should not throw on undefined start value", () => { expect(Helpers.range(undefined as any)).toEqual([]); }); it("should not throw on NaN start value", () => { expect(Helpers.range(NaN as any)).toEqual([]); }); }); }); ================================================ FILE: packages/victory-core/src/victory-util/helpers.ts ================================================ import React, { isValidElement } from "react"; import defaults from "lodash/defaults"; import property from "lodash/property"; import pick from "lodash/pick"; import { ValueOrAccessor } from "../types/prop-types"; export type ElementPadding = { top: number; bottom: number; left: number; right: number; }; export type MaybePointData = { x?: number; x0?: number; x1?: number; y?: number; y0?: number; y1?: number; _x?: number; _x0?: number; _x1?: number; _y?: number; _y0?: number; _y1?: number; _voronoiX?: number; _voronoiY?: number; }; /** * Determine the range of a cartesian axis */ function getCartesianRange(options: { axis: "x" | "y"; height: number; width: number; padding: ElementPadding; }): [number, number] { const vertical = options.axis !== "x"; if (vertical) { return [options.height - options.padding.bottom, options.padding.top]; } return [options.padding.left, options.width - options.padding.right]; } /** * Determine the range of a polar axis in radians */ function getPolarRange(options: { axis: "x" | "y"; innerRadius?: number; startAngle?: number; endAngle?: number; padding: ElementPadding; height: number; width: number; }): [number, number] { if (options.axis === "x") { const startAngle = degreesToRadians(options.startAngle || 0); const endAngle = degreesToRadians(options.endAngle || 360); return [startAngle, endAngle]; } return [ options.innerRadius || 0, getRadius({ height: options.height, width: options.width, padding: options.padding, }), ]; } /** * Creates an object composed of the inverted keys and values of object. * If object contains duplicate values, subsequent values overwrite property assignments of previous values. */ export function invert(original: Record) { return Object.entries(original).reduce((acc, current) => { acc[current[1]] = current[0]; return acc; }, {}); } /** * creates an object with some keys excluded * replacement for lodash.omit for performance. does not mimic the entire lodash.omit api * @param {Object} originalObject: created object will be based on this object * @param {Array} ks: an array of keys to omit from the new object * @returns {Object} new object with same properties as originalObject */ export function omit( originalObject: T, ks: Array = [], ): Omit { // code based on babel's _objectWithoutProperties const newObject = {} as T; for (const key in originalObject) { // @ts-expect-error String is not assignable to Key if (ks.indexOf(key) >= 0) { continue; } if (!Object.prototype.hasOwnProperty.call(originalObject, key)) { continue; } newObject[key] = originalObject[key]; } return newObject; } /** * Coalesce the x and y values from a data point */ export function getPoint(datum: MaybePointData): MaybePointData { const { _x, _x1, _x0, _voronoiX, _y, _y1, _y0, _voronoiY } = datum; const defaultX = _x1 ?? _x; const defaultY = _y1 ?? _y; const point = { x: _voronoiX ?? defaultX, x0: _x0 ?? _x, y: _voronoiY ?? defaultY, y0: _y0 ?? _y, }; return defaults({}, point, datum); } /** * Scale a point based on the origin, direction, and given scale function */ export function scalePoint( props: { scale: { x: (x?: number) => number; y: (y?: number) => number }; polar?: boolean; horizontal?: boolean; origin?: { x: number; y: number }; }, datum: MaybePointData, ) { const { scale, polar, horizontal } = props; const d = getPoint(datum); const origin = props.origin || { x: 0, y: 0 }; const x = horizontal ? scale.y(d.y) : scale.x(d.x); const x0 = horizontal ? scale.y(d.y0) : scale.x(d.x0); const y = horizontal ? scale.x(d.x) : scale.y(d.y); const y0 = horizontal ? scale.x(d.x0) : scale.y(d.y0); return { x: polar ? y * Math.cos(x) + origin.x : x, x0: polar ? y0 * Math.cos(x0) + origin.x : x0, y: polar ? -y * Math.sin(x) + origin.y : y, y0: polar ? -y0 * Math.sin(x0) + origin.x : y0, }; } /** * Returns a padding value from a number or partial padding values */ export function getPadding( padding?: number | Partial, ): ElementPadding { const paddingVal = typeof padding === "number" ? padding : 0; const paddingObj = typeof padding === "object" ? padding : {}; return { top: paddingObj.top || paddingVal, bottom: paddingObj.bottom || paddingVal, left: paddingObj.left || paddingVal, right: paddingObj.right || paddingVal, }; } /** * Returns true if the component is defined as a tooltip */ export function isTooltip(component?: { type?: { role?: string } }) { const labelRole = component && component.type && component.type.role; return labelRole === "tooltip"; } export function getDefaultStyles(props, role) { const { theme = {}, labelComponent } = props; const defaultStyles = (theme[role] && theme[role].style) || {}; if (!isTooltip(labelComponent)) { return defaultStyles; } const tooltipStyle = (theme.tooltip && theme.tooltip.style) || {}; const labelStyle = defaults({}, tooltipStyle, defaultStyles.labels); return defaults({}, { labels: labelStyle }, defaultStyles); } export function getStyles(style, defaultStyles) { const width = "100%"; const height = "100%"; if (!style) { return defaults({ parent: { height, width } }, defaultStyles); } const { data, labels, parent } = style; const defaultParent = (defaultStyles && defaultStyles.parent) || {}; const defaultLabels = (defaultStyles && defaultStyles.labels) || {}; const defaultData = (defaultStyles && defaultStyles.data) || {}; return { parent: defaults({}, parent, defaultParent, { width, height }), labels: defaults({}, labels, defaultLabels), data: defaults({}, data, defaultData), }; } /** * Returns the value of a prop or accessor function with the given props */ export function evaluateProp( prop: ValueOrAccessor>, props: Record, ): TValue { return isFunction(prop) ? prop(props) : prop; } export function evaluateStyle(style, props) { if (props.disableInlineStyles) { return {}; } if (!style || !Object.keys(style).some((value) => isFunction(style[value]))) { return style; } return Object.keys(style).reduce((prev, curr) => { prev[curr] = evaluateProp(style[curr], props); return prev; }, {}); } export function degreesToRadians(degrees) { return typeof degrees === "number" ? degrees * (Math.PI / 180) : degrees; } export function radiansToDegrees(radians) { return typeof radians === "number" ? radians / (Math.PI / 180) : radians; } /** * Get the maximum radius that will fit in the container */ export function getRadius(options: { height: number; width: number; padding: ElementPadding; }) { const { width, height, padding } = options; const { left, right, top, bottom } = padding; return Math.min(width - left - right, height - top - bottom) / 2; } /** * Returns the origin for a polar chart within the padded area */ export function getPolarOrigin(props: { height: number; width: number; padding: ElementPadding; }): { x: number; y: number } { const { width, height } = props; const { top, bottom, left, right } = getPadding(props.padding); const radius = Math.min(width - left - right, height - top - bottom) / 2; const offsetWidth = width / 2 + left - right; const offsetHeight = height / 2 + top - bottom; return { x: offsetWidth + radius > width ? radius + left - right : offsetWidth, y: offsetHeight + radius > height ? radius + top - bottom : offsetHeight, }; } /** * Determine the range of an axis based on the given props */ export function getRange( props: { range?: [number, number]; polar?: boolean; innerRadius?: number; startAngle?: number; endAngle?: number; height: number; width: number; padding: number | Partial; }, axis: "x" | "y", ) { if (props.range && props.range[axis]) { return props.range[axis]; } else if (props.range && Array.isArray(props.range)) { return props.range; } return props.polar ? getPolarRange({ axis, innerRadius: props.innerRadius, startAngle: props.startAngle, endAngle: props.endAngle, height: props.height, width: props.width, padding: getPadding(props.padding), }) : getCartesianRange({ axis, height: props.height, width: props.width, padding: getPadding(props.padding), }); } /** * Checks if `value` is `null` or `undefined`. * @returns {boolean} Returns `true` if `value` is nullish, else `false`. */ export function isNil(value: any): boolean { // eslint-disable-next-line eqeqeq return value == null; } /** * Checks if `value` is classified as a `Function` object. * * @since 0.1.0 * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a function, else `false`. */ export function isFunction(value: any): value is (...args: any[]) => any { return typeof value === "function"; } export function createAccessor(key) { // creates a data accessor function // given a property key, path, array index, or null for identity. if (isFunction(key)) { return key; } else if (key === null || key === undefined) { // null/undefined means "return the data item itself" return (x) => x; } // otherwise, assume it is an array index, property key or path (_.property handles all three) return property(key); } export function modifyProps(props, fallbackProps?, role?) { const theme = props.theme && props.theme[role] ? props.theme[role] : {}; const themeProps = omit(theme, ["style"]); const horizontal = isHorizontal(props); const defaultObject = horizontal === undefined ? {} : { horizontal }; return defaults(defaultObject, props, themeProps, fallbackProps); } /** * Returns the given axis or the opposite axis when horizontal * @param {string} axis: the given axis, either "x" pr "y" * @param {Boolean} horizontal: true when the chart is flipped to the horizontal orientation * @returns {String} the dimension appropriate for the axis given its props "x" or "y" */ export function getCurrentAxis(axis, horizontal) { const otherAxis = axis === "x" ? "y" : "x"; return horizontal ? otherAxis : axis; } /** * Creates an object with the same keys as object and values generated by running * each own enumerable string keyed property of object through the function fn */ export function mapValues( values: T, fn: (value?: any) => any, ): T | undefined { if (values) { return Object.keys(values).reduce((acc, key) => { acc[key] = fn(values[key]); return acc; }, {} as T); } } /** * Creates an array of numbers (positive and/or negative) progressing * from start up to, but not including, end. * A step of -1 is used if a negative start is specified without an end or step. * If end is not specified, it's set to start with start then set to 0. * * @param start The length of the array to create, or the start value * @param end [The end value] If this is defined, start is the start value * @returns An array of the given length */ export function range(start: number, end?: number, increment?: number) { // when the end index is not given, start from 0 const startIndex = end ? start : 0; // when the end index is not given, the end of the range is the start index let endIndex = end ? end : start; // ensure endIndex is not a falsy value if (!endIndex) endIndex = 0; const k = endIndex - startIndex; // the value range const length = Math.abs(k); // the length of the range const sign = k / length || 1; // the sign of the range (negative or positive) const inc = increment || 1; // the step size of each increment // normalize the array length when dealing with floating point values const arrayLength = Math.max(Math.ceil(length / inc), 0); return Array.from(Array(arrayLength), (_, i) => startIndex + i * sign * inc); } /** * @param {Array} children: an array of child components * @param {Function} iteratee: a function with arguments "child", "childName", and "parent" * @param {Object} parentProps: props from the parent that are applied to children * @param {any} initialMemo: The object in which the iteration results are combined. * @param {Function} combine: Combines the result of the iteratee with the current memo * to the memo for the next iteration step * @returns {Array} returns an array of results from calling the iteratee on all nested children */ /* eslint-disable max-params */ export function reduceChildren< TChildren extends React.ReactNode, TItem, TResult = TItem[], >( children: TChildren[], iteratee: ( child: TChildren, childName: string, parent?: TChildren, ) => TItem | null, parentProps = {}, // @ts-expect-error These defaults are hard to type initialMemo: TResult = [], combine: (memo: TResult, item: TItem) => TResult = (memo, item) => // @ts-expect-error These defaults are hard to type memo.concat(item), ): TResult { const sharedProps = [ "data", "domain", "categories", "polar", "startAngle", "endAngle", "minDomain", "maxDomain", "horizontal", ]; const traverseChildren = (childArray, names, parent?) => { return childArray.reduce((memo, child, index) => { let newMemo = memo; const childRole = child.type && child.type.role; const childName = child.props.name || `${childRole}-${names[index]}`; if (child.props && child.props.children) { const childProps = Object.assign( {}, child.props, pick(parentProps, sharedProps), ); const nestedChildren = child.type && child.type.role === "stack" && isFunction(child.type.getChildren) ? child.type.getChildren(childProps) : ( React.Children.toArray( child.props.children, ) as Array ).map((c) => { const nestedChildProps = Object.assign( {}, c.props, pick(childProps, sharedProps), ); return React.cloneElement(c, nestedChildProps); }); const childNames = nestedChildren.map((c, i) => `${childName}-${i}`); const nestedResults = traverseChildren( nestedChildren, childNames, child, ); newMemo = combine(newMemo, nestedResults); } else { const result = iteratee(child, childName, parent); if (result) { newMemo = combine(newMemo, result); } } return newMemo; }, initialMemo); }; const validChildren = children.filter(isValidElement); const childNames = validChildren.map((c, i) => i); return traverseChildren(validChildren, childNames); } /** * @param {Object} props: the props object * @returns {Boolean} returns true if the props object contains `horizontal: true` of if any * children or nested children are horizontal */ export function isHorizontal(props: { horizontal?: boolean; children?: React.ReactNode; }) { if (props.horizontal !== undefined || !props.children) { return props.horizontal; } const traverseChildren = (childArray) => { return childArray.reduce((memo, child) => { const childProps = child.props || {}; if (memo || childProps.horizontal || !childProps.children) { return memo || childProps.horizontal; } return traverseChildren(React.Children.toArray(childProps.children)); }, false); }; return traverseChildren(React.Children.toArray(props.children)); } ================================================ FILE: packages/victory-core/src/victory-util/hooks/index.ts ================================================ export { usePreviousProps } from "./use-previous-props"; export { useAnimationState } from "./use-animation-state"; ================================================ FILE: packages/victory-core/src/victory-util/hooks/use-animation-state.ts ================================================ import React from "react"; import defaults from "lodash/defaults"; import * as Collection from "../collection"; import * as Transitions from "../transitions"; const INITIAL_STATE: AnimationState = { nodesShouldLoad: false, nodesDoneLoad: false, animating: true, }; type AnyObject = Record; export type AnimationState = { nodesShouldLoad?: boolean; nodesDoneLoad?: boolean; animating?: boolean; childrenTransitions?: any[]; nodesWillExit?: boolean; nodesWillEnter?: boolean; nodesShouldEnter?: boolean; oldProps?: AnyObject; nextProps?: AnyObject; continuous?: boolean; }; export const useAnimationState = (initialState = INITIAL_STATE) => { const [state, _setState] = React.useState(initialState); // This allows us to use a state object and maintain the same API as this.setState const setState = React.useCallback( (newState: AnimationState) => { _setState((oldState) => ({ ...oldState, ...newState })); }, [_setState], ); // This is a copy of Wrapper.getAnimationProps const getAnimationProps = React.useCallback( (props: AnyObject | undefined, child, index) => { if (!props?.animate) { return child.props.animate; } const getFilteredState = () => { let childrenTransitions = state && state.childrenTransitions; childrenTransitions = Collection.isArrayOfArrays(childrenTransitions) ? childrenTransitions[index] : childrenTransitions; return defaults({ childrenTransitions }, state); }; let getTransitions = props.animate && props.animate.getTransitions; const filteredState = getFilteredState(); const parentState = (props.animate && props.animate.parentState) || filteredState; if (!getTransitions) { const getTransitionProps = Transitions.getTransitionPropsFactory( props, filteredState, (newState) => setState(newState), ); getTransitions = (childComponent) => getTransitionProps(childComponent, index); } return defaults( { getTransitions, parentState }, props.animate, child.props.animate, ); }, [state, setState], ); // This is a copy of Wrapper.setAnimationState const setAnimationState = React.useCallback( (props: AnyObject | undefined, nextProps) => { if (!props?.animate) { return; } if (props.animate.parentState) { const nodesWillExit = props.animate.parentState.nodesWillExit; const oldProps = nodesWillExit ? props : null; const newState = defaults( { oldProps, nextProps }, props.animate.parentState, ); setState(newState); } else { const oldChildren = React.Children.toArray(props.children); const nextChildren = React.Children.toArray(nextProps.children); const isContinuous = (child) => { const check = (c) => c.type && c.type.continuous; return Array.isArray(child) ? child.some(check) : check(child); }; const continuous = !props.polar && oldChildren.some((child: any) => { return ( isContinuous(child) || (child?.props?.children && isContinuous(child.props.children)) ); }); const { nodesWillExit, nodesWillEnter, childrenTransitions, nodesShouldEnter, } = Transitions.getInitialTransitionState(oldChildren, nextChildren); setState({ nodesWillExit, nodesWillEnter, nodesShouldEnter, childrenTransitions: Collection.isArrayOfArrays(childrenTransitions) ? childrenTransitions[0] : childrenTransitions, oldProps: nodesWillExit ? props : undefined, nextProps, continuous, }); } }, [setState], ); const getProps = React.useCallback( (initialProps: AnyObject) => { return state && state.nodesWillExit ? state.oldProps || initialProps : initialProps; }, [state], ); return { state, setState, getAnimationProps, setAnimationState, getProps }; }; ================================================ FILE: packages/victory-core/src/victory-util/hooks/use-previous-props.ts ================================================ import React from "react"; export function usePreviousProps(props: T): T | undefined { const ref = React.useRef(); React.useEffect(() => { ref.current = props; }); return ref.current; } ================================================ FILE: packages/victory-core/src/victory-util/immutable-types.d.ts ================================================ /** * Copyright (c) 2014-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /** * Immutable data encourages pure functions (data-in, data-out) and lends itself * to much simpler application development and enabling techniques from * functional programming such as lazy evaluation. * * While designed to bring these powerful functional concepts to JavaScript, it * presents an Object-Oriented API familiar to Javascript engineers and closely * mirroring that of Array, Map, and Set. It is easy and efficient to convert to * and from plain Javascript types. * Note: all examples are presented in [ES6][]. To run in all browsers, they * need to be translated to ES3. For example: * * // ES6 * foo.map(x => x * x); * // ES3 * foo.map(function (x) { return x * x; }); * * [ES6]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla */ /** * Deeply converts plain JS objects and arrays to Immutable Maps and Lists. * * If a `reviver` is optionally provided, it will be called with every * collection as a Seq (beginning with the most nested collections * and proceeding to the top-level collection itself), along with the key * refering to each collection and the parent JS object provided as `this`. * For the top level, object, the key will be `""`. This `reviver` is expected * to return a new Immutable Iterable, allowing for custom conversions from * deep JS objects. * * This example converts JSON to List and OrderedMap: * * Immutable.fromJS({a: {b: [10, 20, 30]}, c: 40}, function (key, value) { * var isIndexed = Immutable.Iterable.isIndexed(value); * return isIndexed ? value.toList() : value.toOrderedMap(); * }); * * // true, "b", {b: [10, 20, 30]} * // false, "a", {a: {b: [10, 20, 30]}, c: 40} * // false, "", {"": {a: {b: [10, 20, 30]}, c: 40}} * * If `reviver` is not provided, the default behavior will convert Arrays into * Lists and Objects into Maps. * * `reviver` acts similarly to the [same parameter in `JSON.parse`][1]. * * `Immutable.fromJS` is conservative in its conversion. It will only convert * arrays which pass `Array.isArray` to Lists, and only raw objects (no custom * prototype) to Map. * * Keep in mind, when using JS objects to construct Immutable Maps, that * JavaScript Object properties are always strings, even if written in a * quote-less shorthand, while Immutable Maps accept keys of any type. * * ```js * var obj = { 1: "one" }; * Object.keys(obj); // [ "1" ] * obj["1"]; // "one" * obj[1]; // "one" * * var map = Map(obj); * map.get("1"); // "one" * map.get(1); // undefined * ``` * * Property access for JavaScript Objects first converts the key to a string, * but since Immutable Map keys can be of any type the argument to `get()` is * not altered. * * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter * "Using the reviver parameter" */ export function fromJS( json: any, reviver?: (k: any, v: Iterable) => any, ): any; /** * Value equality check with semantics similar to `Object.is`, but treats * Immutable `Iterable`s as values, equal if the second `Iterable` includes * equivalent values. * * It's used throughout Immutable when checking for equality, including `Map` * key equality and `Set` membership. * * var map1 = Immutable.Map({a:1, b:1, c:1}); * var map2 = Immutable.Map({a:1, b:1, c:1}); * assert(map1 !== map2); * assert(Object.is(map1, map2) === false); * assert(Immutable.is(map1, map2) === true); * * Note: Unlike `Object.is`, `Immutable.is` assumes `0` and `-0` are the same * value, matching the behavior of ES6 Map key equality. */ export function is(first: any, second: any): boolean; /** * Lists are ordered indexed dense collections, much like a JavaScript * Array. * * Lists are immutable and fully persistent with O(log32 N) gets and sets, * and O(1) push and pop. * * Lists implement Deque, with efficient addition and removal from both the * end (`push`, `pop`) and beginning (`unshift`, `shift`). * * Unlike a JavaScript Array, there is no distinction between an * "unset" index and an index set to `undefined`. `List#forEach` visits all * indices from 0 to size, regardless of whether they were explicitly defined. */ export module List { /** * True if the provided value is a List */ function isList(maybeList: any): boolean; /** * Creates a new List containing `values`. */ function of(...values: T[]): List; } /** * Create a new immutable List containing the values of the provided * iterable-like. */ export function List(): List; export function List(iter: Iterable.Indexed): List; export function List(iter: Iterable.Set): List; export function List(iter: Iterable.Keyed): List; export function List(array: Array): List; export function List(iterator: Iterator): List; export function List(iterable: /*Iterable*/ Object): List; export interface List extends Collection.Indexed { // Persistent changes /** * Returns a new List which includes `value` at `index`. If `index` already * exists in this List, it will be replaced. * * `index` may be a negative number, which indexes back from the end of the * List. `v.set(-1, "value")` sets the last item in the List. * * If `index` larger than `size`, the returned List's `size` will be large * enough to include the `index`. */ set(index: number, value: T): List; /** * Returns a new List which excludes this `index` and with a size 1 less * than this List. Values at indices above `index` are shifted down by 1 to * fill the position. * * This is synonymous with `list.splice(index, 1)`. * * `index` may be a negative number, which indexes back from the end of the * List. `v.delete(-1)` deletes the last item in the List. * * Note: `delete` cannot be safely used in IE8 * @alias remove */ delete(index: number): List; remove(index: number): List; /** * Returns a new List with `value` at `index` with a size 1 more than this * List. Values at indices above `index` are shifted over by 1. * * This is synonymous with `list.splice(index, 0, value) */ insert(index: number, value: T): List; /** * Returns a new List with 0 size and no values. */ clear(): List; /** * Returns a new List with the provided `values` appended, starting at this * List's `size`. */ push(...values: T[]): List; /** * Returns a new List with a size ones less than this List, excluding * the last index in this List. * * Note: this differs from `Array#pop` because it returns a new * List rather than the removed value. Use `last()` to get the last value * in this List. */ pop(): List; /** * Returns a new List with the provided `values` prepended, shifting other * values ahead to higher indices. */ unshift(...values: T[]): List; /** * Returns a new List with a size ones less than this List, excluding * the first index in this List, shifting all other values to a lower index. * * Note: this differs from `Array#shift` because it returns a new * List rather than the removed value. Use `first()` to get the first * value in this List. */ shift(): List; /** * Returns a new List with an updated value at `index` with the return * value of calling `updater` with the existing value, or `notSetValue` if * `index` was not set. If called with a single argument, `updater` is * called with the List itself. * * `index` may be a negative number, which indexes back from the end of the * List. `v.update(-1)` updates the last item in the List. * * @see `Map#update` */ update(updater: (value: List) => List): List; update(index: number, updater: (value: T) => T): List; update(index: number, notSetValue: T, updater: (value: T) => T): List; /** * @see `Map#merge` */ merge(...iterables: Iterable.Indexed[]): List; merge(...iterables: Array[]): List; /** * @see `Map#mergeWith` */ mergeWith( merger: (previous?: T, next?: T, key?: number) => T, ...iterables: Iterable.Indexed[] ): List; mergeWith( merger: (previous?: T, next?: T, key?: number) => T, ...iterables: Array[] ): List; /** * @see `Map#mergeDeep` */ mergeDeep(...iterables: Iterable.Indexed[]): List; mergeDeep(...iterables: Array[]): List; /** * @see `Map#mergeDeepWith` */ mergeDeepWith( merger: (previous?: T, next?: T, key?: number) => T, ...iterables: Iterable.Indexed[] ): List; mergeDeepWith( merger: (previous?: T, next?: T, key?: number) => T, ...iterables: Array[] ): List; /** * Returns a new List with size `size`. If `size` is less than this * List's size, the new List will exclude values at the higher indices. * If `size` is greater than this List's size, the new List will have * undefined values for the newly available indices. * * When building a new List and the final size is known up front, `setSize` * used in conjunction with `withMutations` may result in the more * performant construction. */ setSize(size: number): List; // Deep persistent changes /** * Returns a new List having set `value` at this `keyPath`. If any keys in * `keyPath` do not exist, a new immutable Map will be created at that key. * * Index numbers are used as keys to determine the path to follow in * the List. */ setIn(keyPath: Array, value: any): List; setIn(keyPath: Iterable, value: any): List; /** * Returns a new List having removed the value at this `keyPath`. If any * keys in `keyPath` do not exist, no change will occur. * * @alias removeIn */ deleteIn(keyPath: Array): List; deleteIn(keyPath: Iterable): List; removeIn(keyPath: Array): List; removeIn(keyPath: Iterable): List; /** * @see `Map#updateIn` */ updateIn(keyPath: Array, updater: (value: any) => any): List; updateIn( keyPath: Array, notSetValue: any, updater: (value: any) => any, ): List; updateIn(keyPath: Iterable, updater: (value: any) => any): List; updateIn( keyPath: Iterable, notSetValue: any, updater: (value: any) => any, ): List; /** * @see `Map#mergeIn` */ mergeIn( keyPath: Iterable, ...iterables: Iterable.Indexed[] ): List; mergeIn(keyPath: Array, ...iterables: Iterable.Indexed[]): List; mergeIn(keyPath: Array, ...iterables: Array[]): List; /** * @see `Map#mergeDeepIn` */ mergeDeepIn( keyPath: Iterable, ...iterables: Iterable.Indexed[] ): List; mergeDeepIn( keyPath: Array, ...iterables: Iterable.Indexed[] ): List; mergeDeepIn(keyPath: Array, ...iterables: Array[]): List; // Transient changes /** * Note: Not all methods can be used on a mutable collection or within * `withMutations`! Only `set`, `push`, `pop`, `shift`, `unshift` and * `merge` may be used mutatively. * * @see `Map#withMutations` */ withMutations(mutator: (mutable: List) => any): List; /** * @see `Map#asMutable` */ asMutable(): List; /** * @see `Map#asImmutable` */ asImmutable(): List; } /** * Immutable Map is an unordered Iterable.Keyed of (key, value) pairs with * `O(log32 N)` gets and `O(log32 N)` persistent sets. * * Iteration order of a Map is undefined, however is stable. Multiple * iterations of the same Map will iterate in the same order. * * Map's keys can be of any type, and use `Immutable.is` to determine key * equality. This allows the use of any value (including NaN) as a key. * * Because `Immutable.is` returns equality based on value semantics, and * Immutable collections are treated as values, any Immutable collection may * be used as a key. * * Map().set(List.of(1), 'listofone').get(List.of(1)); * // 'listofone' * * Any JavaScript object may be used as a key, however strict identity is used * to evaluate key equality. Two similar looking objects will represent two * different keys. * * Implemented by a hash-array mapped trie. */ export module Map { /** * True if the provided value is a Map */ function isMap(maybeMap: any): boolean; /** * Creates a new Map from alternating keys and values */ function of(...keyValues: any[]): Map; } /** * Creates a new Immutable Map. * * Created with the same key value pairs as the provided Iterable.Keyed or * JavaScript Object or expects an Iterable of [K, V] tuple entries. * * var newMap = Map({key: "value"}); * var newMap = Map([["key", "value"]]); * * Keep in mind, when using JS objects to construct Immutable Maps, that * JavaScript Object properties are always strings, even if written in a * quote-less shorthand, while Immutable Maps accept keys of any type. * * ```js * var obj = { 1: "one" }; * Object.keys(obj); // [ "1" ] * obj["1"]; // "one" * obj[1]; // "one" * * var map = Map(obj); * map.get("1"); // "one" * map.get(1); // undefined * ``` * * Property access for JavaScript Objects first converts the key to a string, * but since Immutable Map keys can be of any type the argument to `get()` is * not altered. */ export function Map(): Map; export function Map(iter: Iterable.Keyed): Map; export function Map(iter: Iterable>): Map; export function Map(array: Array>): Map; export function Map(obj: { [key: string]: V }): Map; export function Map(iterator: Iterator>): Map; export function Map(iterable: /*Iterable<[K,V]>*/ Object): Map; export interface Map extends Collection.Keyed { // Persistent changes /** * Returns a new Map also containing the new key, value pair. If an equivalent * key already exists in this Map, it will be replaced. */ set(key: K, value: V): Map; /** * Returns a new Map which excludes this `key`. * * Note: `delete` cannot be safely used in IE8, but is provided to mirror * the ES6 collection API. * @alias remove */ delete(key: K): Map; remove(key: K): Map; /** * Returns a new Map containing no keys or values. */ clear(): Map; /** * Returns a new Map having updated the value at this `key` with the return * value of calling `updater` with the existing value, or `notSetValue` if * the key was not set. If called with only a single argument, `updater` is * called with the Map itself. * * Equivalent to: `map.set(key, updater(map.get(key, notSetValue)))`. */ update(updater: (value: Map) => Map): Map; update(key: K, updater: (value: V) => V): Map; update(key: K, notSetValue: V, updater: (value: V) => V): Map; /** * Returns a new Map resulting from merging the provided Iterables * (or JS objects) into this Map. In other words, this takes each entry of * each iterable and sets it on this Map. * * If any of the values provided to `merge` are not Iterable (would return * false for `Immutable.Iterable.isIterable`) then they are deeply converted * via `Immutable.fromJS` before being merged. However, if the value is an * Iterable but includes non-iterable JS objects or arrays, those nested * values will be preserved. * * var x = Immutable.Map({a: 10, b: 20, c: 30}); * var y = Immutable.Map({b: 40, a: 50, d: 60}); * x.merge(y) // { a: 50, b: 40, c: 30, d: 60 } * y.merge(x) // { b: 20, a: 10, d: 60, c: 30 } * */ merge(...iterables: Iterable[]): Map; merge(...iterables: { [key: string]: V }[]): Map; /** * Like `merge()`, `mergeWith()` returns a new Map resulting from merging * the provided Iterables (or JS objects) into this Map, but uses the * `merger` function for dealing with conflicts. * * var x = Immutable.Map({a: 10, b: 20, c: 30}); * var y = Immutable.Map({b: 40, a: 50, d: 60}); * x.mergeWith((prev, next) => prev / next, y) // { a: 0.2, b: 0.5, c: 30, d: 60 } * y.mergeWith((prev, next) => prev / next, x) // { b: 2, a: 5, d: 60, c: 30 } * */ mergeWith( merger: (previous?: V, next?: V, key?: K) => V, ...iterables: Iterable[] ): Map; mergeWith( merger: (previous?: V, next?: V, key?: K) => V, ...iterables: { [key: string]: V }[] ): Map; /** * Like `merge()`, but when two Iterables conflict, it merges them as well, * recursing deeply through the nested data. * * var x = Immutable.fromJS({a: { x: 10, y: 10 }, b: { x: 20, y: 50 } }); * var y = Immutable.fromJS({a: { x: 2 }, b: { y: 5 }, c: { z: 3 } }); * x.mergeDeep(y) // {a: { x: 2, y: 10 }, b: { x: 20, y: 5 }, c: { z: 3 } } * */ mergeDeep(...iterables: Iterable[]): Map; mergeDeep(...iterables: { [key: string]: V }[]): Map; /** * Like `mergeDeep()`, but when two non-Iterables conflict, it uses the * `merger` function to determine the resulting value. * * var x = Immutable.fromJS({a: { x: 10, y: 10 }, b: { x: 20, y: 50 } }); * var y = Immutable.fromJS({a: { x: 2 }, b: { y: 5 }, c: { z: 3 } }); * x.mergeDeepWith((prev, next) => prev / next, y) * // {a: { x: 5, y: 10 }, b: { x: 20, y: 10 }, c: { z: 3 } } * */ mergeDeepWith( merger: (previous?: V, next?: V, key?: K) => V, ...iterables: Iterable[] ): Map; mergeDeepWith( merger: (previous?: V, next?: V, key?: K) => V, ...iterables: { [key: string]: V }[] ): Map; // Deep persistent changes /** * Returns a new Map having set `value` at this `keyPath`. If any keys in * `keyPath` do not exist, a new immutable Map will be created at that key. */ setIn(keyPath: Array, value: any): Map; setIn(KeyPath: Iterable, value: any): Map; /** * Returns a new Map having removed the value at this `keyPath`. If any keys * in `keyPath` do not exist, no change will occur. * * @alias removeIn */ deleteIn(keyPath: Array): Map; deleteIn(keyPath: Iterable): Map; removeIn(keyPath: Array): Map; removeIn(keyPath: Iterable): Map; /** * Returns a new Map having applied the `updater` to the entry found at the * keyPath. * * If any keys in `keyPath` do not exist, new Immutable `Map`s will * be created at those keys. If the `keyPath` does not already contain a * value, the `updater` function will be called with `notSetValue`, if * provided, otherwise `undefined`. * * var data = Immutable.fromJS({ a: { b: { c: 10 } } }); * data = data.updateIn(['a', 'b', 'c'], val => val * 2); * // { a: { b: { c: 20 } } } * * If the `updater` function returns the same value it was called with, then * no change will occur. This is still true if `notSetValue` is provided. * * var data1 = Immutable.fromJS({ a: { b: { c: 10 } } }); * data2 = data1.updateIn(['x', 'y', 'z'], 100, val => val); * assert(data2 === data1); * */ updateIn(keyPath: Array, updater: (value: any) => any): Map; updateIn( keyPath: Array, notSetValue: any, updater: (value: any) => any, ): Map; updateIn( keyPath: Iterable, updater: (value: any) => any, ): Map; updateIn( keyPath: Iterable, notSetValue: any, updater: (value: any) => any, ): Map; /** * A combination of `updateIn` and `merge`, returning a new Map, but * performing the merge at a point arrived at by following the keyPath. * In other words, these two lines are equivalent: * * x.updateIn(['a', 'b', 'c'], abc => abc.merge(y)); * x.mergeIn(['a', 'b', 'c'], y); * */ mergeIn( keyPath: Iterable, ...iterables: Iterable[] ): Map; mergeIn(keyPath: Array, ...iterables: Iterable[]): Map; mergeIn( keyPath: Array, ...iterables: { [key: string]: V }[] ): Map; /** * A combination of `updateIn` and `mergeDeep`, returning a new Map, but * performing the deep merge at a point arrived at by following the keyPath. * In other words, these two lines are equivalent: * * x.updateIn(['a', 'b', 'c'], abc => abc.mergeDeep(y)); * x.mergeDeepIn(['a', 'b', 'c'], y); * */ mergeDeepIn( keyPath: Iterable, ...iterables: Iterable[] ): Map; mergeDeepIn(keyPath: Array, ...iterables: Iterable[]): Map; mergeDeepIn( keyPath: Array, ...iterables: { [key: string]: V }[] ): Map; // Transient changes /** * Every time you call one of the above functions, a new immutable Map is * created. If a pure function calls a number of these to produce a final * return value, then a penalty on performance and memory has been paid by * creating all of the intermediate immutable Maps. * * If you need to apply a series of mutations to produce a new immutable * Map, `withMutations()` creates a temporary mutable copy of the Map which * can apply mutations in a highly performant manner. In fact, this is * exactly how complex mutations like `merge` are done. * * As an example, this results in the creation of 2, not 4, new Maps: * * var map1 = Immutable.Map(); * var map2 = map1.withMutations(map => { * map.set('a', 1).set('b', 2).set('c', 3); * }); * assert(map1.size === 0); * assert(map2.size === 3); * * Note: Not all methods can be used on a mutable collection or within * `withMutations`! Only `set` and `merge` may be used mutatively. * */ withMutations(mutator: (mutable: Map) => any): Map; /** * Another way to avoid creation of intermediate Immutable maps is to create * a mutable copy of this collection. Mutable copies *always* return `this`, * and thus shouldn't be used for equality. Your function should never return * a mutable copy of a collection, only use it internally to create a new * collection. If possible, use `withMutations` as it provides an easier to * use API. * * Note: if the collection is already mutable, `asMutable` returns itself. * * Note: Not all methods can be used on a mutable collection or within * `withMutations`! Only `set` and `merge` may be used mutatively. */ asMutable(): Map; /** * The yin to `asMutable`'s yang. Because it applies to mutable collections, * this operation is *mutable* and returns itself. Once performed, the mutable * copy has become immutable and can be safely returned from a function. */ asImmutable(): Map; } /** * A type of Map that has the additional guarantee that the iteration order of * entries will be the order in which they were set(). * * The iteration behavior of OrderedMap is the same as native ES6 Map and * JavaScript Object. * * Note that `OrderedMap` are more expensive than non-ordered `Map` and may * consume more memory. `OrderedMap#set` is amortized O(log32 N), but not * stable. */ export module OrderedMap { /** * True if the provided value is an OrderedMap. */ function isOrderedMap(maybeOrderedMap: any): boolean; } /** * Creates a new Immutable OrderedMap. * * Created with the same key value pairs as the provided Iterable.Keyed or * JavaScript Object or expects an Iterable of [K, V] tuple entries. * * The iteration order of key-value pairs provided to this constructor will * be preserved in the OrderedMap. * * var newOrderedMap = OrderedMap({key: "value"}); * var newOrderedMap = OrderedMap([["key", "value"]]); * */ export function OrderedMap(): OrderedMap; export function OrderedMap(iter: Iterable.Keyed): OrderedMap; export function OrderedMap( iter: Iterable>, ): OrderedMap; export function OrderedMap( array: Array>, ): OrderedMap; export function OrderedMap(obj: { [key: string]: V }): OrderedMap; export function OrderedMap( iterator: Iterator>, ): OrderedMap; export function OrderedMap( iterable: /*Iterable<[K,V]>*/ Object, ): OrderedMap; export interface OrderedMap extends Map {} /** * A Collection of unique values with `O(log32 N)` adds and has. * * When iterating a Set, the entries will be (value, value) pairs. Iteration * order of a Set is undefined, however is stable. Multiple iterations of the * same Set will iterate in the same order. * * Set values, like Map keys, may be of any type. Equality is determined using * `Immutable.is`, enabling Sets to uniquely include other Immutable * collections, custom value types, and NaN. */ export module Set { /** * True if the provided value is a Set */ function isSet(maybeSet: any): boolean; /** * Creates a new Set containing `values`. */ function of(...values: T[]): Set; /** * `Set.fromKeys()` creates a new immutable Set containing the keys from * this Iterable or JavaScript Object. */ function fromKeys(iter: Iterable): Set; function fromKeys(obj: { [key: string]: any }): Set; } /** * Create a new immutable Set containing the values of the provided * iterable-like. */ export function Set(): Set; export function Set(iter: Iterable.Set): Set; export function Set(iter: Iterable.Indexed): Set; export function Set(iter: Iterable.Keyed): Set; export function Set(array: Array): Set; export function Set(iterator: Iterator): Set; export function Set(iterable: /*Iterable*/ Object): Set; export interface Set extends Collection.Set { // Persistent changes /** * Returns a new Set which also includes this value. */ add(value: T): Set; /** * Returns a new Set which excludes this value. * * Note: `delete` cannot be safely used in IE8 * @alias remove */ delete(value: T): Set; remove(value: T): Set; /** * Returns a new Set containing no values. */ clear(): Set; /** * Returns a Set including any value from `iterables` that does not already * exist in this Set. * @alias merge */ union(...iterables: Iterable[]): Set; union(...iterables: Array[]): Set; merge(...iterables: Iterable[]): Set; merge(...iterables: Array[]): Set; /** * Returns a Set which has removed any values not also contained * within `iterables`. */ intersect(...iterables: Iterable[]): Set; intersect(...iterables: Array[]): Set; /** * Returns a Set excluding any values contained within `iterables`. */ subtract(...iterables: Iterable[]): Set; subtract(...iterables: Array[]): Set; // Transient changes /** * Note: Not all methods can be used on a mutable collection or within * `withMutations`! Only `add` may be used mutatively. * * @see `Map#withMutations` */ withMutations(mutator: (mutable: Set) => any): Set; /** * @see `Map#asMutable` */ asMutable(): Set; /** * @see `Map#asImmutable` */ asImmutable(): Set; } /** * A type of Set that has the additional guarantee that the iteration order of * values will be the order in which they were `add`ed. * * The iteration behavior of OrderedSet is the same as native ES6 Set. * * Note that `OrderedSet` are more expensive than non-ordered `Set` and may * consume more memory. `OrderedSet#add` is amortized O(log32 N), but not * stable. */ export module OrderedSet { /** * True if the provided value is an OrderedSet. */ function isOrderedSet(maybeOrderedSet: any): boolean; /** * Creates a new OrderedSet containing `values`. */ function of(...values: T[]): OrderedSet; /** * `OrderedSet.fromKeys()` creates a new immutable OrderedSet containing * the keys from this Iterable or JavaScript Object. */ function fromKeys(iter: Iterable): OrderedSet; function fromKeys(obj: { [key: string]: any }): OrderedSet; } /** * Create a new immutable OrderedSet containing the values of the provided * iterable-like. */ export function OrderedSet(): OrderedSet; export function OrderedSet(iter: Iterable.Set): OrderedSet; export function OrderedSet(iter: Iterable.Indexed): OrderedSet; export function OrderedSet( iter: Iterable.Keyed, ): OrderedSet; export function OrderedSet(array: Array): OrderedSet; export function OrderedSet(iterator: Iterator): OrderedSet; export function OrderedSet(iterable: /*Iterable*/ Object): OrderedSet; export interface OrderedSet extends Set {} /** * Stacks are indexed collections which support very efficient O(1) addition * and removal from the front using `unshift(v)` and `shift()`. * * For familiarity, Stack also provides `push(v)`, `pop()`, and `peek()`, but * be aware that they also operate on the front of the list, unlike List or * a JavaScript Array. * * Note: `reverse()` or any inherent reverse traversal (`reduceRight`, * `lastIndexOf`, etc.) is not efficient with a Stack. * * Stack is implemented with a Single-Linked List. */ export module Stack { /** * True if the provided value is a Stack */ function isStack(maybeStack: any): boolean; /** * Creates a new Stack containing `values`. */ function of(...values: T[]): Stack; } /** * Create a new immutable Stack containing the values of the provided * iterable-like. * * The iteration order of the provided iterable is preserved in the * resulting `Stack`. */ export function Stack(): Stack; export function Stack(iter: Iterable.Indexed): Stack; export function Stack(iter: Iterable.Set): Stack; export function Stack(iter: Iterable.Keyed): Stack; export function Stack(array: Array): Stack; export function Stack(iterator: Iterator): Stack; export function Stack(iterable: /*Iterable*/ Object): Stack; export interface Stack extends Collection.Indexed { // Reading values /** * Alias for `Stack.first()`. */ peek(): T; // Persistent changes /** * Returns a new Stack with 0 size and no values. */ clear(): Stack; /** * Returns a new Stack with the provided `values` prepended, shifting other * values ahead to higher indices. * * This is very efficient for Stack. */ unshift(...values: T[]): Stack; /** * Like `Stack#unshift`, but accepts a iterable rather than varargs. */ unshiftAll(iter: Iterable): Stack; unshiftAll(iter: Array): Stack; /** * Returns a new Stack with a size ones less than this Stack, excluding * the first item in this Stack, shifting all other values to a lower index. * * Note: this differs from `Array#shift` because it returns a new * Stack rather than the removed value. Use `first()` or `peek()` to get the * first value in this Stack. */ shift(): Stack; /** * Alias for `Stack#unshift` and is not equivalent to `List#push`. */ push(...values: T[]): Stack; /** * Alias for `Stack#unshiftAll`. */ pushAll(iter: Iterable): Stack; pushAll(iter: Array): Stack; /** * Alias for `Stack#shift` and is not equivalent to `List#pop`. */ pop(): Stack; // Transient changes /** * Note: Not all methods can be used on a mutable collection or within * `withMutations`! Only `set`, `push`, and `pop` may be used mutatively. * * @see `Map#withMutations` */ withMutations(mutator: (mutable: Stack) => any): Stack; /** * @see `Map#asMutable` */ asMutable(): Stack; /** * @see `Map#asImmutable` */ asImmutable(): Stack; } /** * Returns a Seq.Indexed of numbers from `start` (inclusive) to `end` * (exclusive), by `step`, where `start` defaults to 0, `step` to 1, and `end` to * infinity. When `start` is equal to `end`, returns empty range. * * Range() // [0,1,2,3,...] * Range(10) // [10,11,12,13,...] * Range(10,15) // [10,11,12,13,14] * Range(10,30,5) // [10,15,20,25] * Range(30,10,5) // [30,25,20,15] * Range(30,30,5) // [] * */ export function Range( start?: number, end?: number, step?: number, ): Seq.Indexed; /** * Returns a Seq.Indexed of `value` repeated `times` times. When `times` is * not defined, returns an infinite `Seq` of `value`. * * Repeat('foo') // ['foo','foo','foo',...] * Repeat('bar',4) // ['bar','bar','bar','bar'] * */ export function Repeat(value: T, times?: number): Seq.Indexed; /** * Creates a new Class which produces Record instances. A record is similar to * a JS object, but enforce a specific set of allowed string keys, and have * default values. * * var ABRecord = Record({a:1, b:2}) * var myRecord = new ABRecord({b:3}) * * Records always have a value for the keys they define. `remove`ing a key * from a record simply resets it to the default value for that key. * * myRecord.size // 2 * myRecord.get('a') // 1 * myRecord.get('b') // 3 * myRecordWithoutB = myRecord.remove('b') * myRecordWithoutB.get('b') // 2 * myRecordWithoutB.size // 2 * * Values provided to the constructor not found in the Record type will * be ignored. For example, in this case, ABRecord is provided a key "x" even * though only "a" and "b" have been defined. The value for "x" will be * ignored for this record. * * var myRecord = new ABRecord({b:3, x:10}) * myRecord.get('x') // undefined * * Because Records have a known set of string keys, property get access works * as expected, however property sets will throw an Error. * * Note: IE8 does not support property access. Only use `get()` when * supporting IE8. * * myRecord.b // 3 * myRecord.b = 5 // throws Error * * Record Classes can be extended as well, allowing for custom methods on your * Record. This is not a common pattern in functional environments, but is in * many JS programs. * * Note: TypeScript does not support this type of subclassing. * * class ABRecord extends Record({a:1,b:2}) { * getAB() { * return this.a + this.b; * } * } * * var myRecord = new ABRecord({b: 3}) * myRecord.getAB() // 4 * */ export module Record { export interface Class { new (): Map; new (values: { [key: string]: any }): Map; new (values: Iterable): Map; // deprecated (): Map; (values: { [key: string]: any }): Map; (values: Iterable): Map; // deprecated } } export function Record( defaultValues: { [key: string]: any }, name?: string, ): Record.Class; /** * Represents a sequence of values, but may not be backed by a concrete data * structure. * * **Seq is immutable** — Once a Seq is created, it cannot be * changed, appended to, rearranged or otherwise modified. Instead, any * mutative method called on a `Seq` will return a new `Seq`. * * **Seq is lazy** — Seq does as little work as necessary to respond to any * method call. Values are often created during iteration, including implicit * iteration when reducing or converting to a concrete data structure such as * a `List` or JavaScript `Array`. * * For example, the following performs no work, because the resulting * Seq's values are never iterated: * * var oddSquares = Immutable.Seq.of(1,2,3,4,5,6,7,8) * .filter(x => x % 2).map(x => x * x); * * Once the Seq is used, it performs only the work necessary. In this * example, no intermediate data structures are ever created, filter is only * called three times, and map is only called once: * * console.log(oddSquares.get(1)); // 9 * * Seq allows for the efficient chaining of operations, * allowing for the expression of logic that can otherwise be very tedious: * * Immutable.Seq({a:1, b:1, c:1}) * .flip().map(key => key.toUpperCase()).flip().toObject(); * // Map { A: 1, B: 1, C: 1 } * * As well as expressing logic that would otherwise be memory or time limited: * * Immutable.Range(1, Infinity) * .skip(1000) * .map(n => -n) * .filter(n => n % 2 === 0) * .take(2) * .reduce((r, n) => r * n, 1); * // 1006008 * * Seq is often used to provide a rich collection API to JavaScript Object. * * Immutable.Seq({ x: 0, y: 1, z: 2 }).map(v => v * 2).toObject(); * // { x: 0, y: 2, z: 4 } */ export module Seq { /** * True if `maybeSeq` is a Seq, it is not backed by a concrete * structure such as Map, List, or Set. */ function isSeq(maybeSeq: any): boolean; /** * Returns a Seq of the values provided. Alias for `Seq.Indexed.of()`. */ function of(...values: T[]): Seq.Indexed; /** * `Seq` which represents key-value pairs. */ export module Keyed {} /** * Always returns a Seq.Keyed, if input is not keyed, expects an * iterable of [K, V] tuples. */ export function Keyed(): Seq.Keyed; export function Keyed(seq: Iterable.Keyed): Seq.Keyed; export function Keyed( seq: Iterable, ): Seq.Keyed; export function Keyed(array: Array): Seq.Keyed; export function Keyed(obj: { [key: string]: V }): Seq.Keyed; export function Keyed( iterator: Iterator, ): Seq.Keyed; export function Keyed( iterable: /*Iterable<[K,V]>*/ Object, ): Seq.Keyed; export interface Keyed extends Seq, Iterable.Keyed { /** * Returns itself */ toSeq(): /*this*/ Seq.Keyed; } /** * `Seq` which represents an ordered indexed list of values. */ module Indexed { /** * Provides an Seq.Indexed of the values provided. */ function of(...values: T[]): Seq.Indexed; } /** * Always returns Seq.Indexed, discarding associated keys and * supplying incrementing indices. */ export function Indexed(): Seq.Indexed; export function Indexed(seq: Iterable.Indexed): Seq.Indexed; export function Indexed(seq: Iterable.Set): Seq.Indexed; export function Indexed( seq: Iterable.Keyed, ): Seq.Indexed; export function Indexed(array: Array): Seq.Indexed; export function Indexed(iterator: Iterator): Seq.Indexed; export function Indexed(iterable: /*Iterable*/ Object): Seq.Indexed; export interface Indexed extends Seq, Iterable.Indexed { /** * Returns itself */ toSeq(): /*this*/ Seq.Indexed; } /** * `Seq` which represents a set of values. * * Because `Seq` are often lazy, `Seq.Set` does not provide the same guarantee * of value uniqueness as the concrete `Set`. */ export module Set { /** * Returns a Seq.Set of the provided values */ function of(...values: T[]): Seq.Set; } /** * Always returns a Seq.Set, discarding associated indices or keys. */ export function Set(): Seq.Set; export function Set(seq: Iterable.Set): Seq.Set; export function Set(seq: Iterable.Indexed): Seq.Set; export function Set(seq: Iterable.Keyed): Seq.Set; export function Set(array: Array): Seq.Set; export function Set(iterator: Iterator): Seq.Set; export function Set(iterable: /*Iterable*/ Object): Seq.Set; export interface Set extends Seq, Iterable.Set { /** * Returns itself */ toSeq(): /*this*/ Seq.Set; } } /** * Creates a Seq. * * Returns a particular kind of `Seq` based on the input. * * * If a `Seq`, that same `Seq`. * * If an `Iterable`, a `Seq` of the same kind (Keyed, Indexed, or Set). * * If an Array-like, an `Seq.Indexed`. * * If an Object with an Iterator, an `Seq.Indexed`. * * If an Iterator, an `Seq.Indexed`. * * If an Object, a `Seq.Keyed`. * */ export function Seq(): Seq; export function Seq(seq: Seq): Seq; export function Seq(iterable: Iterable): Seq; export function Seq(array: Array): Seq.Indexed; export function Seq(obj: { [key: string]: V }): Seq.Keyed; export function Seq(iterator: Iterator): Seq.Indexed; export function Seq(iterable: /*ES6Iterable*/ Object): Seq.Indexed; export interface Seq extends Iterable { /** * Some Seqs can describe their size lazily. When this is the case, * size will be an integer. Otherwise it will be undefined. * * For example, Seqs returned from `map()` or `reverse()` * preserve the size of the original `Seq` while `filter()` does not. * * Note: `Range`, `Repeat` and `Seq`s made from `Array`s and `Object`s will * always have a size. */ size: number /*?*/; // Force evaluation /** * Because Sequences are lazy and designed to be chained together, they do * not cache their results. For example, this map function is called a total * of 6 times, as each `join` iterates the Seq of three values. * * var squares = Seq.of(1,2,3).map(x => x * x); * squares.join() + squares.join(); * * If you know a `Seq` will be used multiple times, it may be more * efficient to first cache it in memory. Here, the map function is called * only 3 times. * * var squares = Seq.of(1,2,3).map(x => x * x).cacheResult(); * squares.join() + squares.join(); * * Use this method judiciously, as it must fully evaluate a Seq which can be * a burden on memory and possibly performance. * * Note: after calling `cacheResult`, a Seq will always have a `size`. */ cacheResult(): /*this*/ Seq; } /** * The `Iterable` is a set of (key, value) entries which can be iterated, and * is the base class for all collections in `immutable`, allowing them to * make use of all the Iterable methods (such as `map` and `filter`). * * Note: An iterable is always iterated in the same order, however that order * may not always be well defined, as is the case for the `Map` and `Set`. */ export module Iterable { /** * True if `maybeIterable` is an Iterable, or any of its subclasses. */ function isIterable(maybeIterable: any): boolean; /** * True if `maybeKeyed` is an Iterable.Keyed, or any of its subclasses. */ function isKeyed(maybeKeyed: any): boolean; /** * True if `maybeIndexed` is a Iterable.Indexed, or any of its subclasses. */ function isIndexed(maybeIndexed: any): boolean; /** * True if `maybeAssociative` is either a keyed or indexed Iterable. */ function isAssociative(maybeAssociative: any): boolean; /** * True if `maybeOrdered` is an Iterable where iteration order is well * defined. True for Iterable.Indexed as well as OrderedMap and OrderedSet. */ function isOrdered(maybeOrdered: any): boolean; /** * Keyed Iterables have discrete keys tied to each value. * * When iterating `Iterable.Keyed`, each iteration will yield a `[K, V]` * tuple, in other words, `Iterable#entries` is the default iterator for * Keyed Iterables. */ export module Keyed {} /** * Creates an Iterable.Keyed * * Similar to `Iterable()`, however it expects iterable-likes of [K, V] * tuples if not constructed from a Iterable.Keyed or JS Object. */ export function Keyed(iter: Iterable.Keyed): Iterable.Keyed; export function Keyed( iter: Iterable, ): Iterable.Keyed; export function Keyed( array: Array, ): Iterable.Keyed; export function Keyed(obj: { [key: string]: V; }): Iterable.Keyed; export function Keyed( iterator: Iterator, ): Iterable.Keyed; export function Keyed( iterable: /*Iterable<[K,V]>*/ Object, ): Iterable.Keyed; export interface Keyed extends Iterable { /** * Returns Seq.Keyed. * @override */ toSeq(): Seq.Keyed; // Sequence functions /** * Returns a new Iterable.Keyed of the same type where the keys and values * have been flipped. * * Seq({ a: 'z', b: 'y' }).flip() // { z: 'a', y: 'b' } * */ flip(): /*this*/ Iterable.Keyed; /** * Returns a new Iterable.Keyed of the same type with keys passed through * a `mapper` function. * * Seq({ a: 1, b: 2 }) * .mapKeys(x => x.toUpperCase()) * // Seq { A: 1, B: 2 } * */ mapKeys( mapper: (key?: K, value?: V, iter?: /*this*/ Iterable.Keyed) => M, context?: any, ): /*this*/ Iterable.Keyed; /** * Returns a new Iterable.Keyed of the same type with entries * ([key, value] tuples) passed through a `mapper` function. * * Seq({ a: 1, b: 2 }) * .mapEntries(([k, v]) => [k.toUpperCase(), v * 2]) * // Seq { A: 2, B: 4 } * */ mapEntries( mapper: ( entry?: /*(K, V)*/ Array, index?: number, iter?: /*this*/ Iterable.Keyed, ) => /*[KM, VM]*/ Array, context?: any, ): /*this*/ Iterable.Keyed; } /** * Indexed Iterables have incrementing numeric keys. They exhibit * slightly different behavior than `Iterable.Keyed` for some methods in order * to better mirror the behavior of JavaScript's `Array`, and add methods * which do not make sense on non-indexed Iterables such as `indexOf`. * * Unlike JavaScript arrays, `Iterable.Indexed`s are always dense. "Unset" * indices and `undefined` indices are indistinguishable, and all indices from * 0 to `size` are visited when iterated. * * All Iterable.Indexed methods return re-indexed Iterables. In other words, * indices always start at 0 and increment until size. If you wish to * preserve indices, using them as keys, convert to a Iterable.Keyed by * calling `toKeyedSeq`. */ export module Indexed {} /** * Creates a new Iterable.Indexed. */ export function Indexed(iter: Iterable.Indexed): Iterable.Indexed; export function Indexed(iter: Iterable.Set): Iterable.Indexed; export function Indexed( iter: Iterable.Keyed, ): Iterable.Indexed; export function Indexed(array: Array): Iterable.Indexed; export function Indexed(iterator: Iterator): Iterable.Indexed; export function Indexed( iterable: /*Iterable*/ Object, ): Iterable.Indexed; export interface Indexed extends Iterable { // Reading values /** * Returns the value associated with the provided index, or notSetValue if * the index is beyond the bounds of the Iterable. * * `index` may be a negative number, which indexes back from the end of the * Iterable. `s.get(-1)` gets the last item in the Iterable. */ get(index: number, notSetValue?: T): T; // Conversion to Seq /** * Returns Seq.Indexed. * @override */ toSeq(): Seq.Indexed; /** * If this is an iterable of [key, value] entry tuples, it will return a * Seq.Keyed of those entries. */ fromEntrySeq(): Seq.Keyed; // Combination /** * Returns an Iterable of the same type with `separator` between each item * in this Iterable. */ interpose(separator: T): /*this*/ Iterable.Indexed; /** * Returns an Iterable of the same type with the provided `iterables` * interleaved into this iterable. * * The resulting Iterable includes the first item from each, then the * second from each, etc. * * I.Seq.of(1,2,3).interleave(I.Seq.of('A','B','C')) * // Seq [ 1, 'A', 2, 'B', 3, 'C' ] * * The shortest Iterable stops interleave. * * I.Seq.of(1,2,3).interleave( * I.Seq.of('A','B'), * I.Seq.of('X','Y','Z') * ) * // Seq [ 1, 'A', 'X', 2, 'B', 'Y' ] */ interleave( ...iterables: Array> ): /*this*/ Iterable.Indexed; /** * Splice returns a new indexed Iterable by replacing a region of this * Iterable with new values. If values are not provided, it only skips the * region to be removed. * * `index` may be a negative number, which indexes back from the end of the * Iterable. `s.splice(-2)` splices after the second to last item. * * Seq(['a','b','c','d']).splice(1, 2, 'q', 'r', 's') * // Seq ['a', 'q', 'r', 's', 'd'] * */ splice( index: number, removeNum: number, ...values: /*Array | T>*/ any[] ): /*this*/ Iterable.Indexed; /** * Returns an Iterable of the same type "zipped" with the provided * iterables. * * Like `zipWith`, but using the default `zipper`: creating an `Array`. * * var a = Seq.of(1, 2, 3); * var b = Seq.of(4, 5, 6); * var c = a.zip(b); // Seq [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ] * */ zip( ...iterables: Array> ): /*this*/ Iterable.Indexed; /** * Returns an Iterable of the same type "zipped" with the provided * iterables by using a custom `zipper` function. * * var a = Seq.of(1, 2, 3); * var b = Seq.of(4, 5, 6); * var c = a.zipWith((a, b) => a + b, b); // Seq [ 5, 7, 9 ] * */ zipWith( zipper: (value: T, otherValue: U) => Z, otherIterable: Iterable, ): Iterable.Indexed; zipWith( zipper: (value: T, otherValue: U, thirdValue: V) => Z, otherIterable: Iterable, thirdIterable: Iterable, ): Iterable.Indexed; zipWith( zipper: (...any: Array) => Z, ...iterables: Array> ): Iterable.Indexed; // Search for value /** * Returns the first index at which a given value can be found in the * Iterable, or -1 if it is not present. */ indexOf(searchValue: T): number; /** * Returns the last index at which a given value can be found in the * Iterable, or -1 if it is not present. */ lastIndexOf(searchValue: T): number; /** * Returns the first index in the Iterable where a value satisfies the * provided predicate function. Otherwise -1 is returned. */ findIndex( predicate: ( value?: T, index?: number, iter?: /*this*/ Iterable.Indexed, ) => boolean, context?: any, ): number; /** * Returns the last index in the Iterable where a value satisfies the * provided predicate function. Otherwise -1 is returned. */ findLastIndex( predicate: ( value?: T, index?: number, iter?: /*this*/ Iterable.Indexed, ) => boolean, context?: any, ): number; } /** * Set Iterables only represent values. They have no associated keys or * indices. Duplicate values are possible in Seq.Sets, however the * concrete `Set` does not allow duplicate values. * * Iterable methods on Iterable.Set such as `map` and `forEach` will provide * the value as both the first and second arguments to the provided function. * * var seq = Seq.Set.of('A', 'B', 'C'); * assert.equal(seq.every((v, k) => v === k), true); * */ export module Set {} /** * Similar to `Iterable()`, but always returns a Iterable.Set. */ export function Set(iter: Iterable.Set): Iterable.Set; export function Set(iter: Iterable.Indexed): Iterable.Set; export function Set( iter: Iterable.Keyed, ): Iterable.Set; export function Set(array: Array): Iterable.Set; export function Set(iterator: Iterator): Iterable.Set; export function Set(iterable: /*Iterable*/ Object): Iterable.Set; export interface Set extends Iterable { /** * Returns Seq.Set. * @override */ toSeq(): Seq.Set; } } /** * Creates an Iterable. * * The type of Iterable created is based on the input. * * * If an `Iterable`, that same `Iterable`. * * If an Array-like, an `Iterable.Indexed`. * * If an Object with an Iterator, an `Iterable.Indexed`. * * If an Iterator, an `Iterable.Indexed`. * * If an Object, an `Iterable.Keyed`. * * This methods forces the conversion of Objects and Strings to Iterables. * If you want to ensure that a Iterable of one item is returned, use * `Seq.of`. */ export function Iterable(iterable: Iterable): Iterable; export function Iterable(array: Array): Iterable.Indexed; export function Iterable(obj: { [key: string]: V; }): Iterable.Keyed; export function Iterable(iterator: Iterator): Iterable.Indexed; export function Iterable( iterable: /*ES6Iterable*/ Object, ): Iterable.Indexed; export function Iterable(value: V): Iterable.Indexed; export interface Iterable { // Value equality /** * True if this and the other Iterable have value equality, as defined * by `Immutable.is()`. * * Note: This is equivalent to `Immutable.is(this, other)`, but provided to * allow for chained expressions. */ equals(other: Iterable): boolean; /** * Computes and returns the hashed identity for this Iterable. * * The `hashCode` of an Iterable is used to determine potential equality, * and is used when adding this to a `Set` or as a key in a `Map`, enabling * lookup via a different instance. * * var a = List.of(1, 2, 3); * var b = List.of(1, 2, 3); * assert(a !== b); // different instances * var set = Set.of(a); * assert(set.has(b) === true); * * If two values have the same `hashCode`, they are [not guaranteed * to be equal][Hash Collision]. If two values have different `hashCode`s, * they must not be equal. * * [Hash Collision]: http://en.wikipedia.org/wiki/Collision_(computer_science) */ hashCode(): number; // Reading values /** * Returns the value associated with the provided key, or notSetValue if * the Iterable does not contain this key. * * Note: it is possible a key may be associated with an `undefined` value, * so if `notSetValue` is not provided and this method returns `undefined`, * that does not guarantee the key was not found. */ get(key: K, notSetValue?: V): V; /** * True if a key exists within this `Iterable`, using `Immutable.is` to determine equality */ has(key: K): boolean; /** * True if a value exists within this `Iterable`, using `Immutable.is` to determine equality * @alias contains */ includes(value: V): boolean; contains(value: V): boolean; /** * The first value in the Iterable. */ first(): V; /** * The last value in the Iterable. */ last(): V; // Reading deep values /** * Returns the value found by following a path of keys or indices through * nested Iterables. */ getIn(searchKeyPath: Array, notSetValue?: any): any; getIn(searchKeyPath: Iterable, notSetValue?: any): any; /** * True if the result of following a path of keys or indices through nested * Iterables results in a set value. */ hasIn(searchKeyPath: Array): boolean; hasIn(searchKeyPath: Iterable): boolean; // Conversion to JavaScript types /** * Deeply converts this Iterable to equivalent JS. * * `Iterable.Indexeds`, and `Iterable.Sets` become Arrays, while * `Iterable.Keyeds` become Objects. * * @alias toJSON */ toJS(): any; /** * Shallowly converts this iterable to an Array, discarding keys. */ toArray(): Array; /** * Shallowly converts this Iterable to an Object. * * Throws if keys are not strings. */ toObject(): { [key: string]: V }; // Conversion to Collections /** * Converts this Iterable to a Map, Throws if keys are not hashable. * * Note: This is equivalent to `Map(this.toKeyedSeq())`, but provided * for convenience and to allow for chained expressions. */ toMap(): Map; /** * Converts this Iterable to a Map, maintaining the order of iteration. * * Note: This is equivalent to `OrderedMap(this.toKeyedSeq())`, but * provided for convenience and to allow for chained expressions. */ toOrderedMap(): OrderedMap; /** * Converts this Iterable to a Set, discarding keys. Throws if values * are not hashable. * * Note: This is equivalent to `Set(this)`, but provided to allow for * chained expressions. */ toSet(): Set; /** * Converts this Iterable to a Set, maintaining the order of iteration and * discarding keys. * * Note: This is equivalent to `OrderedSet(this.valueSeq())`, but provided * for convenience and to allow for chained expressions. */ toOrderedSet(): OrderedSet; /** * Converts this Iterable to a List, discarding keys. * * Note: This is equivalent to `List(this)`, but provided to allow * for chained expressions. */ toList(): List; /** * Converts this Iterable to a Stack, discarding keys. Throws if values * are not hashable. * * Note: This is equivalent to `Stack(this)`, but provided to allow for * chained expressions. */ toStack(): Stack; // Conversion to Seq /** * Converts this Iterable to a Seq of the same kind (indexed, * keyed, or set). */ toSeq(): Seq; /** * Returns a Seq.Keyed from this Iterable where indices are treated as keys. * * This is useful if you want to operate on an * Iterable.Indexed and preserve the [index, value] pairs. * * The returned Seq will have identical iteration order as * this Iterable. * * Example: * * var indexedSeq = Immutable.Seq.of('A', 'B', 'C'); * indexedSeq.filter(v => v === 'B').toString() // Seq [ 'B' ] * var keyedSeq = indexedSeq.toKeyedSeq(); * keyedSeq.filter(v => v === 'B').toString() // Seq { 1: 'B' } * */ toKeyedSeq(): Seq.Keyed; /** * Returns an Seq.Indexed of the values of this Iterable, discarding keys. */ toIndexedSeq(): Seq.Indexed; /** * Returns a Seq.Set of the values of this Iterable, discarding keys. */ toSetSeq(): Seq.Set; // Iterators /** * An iterator of this `Iterable`'s keys. * * Note: this will return an ES6 iterator which does not support Immutable JS sequence algorithms. Use `keySeq` instead, if this is what you want. */ keys(): Iterator; /** * An iterator of this `Iterable`'s values. * * Note: this will return an ES6 iterator which does not support Immutable JS sequence algorithms. Use `valueSeq` instead, if this is what you want. */ values(): Iterator; /** * An iterator of this `Iterable`'s entries as `[key, value]` tuples. * * Note: this will return an ES6 iterator which does not support Immutable JS sequence algorithms. Use `entrySeq` instead, if this is what you want. */ entries(): Iterator>; // Iterables (Seq) /** * Returns a new Seq.Indexed of the keys of this Iterable, * discarding values. */ keySeq(): Seq.Indexed; /** * Returns an Seq.Indexed of the values of this Iterable, discarding keys. */ valueSeq(): Seq.Indexed; /** * Returns a new Seq.Indexed of [key, value] tuples. */ entrySeq(): Seq.Indexed>; // Sequence algorithms /** * Returns a new Iterable of the same type with values passed through a * `mapper` function. * * Seq({ a: 1, b: 2 }).map(x => 10 * x) * // Seq { a: 10, b: 20 } * */ map( mapper: (value?: V, key?: K, iter?: /*this*/ Iterable) => M, context?: any, ): /*this*/ Iterable; /** * Returns a new Iterable of the same type with only the entries for which * the `predicate` function returns true. * * Seq({a:1,b:2,c:3,d:4}).filter(x => x % 2 === 0) * // Seq { b: 2, d: 4 } * */ filter( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, ): /*this*/ Iterable; /** * Returns a new Iterable of the same type with only the entries for which * the `predicate` function returns false. * * Seq({a:1,b:2,c:3,d:4}).filterNot(x => x % 2 === 0) * // Seq { a: 1, c: 3 } * */ filterNot( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, ): /*this*/ Iterable; /** * Returns a new Iterable of the same type in reverse order. */ reverse(): /*this*/ Iterable; /** * Returns a new Iterable of the same type which includes the same entries, * stably sorted by using a `comparator`. * * If a `comparator` is not provided, a default comparator uses `<` and `>`. * * `comparator(valueA, valueB)`: * * * Returns `0` if the elements should not be swapped. * * Returns `-1` (or any negative number) if `valueA` comes before `valueB` * * Returns `1` (or any positive number) if `valueA` comes after `valueB` * * Is pure, i.e. it must always return the same value for the same pair * of values. * * When sorting collections which have no defined order, their ordered * equivalents will be returned. e.g. `map.sort()` returns OrderedMap. */ sort(comparator?: (valueA: V, valueB: V) => number): /*this*/ Iterable; /** * Like `sort`, but also accepts a `comparatorValueMapper` which allows for * sorting by more sophisticated means: * * hitters.sortBy(hitter => hitter.avgHits); * */ sortBy( comparatorValueMapper: ( value?: V, key?: K, iter?: /*this*/ Iterable, ) => C, comparator?: (valueA: C, valueB: C) => number, ): /*this*/ Iterable; /** * Returns a `Iterable.Keyed` of `Iterable.Keyeds`, grouped by the return * value of the `grouper` function. * * Note: This is always an eager operation. */ groupBy( grouper: (value?: V, key?: K, iter?: /*this*/ Iterable) => G, context?: any, ): /*Map*/ Seq.Keyed>; // Side effects /** * The `sideEffect` is executed for every entry in the Iterable. * * Unlike `Array#forEach`, if any call of `sideEffect` returns * `false`, the iteration will stop. Returns the number of entries iterated * (including the last iteration which returned false). */ forEach( sideEffect: (value?: V, key?: K, iter?: /*this*/ Iterable) => any, context?: any, ): number; // Creating subsets /** * Returns a new Iterable of the same type representing a portion of this * Iterable from start up to but not including end. * * If begin is negative, it is offset from the end of the Iterable. e.g. * `slice(-2)` returns a Iterable of the last two entries. If it is not * provided the new Iterable will begin at the beginning of this Iterable. * * If end is negative, it is offset from the end of the Iterable. e.g. * `slice(0, -1)` returns an Iterable of everything but the last entry. If * it is not provided, the new Iterable will continue through the end of * this Iterable. * * If the requested slice is equivalent to the current Iterable, then it * will return itself. */ slice(begin?: number, end?: number): /*this*/ Iterable; /** * Returns a new Iterable of the same type containing all entries except * the first. */ rest(): /*this*/ Iterable; /** * Returns a new Iterable of the same type containing all entries except * the last. */ butLast(): /*this*/ Iterable; /** * Returns a new Iterable of the same type which excludes the first `amount` * entries from this Iterable. */ skip(amount: number): /*this*/ Iterable; /** * Returns a new Iterable of the same type which excludes the last `amount` * entries from this Iterable. */ skipLast(amount: number): /*this*/ Iterable; /** * Returns a new Iterable of the same type which includes entries starting * from when `predicate` first returns false. * * Seq.of('dog','frog','cat','hat','god') * .skipWhile(x => x.match(/g/)) * // Seq [ 'cat', 'hat', 'god' ] * */ skipWhile( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, ): /*this*/ Iterable; /** * Returns a new Iterable of the same type which includes entries starting * from when `predicate` first returns true. * * Seq.of('dog','frog','cat','hat','god') * .skipUntil(x => x.match(/hat/)) * // Seq [ 'hat', 'god' ] * */ skipUntil( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, ): /*this*/ Iterable; /** * Returns a new Iterable of the same type which includes the first `amount` * entries from this Iterable. */ take(amount: number): /*this*/ Iterable; /** * Returns a new Iterable of the same type which includes the last `amount` * entries from this Iterable. */ takeLast(amount: number): /*this*/ Iterable; /** * Returns a new Iterable of the same type which includes entries from this * Iterable as long as the `predicate` returns true. * * Seq.of('dog','frog','cat','hat','god') * .takeWhile(x => x.match(/o/)) * // Seq [ 'dog', 'frog' ] * */ takeWhile( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, ): /*this*/ Iterable; /** * Returns a new Iterable of the same type which includes entries from this * Iterable as long as the `predicate` returns false. * * Seq.of('dog','frog','cat','hat','god').takeUntil(x => x.match(/at/)) * // ['dog', 'frog'] * */ takeUntil( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, ): /*this*/ Iterable; // Combination /** * Returns a new Iterable of the same type with other values and * iterable-like concatenated to this one. * * For Seqs, all entries will be present in * the resulting iterable, even if they have the same key. */ concat( ...valuesOrIterables: /*Array|V*/ any[] ): /*this*/ Iterable; /** * Flattens nested Iterables. * * Will deeply flatten the Iterable by default, returning an Iterable of the * same type, but a `depth` can be provided in the form of a number or * boolean (where true means to shallowly flatten one level). A depth of 0 * (or shallow: false) will deeply flatten. * * Flattens only others Iterable, not Arrays or Objects. * * Note: `flatten(true)` operates on Iterable> and * returns Iterable */ flatten(depth?: number): /*this*/ Iterable; flatten(shallow?: boolean): /*this*/ Iterable; /** * Flat-maps the Iterable, returning an Iterable of the same type. * * Similar to `iter.map(...).flatten(true)`. */ flatMap( mapper: ( value?: V, key?: K, iter?: /*this*/ Iterable, ) => Iterable, context?: any, ): /*this*/ Iterable; flatMap( mapper: ( value?: V, key?: K, iter?: /*this*/ Iterable, ) => /*iterable-like*/ any, context?: any, ): /*this*/ Iterable; // Reducing a value /** * Reduces the Iterable to a value by calling the `reducer` for every entry * in the Iterable and passing along the reduced value. * * If `initialReduction` is not provided, or is null, the first item in the * Iterable will be used. * * @see `Array#reduce`. */ reduce( reducer: ( reduction?: R, value?: V, key?: K, iter?: /*this*/ Iterable, ) => R, initialReduction?: R, context?: any, ): R; /** * Reduces the Iterable in reverse (from the right side). * * Note: Similar to this.reverse().reduce(), and provided for parity * with `Array#reduceRight`. */ reduceRight( reducer: ( reduction?: R, value?: V, key?: K, iter?: /*this*/ Iterable, ) => R, initialReduction?: R, context?: any, ): R; /** * True if `predicate` returns true for all entries in the Iterable. */ every( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, ): boolean; /** * True if `predicate` returns true for any entry in the Iterable. */ some( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, ): boolean; /** * Joins values together as a string, inserting a separator between each. * The default separator is `","`. */ join(separator?: string): string; /** * Returns true if this Iterable includes no values. * * For some lazy `Seq`, `isEmpty` might need to iterate to determine * emptiness. At most one iteration will occur. */ isEmpty(): boolean; /** * Returns the size of this Iterable. * * Regardless of if this Iterable can describe its size lazily (some Seqs * cannot), this method will always return the correct size. E.g. it * evaluates a lazy `Seq` if necessary. * * If `predicate` is provided, then this returns the count of entries in the * Iterable for which the `predicate` returns true. */ count(): number; count( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, ): number; /** * Returns a `Seq.Keyed` of counts, grouped by the return value of * the `grouper` function. * * Note: This is not a lazy operation. */ countBy( grouper: (value?: V, key?: K, iter?: /*this*/ Iterable) => G, context?: any, ): Map; // Search for value /** * Returns the first value for which the `predicate` returns true. */ find( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, notSetValue?: V, ): V; /** * Returns the last value for which the `predicate` returns true. * * Note: `predicate` will be called for each entry in reverse. */ findLast( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, notSetValue?: V, ): V; /** * Returns the first [key, value] entry for which the `predicate` returns true. */ findEntry( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, notSetValue?: V, ): /*[K, V]*/ Array; /** * Returns the last [key, value] entry for which the `predicate` * returns true. * * Note: `predicate` will be called for each entry in reverse. */ findLastEntry( predicate: (value?: V, key?: K, iter?: /*this*/ Iterable) => boolean, context?: any, notSetValue?: V, ): /*[K, V]*/ Array; /** * Returns the key for which the `predicate` returns true. */ findKey( predicate: ( value?: V, key?: K, iter?: /*this*/ Iterable.Keyed, ) => boolean, context?: any, ): K; /** * Returns the last key for which the `predicate` returns true. * * Note: `predicate` will be called for each entry in reverse. */ findLastKey( predicate: ( value?: V, key?: K, iter?: /*this*/ Iterable.Keyed, ) => boolean, context?: any, ): K; /** * Returns the key associated with the search value, or undefined. */ keyOf(searchValue: V): K; /** * Returns the last key associated with the search value, or undefined. */ lastKeyOf(searchValue: V): K; /** * Returns the maximum value in this collection. If any values are * comparatively equivalent, the first one found will be returned. * * The `comparator` is used in the same way as `Iterable#sort`. If it is not * provided, the default comparator is `>`. * * When two values are considered equivalent, the first encountered will be * returned. Otherwise, `max` will operate independent of the order of input * as long as the comparator is commutative. The default comparator `>` is * commutative *only* when types do not differ. * * If `comparator` returns 0 and either value is NaN, undefined, or null, * that value will be returned. */ max(comparator?: (valueA: V, valueB: V) => number): V; /** * Like `max`, but also accepts a `comparatorValueMapper` which allows for * comparing by more sophisticated means: * * hitters.maxBy(hitter => hitter.avgHits); * */ maxBy( comparatorValueMapper: ( value?: V, key?: K, iter?: /*this*/ Iterable, ) => C, comparator?: (valueA: C, valueB: C) => number, ): V; /** * Returns the minimum value in this collection. If any values are * comparatively equivalent, the first one found will be returned. * * The `comparator` is used in the same way as `Iterable#sort`. If it is not * provided, the default comparator is `<`. * * When two values are considered equivalent, the first encountered will be * returned. Otherwise, `min` will operate independent of the order of input * as long as the comparator is commutative. The default comparator `<` is * commutative *only* when types do not differ. * * If `comparator` returns 0 and either value is NaN, undefined, or null, * that value will be returned. */ min(comparator?: (valueA: V, valueB: V) => number): V; /** * Like `min`, but also accepts a `comparatorValueMapper` which allows for * comparing by more sophisticated means: * * hitters.minBy(hitter => hitter.avgHits); * */ minBy( comparatorValueMapper: ( value?: V, key?: K, iter?: /*this*/ Iterable, ) => C, comparator?: (valueA: C, valueB: C) => number, ): V; // Comparison /** * True if `iter` includes every value in this Iterable. */ isSubset(iter: Iterable): boolean; isSubset(iter: Array): boolean; /** * True if this Iterable includes every value in `iter`. */ isSuperset(iter: Iterable): boolean; isSuperset(iter: Array): boolean; /** * Note: this is here as a convenience to work around an issue with * TypeScript https://github.com/Microsoft/TypeScript/issues/285, but * Iterable does not define `size`, instead `Seq` defines `size` as * nullable number, and `Collection` defines `size` as always a number. * * @ignore */ size: number; } /** * Collection is the abstract base class for concrete data structures. It * cannot be constructed directly. * * Implementations should extend one of the subclasses, `Collection.Keyed`, * `Collection.Indexed`, or `Collection.Set`. */ export module Collection { /** * `Collection` which represents key-value pairs. */ export module Keyed {} export interface Keyed extends Collection, Iterable.Keyed { /** * Returns Seq.Keyed. * @override */ toSeq(): Seq.Keyed; } /** * `Collection` which represents ordered indexed values. */ export module Indexed {} export interface Indexed extends Collection, Iterable.Indexed { /** * Returns Seq.Indexed. * @override */ toSeq(): Seq.Indexed; } /** * `Collection` which represents values, unassociated with keys or indices. * * `Collection.Set` implementations should guarantee value uniqueness. */ export module Set {} export interface Set extends Collection, Iterable.Set { /** * Returns Seq.Set. * @override */ toSeq(): Seq.Set; } } export interface Collection extends Iterable { /** * All collections maintain their current `size` as an integer. */ size: number; } /** * ES6 Iterator. * * This is not part of the Immutable library, but a common interface used by * many types in ES6 JavaScript. * * @ignore */ export interface Iterator { next(): { value: T; done: boolean }; } ================================================ FILE: packages/victory-core/src/victory-util/immutable.test.ts ================================================ import * as Immutable from "./immutable"; describe("victory-util/immutable", () => { it("should have valid type guards", () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars function noop(arg: unknown) {} // eslint-disable-next-line @typescript-eslint/no-unused-vars function typeChecks() { const u: unknown = null; // @ts-expect-error "u is unknown" u.asImmutable(); if (Immutable.isIterable(u)) { u.count(); } if (Immutable.isList(u)) { u.asImmutable(); u.countBy((x) => x); } if (Immutable.isMap(u)) { u.asImmutable(); u.countBy((x) => x); } if (Immutable.isRecord(u)) { noop(u.ANYTHING); } } }); }); ================================================ FILE: packages/victory-core/src/victory-util/immutable.ts ================================================ import type { Iterable, Record, List, Map } from "./immutable-types"; export const IMMUTABLE_ITERABLE = "@@__IMMUTABLE_ITERABLE__@@"; export const IMMUTABLE_RECORD = "@@__IMMUTABLE_RECORD__@@"; export const IMMUTABLE_LIST = "@@__IMMUTABLE_LIST__@@"; export const IMMUTABLE_MAP = "@@__IMMUTABLE_MAP__@@"; export function isIterable(x): x is Iterable { return !!(x && x[IMMUTABLE_ITERABLE]); } export function isRecord(x): x is Record { return !!(x && x[IMMUTABLE_RECORD]); } export function isImmutable( x, ): x is Iterable | Record { return isIterable(x) || isRecord(x); } export function isList(x): x is List { return !!(x && x[IMMUTABLE_LIST]); } export function isMap(x): x is Map { return !!(x && x[IMMUTABLE_MAP]); } export function shallowToJS(x, whitelist?: Record) { return isIterable(x) ? x.reduce( (result: any, curr: any, key: any) => { let newCurr = curr; if (whitelist && whitelist[key]) { newCurr = shallowToJS(curr); } result[key] = newCurr; return result; }, isList(x) ? [] : {}, ) : x; } ================================================ FILE: packages/victory-core/src/victory-util/index.ts ================================================ export * from "./add-events"; export * from "./merge-refs"; export * as Axis from "./axis"; export * as Collection from "./collection"; export * from "./common-props"; export * as Data from "./data"; export * as DefaultTransitions from "./default-transitions"; export * as Domain from "./domain"; export * as Events from "./events"; export * as Helpers from "./helpers"; export * as Hooks from "./hooks"; export * as Immutable from "./immutable"; export * as LabelHelpers from "./label-helpers"; export * as LineHelpers from "./line-helpers"; export * as Log from "./log"; export * as PointPathHelpers from "./point-path-helpers"; export * as Scale from "./scale"; export * as Selection from "./selection"; export type { SVGCoordinateType, SVGCoordinateBounds } from "./selection"; export * as Style from "./style"; export * as TextSize from "./textsize"; export type { TextSizeStyleInterface } from "./textsize"; export { default as Timer } from "./timer"; export { default as TimerContext } from "./timer-context"; export * as Transitions from "./transitions"; export * as UserProps from "./user-props"; export * as Wrapper from "./wrapper"; ================================================ FILE: packages/victory-core/src/victory-util/label-helpers.test.tsx ================================================ /* eslint max-nested-callbacks: 0 */ import React from "react"; import * as d3Scale from "victory-vendor/d3-scale"; import * as LabelHelpers from "./label-helpers"; import { VictoryLabel } from "../victory-label/victory-label"; const scale = { x: d3Scale.scaleLinear(), y: d3Scale.scaleLinear() }; const data = [ { x: 0, y: 0 }, { x: 0.5, y: 0.5 }, ]; const labelComponent = ; const style = { labels: { fontSize: 8 } }; const basicProps = { scale, data, labelComponent, style }; describe("victory-util/label-helpers", () => { describe("getProps", () => { it("returns the correct positions given a set of props an and index", () => { data.forEach((datum, index) => { const labelProps = LabelHelpers.getProps(basicProps, index); expect(labelProps.x).toEqual(datum.x); expect(labelProps.y).toEqual(datum.y); }); }); it("returns the correct label text from a labels array", () => { const labels = ["one", "two"]; const props = Object.assign({ labels }, basicProps); data.forEach((datum, index) => { const labelProps = LabelHelpers.getProps(props, index); expect(labelProps.text).toEqual(labels[index]); }); }); it("returns the correct label text from datum", () => { const dataWithLabels = [ { x: 0, y: 0, label: "one" }, { x: 0.5, y: 0.5, label: "two" }, ]; const props = Object.assign({}, basicProps, { data: dataWithLabels }); data.forEach((datum, index) => { const labelProps = LabelHelpers.getProps(props, index); expect(labelProps.text).toEqual(dataWithLabels[index].label); }); }); it("returns the correct positions for polar labels", () => { const polarScale = { x: d3Scale.scaleLinear().range([0, Math.PI * 2]), y: d3Scale.scaleLinear(), }; data.forEach((datum, index) => { const props = Object.assign({}, basicProps, { scale: polarScale, polar: true, }); const labelProps = LabelHelpers.getProps(props, index); expect(labelProps.x).toEqual(datum.y * Math.cos(datum.x * Math.PI * 2)); // We need Math.abs here because 0 does not equal -0 :( expect(Math.abs(labelProps.y)).toEqual( Math.abs(-datum.y * Math.sin(datum.x * Math.PI * 2)), ); }); }); }); describe("getPolarAngle", () => { it("returns zero when labelPlacement is vertical", () => { const angle = LabelHelpers.getPolarAngle({ labelPlacement: "vertical" }); expect(angle).toEqual(0); }); it("returns angles corresponding to perpendicular labelPlacement", () => { expect( LabelHelpers.getPolarAngle({ labelPlacement: "perpendicular" }, 15), ).toEqual(75); expect( LabelHelpers.getPolarAngle({ labelPlacement: "perpendicular" }, 45), ).toEqual(45); expect( LabelHelpers.getPolarAngle({ labelPlacement: "perpendicular" }, 90), ).toEqual(0); expect( LabelHelpers.getPolarAngle({ labelPlacement: "perpendicular" }, 135), ).toEqual(-45); expect( LabelHelpers.getPolarAngle({ labelPlacement: "perpendicular" }, 180), ).toEqual(90); expect( LabelHelpers.getPolarAngle({ labelPlacement: "perpendicular" }, 225), ).toEqual(45); expect( LabelHelpers.getPolarAngle({ labelPlacement: "perpendicular" }, 270), ).toEqual(0); expect( LabelHelpers.getPolarAngle({ labelPlacement: "perpendicular" }, 315), ).toEqual(-45); }); it("returns angles corresponding to parallel labelPlacement", () => { expect( LabelHelpers.getPolarAngle({ labelPlacement: "parallel" }, 15), ).toEqual(-15); expect( LabelHelpers.getPolarAngle({ labelPlacement: "parallel" }, 45), ).toEqual(-45); expect( LabelHelpers.getPolarAngle({ labelPlacement: "parallel" }, 90), ).toEqual(-90); expect( LabelHelpers.getPolarAngle({ labelPlacement: "parallel" }, 135), ).toEqual(45); expect( LabelHelpers.getPolarAngle({ labelPlacement: "parallel" }, 180), ).toEqual(0); expect( LabelHelpers.getPolarAngle({ labelPlacement: "parallel" }, 225), ).toEqual(-45); expect( LabelHelpers.getPolarAngle({ labelPlacement: "parallel" }, 270), ).toEqual(-90); expect( LabelHelpers.getPolarAngle({ labelPlacement: "parallel" }, 315), ).toEqual(45); }); }); }); ================================================ FILE: packages/victory-core/src/victory-util/label-helpers.ts ================================================ import { VictoryLabelProps } from "../victory-label/victory-label"; import * as Helpers from "./helpers"; import defaults from "lodash/defaults"; // Private Functions function getVerticalAnchor(props, datum: VictoryLabelProps["datum"] = {}) { const sign = datum._y >= 0 ? 1 : -1; const labelStyle = (props.style && props.style.labels) || {}; if (datum.verticalAnchor || labelStyle.verticalAnchor) { return datum.verticalAnchor || labelStyle.verticalAnchor; } else if (!props.horizontal) { return sign >= 0 ? "end" : "start"; } return "middle"; } function getTextAnchor(props, datum: VictoryLabelProps["datum"] = {}) { const { style, horizontal } = props; const sign = datum._y >= 0 ? 1 : -1; const labelStyle = (style && style.labels) || {}; if (datum.verticalAnchor || labelStyle.verticalAnchor) { return datum.verticalAnchor || labelStyle.verticalAnchor; } else if (!horizontal) { return "middle"; } return sign >= 0 ? "start" : "end"; } function getAngle(props, datum: VictoryLabelProps["datum"] = {}) { const labelStyle = (props.style && props.style.labels) || {}; return datum.angle === undefined ? labelStyle.angle : datum.angle; } function getPadding(props, datum: VictoryLabelProps["datum"] = {}) { const { horizontal, style } = props; const labelStyle = style.labels || {}; const defaultPadding = Helpers.evaluateProp(labelStyle.padding, props) || 0; const sign = datum._y < 0 ? -1 : 1; return { x: horizontal ? sign * defaultPadding : 0, y: horizontal ? 0 : -1 * sign * defaultPadding, }; } function getOffset(props, datum) { if (props.polar) { return {}; } const padding = getPadding(props, datum); return { dx: padding.x, dy: padding.y, }; } function getPosition(props, datum) { const { polar } = props; const { x, y } = Helpers.scalePoint(props, datum); if (!polar) { return { x, y }; } const polarPadding = getPolarPadding(props, datum); return { x: x + polarPadding.x, y: y + polarPadding.y, }; } function getPolarPadding(props, datum) { const { style } = props; const degrees = getDegrees(props, datum); const labelStyle = style.labels || {}; const padding = Helpers.evaluateProp(labelStyle.padding, props) || 0; const angle = Helpers.degreesToRadians(degrees); return { x: padding * Math.cos(angle), y: -padding * Math.sin(angle), }; } function getLabelPlacement(props) { const { labelComponent, labelPlacement, polar } = props; const defaultLabelPlacement = polar ? "perpendicular" : "vertical"; return labelPlacement ? labelPlacement : (labelComponent.props && labelComponent.props.labelPlacement) || defaultLabelPlacement; } function getPolarOrientation(degrees) { // eslint-disable-next-line no-magic-numbers if (degrees < 45 || degrees > 315) { return "right"; // eslint-disable-next-line no-magic-numbers } else if (degrees >= 45 && degrees <= 135) { return "top"; // eslint-disable-next-line no-magic-numbers } else if (degrees > 135 && degrees < 225) { return "left"; } return "bottom"; } // Exported Functions export function getText(props, datum: VictoryLabelProps["datum"] = {}, index) { if (datum.label !== undefined) { return datum.label; } return Array.isArray(props.labels) ? props.labels[index] : props.labels; } export function getPolarTextAnchor(props, degrees) { const labelPlacement = getLabelPlacement(props); if ( labelPlacement === "perpendicular" || (labelPlacement === "vertical" && (degrees === 90 || degrees === 270)) ) { return "middle"; } return degrees <= 90 || degrees > 270 ? "start" : "end"; } export function getPolarVerticalAnchor(props, degrees) { const labelPlacement = getLabelPlacement(props); const orientation = getPolarOrientation(degrees); if ( labelPlacement === "parallel" || orientation === "left" || orientation === "right" ) { return "middle"; } return orientation === "top" ? "end" : "start"; } export function getPolarAngle(props, baseAngle?) { const { labelPlacement, datum } = props; if (!labelPlacement || labelPlacement === "vertical") { return 0; } const degrees = baseAngle !== undefined ? baseAngle % 360 : getDegrees(props, datum); const sign = (degrees > 90 && degrees < 180) || degrees > 270 ? 1 : -1; let angle = 0; if (degrees === 0 || degrees === 180) { angle = 90; } else if (degrees > 0 && degrees < 180) { angle = 90 - degrees; } else if (degrees > 180 && degrees < 360) { angle = 270 - degrees; } const labelRotation = labelPlacement === "perpendicular" ? 0 : 90; return angle + sign * labelRotation; } export function getDegrees(props, datum) { const { x } = Helpers.getPoint(datum); return Helpers.radiansToDegrees(props.scale.x(x)) % 360; } export function getProps(props, index) { const { scale, data, style, horizontal, polar, width, height, theme, labelComponent, disableInlineStyles, } = props; const datum = data[index]; const degrees = getDegrees(props, datum); const textAnchor = polar ? getPolarTextAnchor(props, degrees) : getTextAnchor(props, datum); const verticalAnchor = polar ? getPolarVerticalAnchor(props, degrees) : getVerticalAnchor(props, datum); const angle = getAngle(props, datum); const text = getText(props, datum, index); const labelPlacement = getLabelPlacement(props); const { x, y } = getPosition(props, datum); const { dx, dy } = getOffset(props, datum); const labelProps = { angle, data, datum, disableInlineStyles, horizontal, index, polar, scale, labelPlacement, text, textAnchor, verticalAnchor, x, y, dx, dy, width, height, style: style.labels, }; if (!Helpers.isTooltip(labelComponent)) { return labelProps; } const tooltipTheme = (theme && theme.tooltip) || {}; return defaults({}, labelProps, Helpers.omit(tooltipTheme, ["style"])); } ================================================ FILE: packages/victory-core/src/victory-util/line-helpers.ts ================================================ import * as d3Shape from "victory-vendor/d3-shape"; const defined = (d) => { const y = d._y1 !== undefined ? d._y1 : d._y; return y !== null && y !== undefined && d._y0 !== null; }; const getXAccessor = (scale) => { return (d) => scale.x(d._x1 !== undefined ? d._x1 : d._x); }; const getYAccessor = (scale) => { return (d) => scale.y(d._y1 !== undefined ? d._y1 : d._y); }; const getAngleAccessor = (scale) => { return (d) => { const x = scale.x(d._x1 !== undefined ? d._x1 : d._x); return -1 * x + Math.PI / 2; }; }; export type CurveName = | "basis" | "cardinal" | "bumpX" | "bumpY" | "bundle" | "catmullRom" | "linear" | "monotoneX" | "monotoneY" | "step" | "stepAfter" | "stepBefore" | "natural"; type ShapeMethod = keyof Pick< typeof d3Shape, | "curveBasis" | "curveCardinal" | "curveBumpX" | "curveBumpY" | "curveBundle" | "curveCatmullRom" | "curveLinear" | "curveMonotoneX" | "curveMonotoneY" | "curveStep" | "curveStepAfter" | "curveStepBefore" | "curveNatural" >; type ShapeMethodClosed = keyof Pick< typeof d3Shape, | "curveBasisClosed" | "curveCardinalClosed" | "curveLinearClosed" | "curveCatmullRomClosed" >; const toNewName = (interpolation: CurveName) => { // d3 shape changed the naming scheme for interpolators from "basis" -> "curveBasis" etc. const capitalize = (s) => s && s[0].toUpperCase() + s.slice(1); return `curve${capitalize(interpolation)}` as ShapeMethod; }; const toNewNameClosed = (interpolation: CurveName) => { return `${toNewName(interpolation)}Closed` as ShapeMethodClosed; }; export const getInterpolationFunction = (props) => { const { interpolation } = props; if (typeof interpolation === "function") { return interpolation; } if (typeof interpolation === "string") { const { polar, openCurve = !polar } = props; const interpolationName = !openCurve ? toNewNameClosed(interpolation as CurveName) : toNewName(interpolation as CurveName); return d3Shape[interpolationName]; } return d3Shape.curveLinear; }; export const getLineFunction = (props) => { const { polar, scale, horizontal } = props; return polar ? d3Shape .lineRadial() .defined(defined) .curve(getInterpolationFunction(props)) .angle(getAngleAccessor(scale)) .radius(getYAccessor(scale)) : d3Shape .line() .defined(defined) .curve(getInterpolationFunction(props)) .x(horizontal ? getYAccessor(scale) : getXAccessor(scale)) .y(horizontal ? getXAccessor(scale) : getYAccessor(scale)); }; ================================================ FILE: packages/victory-core/src/victory-util/log.ts ================================================ /* global console process */ /* eslint-disable no-console */ export function warn(message: string) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - Webpack DefinePlugin will replace process.env.NODE_ENV if (process.env.NODE_ENV !== "production") { if (console && console.warn) { console.warn(message); } } } ================================================ FILE: packages/victory-core/src/victory-util/merge-refs.ts ================================================ import React from "react"; import * as Helpers from "./helpers"; type Ref = React.MutableRefObject | React.LegacyRef | undefined | null; /** * Used to merge multiple React refs into a single callback ref. * * @example * ```tsx *
* ``` */ export function mergeRefs(refs: Ref[]): React.RefCallback { return (value) => { refs.forEach((ref) => { // If the ref is a function, it's a callback ref and we call it with the value. if (Helpers.isFunction(ref)) { ref(value); } else if (ref !== null && ref !== undefined) { // If the ref is an object (not null and not undefined), it's an object ref. // We assign the value to its 'current' property. (ref as React.MutableRefObject).current = value; } }); }; } ================================================ FILE: packages/victory-core/src/victory-util/point-path-helpers.test.ts ================================================ import * as PathHelpers from "./point-path-helpers"; describe("point-path-helpers", () => { const x = 0; const y = 0; const size = 1; describe("circle", () => { it("draws a path for a circle at the correct location", () => { const pathResult = PathHelpers.circle(x, y, size); expect(pathResult).toContain(`M ${x}, ${y}`); }); }); describe("square", () => { it("draws a path for a square at the correct location", () => { const pathResult = PathHelpers.square(x, y, size); const baseSize = 0.87 * size; expect(pathResult).toContain(`M ${x - baseSize}, ${y + baseSize}`); }); }); describe("diamond", () => { it("draws a path for a diamond at the correct location", () => { const pathResult = PathHelpers.diamond(0, 0, 1); const baseSize = 0.87 * size; const length = Math.sqrt(2 * (baseSize * baseSize)); expect(pathResult).toContain( `M ${Math.round(x)}, ${Math.round(y + length)}`, ); }); }); describe("triangleUp", () => { it("draws a path for a triangleUp at the correct location", () => { const pathResult = PathHelpers.triangleUp(0, 0, 1); expect(pathResult).toContain(`M ${x - size}, ${y + size}`); }); }); describe("triangleDown", () => { it("draws a path for a triangleDown at the correct location", () => { const pathResult = PathHelpers.triangleDown(0, 0, 1); expect(pathResult).toContain(`M ${x - size}, ${y - size}`); }); }); describe("plus", () => { it("draws a path for a plus at the correct location", () => { const pathResult = PathHelpers.plus(0, 0, 1); const baseSize = 1.1 * size; const distance = baseSize / 1.5; expect(pathResult).toContain(`M ${x - distance / 2}, ${y + baseSize}`); }); }); describe("minus", () => { it("draws a path for a minus at the correct location", () => { const pathResult = PathHelpers.minus(0, 0, 1); const baseSize = 1.1 * size; const lineHeight = baseSize - baseSize * 0.3; expect(pathResult).toContain(`M ${x - baseSize}, ${y + lineHeight / 2}`); }); }); describe("star", () => { it("draws a path for a star at the correct location", () => { const pathResult = PathHelpers.star(0, 0, 1); const angle = Math.PI / 5; const baseSize = 1.35 * size; expect(pathResult).toContain(`M ${baseSize * Math.sin(angle) + x}`); expect(pathResult).toContain(`${baseSize * Math.cos(angle) + y}`); }); }); }); ================================================ FILE: packages/victory-core/src/victory-util/point-path-helpers.ts ================================================ /* eslint no-magic-numbers: ["error", { "ignore": [0, 1, 2, 2.5, 3] }]*/ import * as Helpers from "./helpers"; export function circle(x: number, y: number, size: number) { return `M ${x}, ${y} m ${-size}, 0 a ${size}, ${size} 0 1,0 ${size * 2},0 a ${size}, ${size} 0 1,0 ${-size * 2},0`; } export function square(x: number, y: number, size: number) { const baseSize = 0.87 * size; // eslint-disable-line no-magic-numbers const x0 = x - baseSize; const y1 = y + baseSize; const distance = x + baseSize - x0; return `M ${x0}, ${y1} h${distance} v-${distance} h-${distance} z`; } export function diamond(x: number, y: number, size: number) { const baseSize = 0.87 * size; // eslint-disable-line no-magic-numbers const length = Math.sqrt(2 * (baseSize * baseSize)); return `M ${x}, ${y + length} l ${length}, -${length} l -${length}, -${length} l -${length}, ${length} l ${length}, ${length} z`; } export function triangleDown(x: number, y: number, size: number) { const height = (size / 2) * Math.sqrt(3); const x0 = x - size; const x1 = x + size; const y0 = y - size; const y1 = y + height; return `M ${x0}, ${y0} L ${x1}, ${y0} L ${x}, ${y1} z`; } export function triangleUp(x: number, y: number, size: number) { const height = (size / 2) * Math.sqrt(3); const x0 = x - size; const x1 = x + size; const y0 = y - height; const y1 = y + size; return `M ${x0}, ${y1} L ${x1}, ${y1} L ${x}, ${y0} z`; } export function plus(x: number, y: number, size: number) { const baseSize = 1.1 * size; // eslint-disable-line no-magic-numbers const distance = baseSize / 1.5; // eslint-disable-line no-magic-numbers return ` M ${x - distance / 2}, ${y + baseSize} v-${distance} h-${distance} v-${distance} h${distance} v-${distance} h${distance} v${distance} h${distance} v${distance} h-${distance} v${distance} z`; } export function cross(x: number, y: number, size: number) { const baseSize = 0.8 * size; // eslint-disable-line no-magic-numbers const distance = baseSize / 1.5; // eslint-disable-line no-magic-numbers return ` M ${x - distance / 2}, ${y + baseSize + distance} v-${distance * 2} h-${distance} v-${distance} h${distance} v-${distance} h${distance} v${distance} h${distance} v${distance} h-${distance} v${distance * 2} z`; } export function minus(x: number, y: number, size: number) { const baseSize = 1.1 * size; // eslint-disable-line no-magic-numbers const lineHeight = baseSize - baseSize * 0.3; // eslint-disable-line no-magic-numbers const x0 = x - baseSize; const y1 = y + lineHeight / 2; const distance = x + baseSize - x0; return `M ${x0}, ${y1} h${distance} v-${lineHeight} h-${distance} z`; } export function star(x: number, y: number, size: number) { const baseSize = 1.35 * size; // eslint-disable-line no-magic-numbers const angle = Math.PI / 5; // eslint-disable-line no-magic-numbers // eslint-disable-next-line no-magic-numbers const starCoords = Helpers.range(10).map((index) => { const length = index % 2 === 0 ? baseSize : baseSize / 2; return `${length * Math.sin(angle * (index + 1)) + x}, ${length * Math.cos(angle * (index + 1)) + y}`; }); return `M ${starCoords.join("L")} z`; } ================================================ FILE: packages/victory-core/src/victory-util/scale.test.ts ================================================ import * as d3Scale from "victory-vendor/d3-scale"; import * as Scale from "./scale"; describe("victory-util/scale", () => { describe("getBaseScale", () => { it("returns a scale from `getScaleFromProps` when string props are provided", () => { const props = { scale: "log" }; const baseScale = Scale.getBaseScale(props, "x"); expect(baseScale).toBeInstanceOf(Function); expect(baseScale.base).toBeInstanceOf(Function); }); it("returns a scale from `getScaleFromProps` when a d3 scale is provided", () => { const props = { scale: d3Scale.scaleLog() }; const baseScale = Scale.getBaseScale(props, "x"); expect(baseScale).toBeInstanceOf(Function); expect(baseScale.base).toBeInstanceOf(Function); }); it("returns a default scale when data is provided", () => { const props = { data: [{ x: 0, y: 1 }] }; const baseScale = Scale.getBaseScale(props, "x"); expect(baseScale).toBeInstanceOf(Function); expect(baseScale.domain).toBeInstanceOf(Function); }); it("returns a default scale when nothing is provided", () => { const baseScale = Scale.getBaseScale({}, "x"); expect(baseScale).toBeInstanceOf(Function); expect(baseScale.domain).toBeInstanceOf(Function); }); }); describe("getScaleFromProps", () => { it("returns a scale when a single scale is provided in props", () => { const props = { scale: "log" }; const propsScale = Scale.getScaleFromProps(props, "x")!; expect(propsScale).toBeInstanceOf(Function); expect(propsScale.base).toBeInstanceOf(Function); }); it("returns a scale when a scale object contains a scale for an axis", () => { const props = { scale: { x: "log" } }; const propsScale = Scale.getScaleFromProps(props, "x")!; expect(propsScale).toBeInstanceOf(Function); expect(propsScale.base).toBeInstanceOf(Function); }); it("returns undefined when a scale object does not contain a scale for an axis", () => { const props = { scale: { y: "log" } }; const propsScale = Scale.getScaleFromProps(props, "x"); expect(propsScale).toBeUndefined(); }); it("returns undefined when an invalid scale is provided in props", () => { const props = { scale: "foo" }; const propsScale = Scale.getScaleFromProps(props, "x"); expect(propsScale).toBeUndefined(); }); it("returns undefined when no scale prop is provided", () => { const propsScale = Scale.getScaleFromProps({}, "x"); expect(propsScale).toBeUndefined(); }); }); describe("getScaleType", () => { it("returns 'log' for log scales", () => { const props = { scale: { x: d3Scale.scaleLog() } }; const scaleType = Scale.getScaleType(props, "x"); expect(scaleType).toEqual("log"); }); it("returns a string value given a string prop", () => { const props = { scale: { x: "linear" } }; const scaleType = Scale.getScaleType(props, "x"); expect(scaleType).toEqual("linear"); }); it("uses data to distinguish between time and linear scales", () => { const props = { scale: { x: d3Scale.scaleLinear() } }; const scaleType = Scale.getScaleType(props, "x"); expect(scaleType).toEqual("linear"); }); it("returns 'linear' when no scale is set", () => { const props = {}; const scaleType = Scale.getScaleType(props, "x"); expect(scaleType).toEqual("linear"); }); it("returns 'time' when no scale is set, and data contains dates", () => { const props = { x: "x", y: "y", data: [{ x: new Date("2016-01-13"), y: 1 }], }; const scaleType = Scale.getScaleType(props, "x"); expect(scaleType).toEqual("time"); }); }); describe("getType", () => { it("returns undefined on unknown function type", () => { // TODO: For some reason this test file doesn't respect the eslint override for this rule. const scaleType = Scale.getType(() => {}); expect(scaleType).toBeUndefined(); }); it("returns a string value given a string prop", () => { const scaleType = Scale.getType("linear"); expect(scaleType).toEqual("linear"); }); it("returns 'log' for log scales", () => { const scaleType = Scale.getType(d3Scale.scaleLog()); expect(scaleType).toEqual("log"); }); it("matches 'quantile'", () => { const scaleType = Scale.getType(d3Scale.scaleQuantile()); expect(scaleType).toEqual("quantile"); }); it("returns undefined for scaleLinear", () => { const scaleType = Scale.getType(d3Scale.scaleLinear()); expect(scaleType).toBeUndefined(); }); }); }); ================================================ FILE: packages/victory-core/src/victory-util/scale.ts ================================================ import isPlainObject from "lodash/isPlainObject"; import * as Helpers from "./helpers"; import * as Collection from "./collection"; import * as d3Scale from "victory-vendor/d3-scale"; import { D3Scale, ScaleName } from "../types/prop-types"; const supportedScaleStrings = ["linear", "time", "log", "sqrt"] as const; type D3ScaleMethods = Pick< typeof d3Scale, "scaleLinear" | "scaleTime" | "scaleLog" | "scaleSqrt" >; // Private Functions function toNewName(scale: ScaleName): keyof D3ScaleMethods { // d3 scale changed the naming scheme for scale from "linear" -> "scaleLinear" etc. const capitalize = (s) => s && s[0].toUpperCase() + s.slice(1); return `scale${capitalize(scale)}` as keyof D3ScaleMethods; } export function validScale( scale: string | D3Scale, ): scale is ScaleName | D3Scale { if (typeof scale === "function") { return ( Helpers.isFunction(scale.copy) && Helpers.isFunction(scale.domain) && Helpers.isFunction(scale.range) ); } else if (typeof scale === "string") { return (supportedScaleStrings as ReadonlyArray).includes(scale); } return false; } function isScaleDefined(props, axis: "x" | "y") { if (!props.scale) { return false; } else if (props.scale.x || props.scale.y) { return !!props.scale[axis]; } return true; } function getScaleTypeFromProps(props, axis: "x" | "y"): string | undefined { if (!isScaleDefined(props, axis)) { return undefined; } const scale = props.scale[axis] || props.scale; return typeof scale === "string" ? scale : getType(scale); } function getScaleFromDomain(props, axis: "x" | "y"): ScaleName | undefined { let domain; if (props.domain && props.domain[axis]) { domain = props.domain[axis]; } else if (props.domain && Array.isArray(props.domain)) { domain = props.domain; } if (!domain) { return undefined; } return Collection.containsDates(domain) ? "time" : "linear"; } function getScaleTypeFromData(props, axis): ScaleName { if (!props.data) { return "linear"; } const accessor = Helpers.createAccessor(props[axis]); const axisData = props.data.map((datum) => { const processedData = isPlainObject(accessor(datum)) ? accessor(datum)[axis] : accessor(datum); return processedData !== undefined ? processedData : datum[axis]; }); return Collection.containsDates(axisData) ? "time" : "linear"; } // Exported Functions export function getScaleFromName(name: ScaleName | string): D3Scale { if (validScale(name)) { const methodName = toNewName(name as ScaleName); // @ts-expect-error scaleTime is not directly compatible with our D3Scale definition return d3Scale[methodName](); } return d3Scale.scaleLinear(); } export function getBaseScale(props, axis: "x" | "y") { const scale = getScaleFromProps(props, axis); if (scale) { return typeof scale === "string" ? getScaleFromName(scale) : scale; } const defaultScale = getScaleFromDomain(props, axis) || getScaleTypeFromData(props, axis); return getScaleFromName(defaultScale); } export function getDefaultScale() { return d3Scale.scaleLinear(); } export function getScaleFromProps(props, axis): D3Scale | undefined { if (!isScaleDefined(props, axis)) { return undefined; } const scale = props.scale[axis] || props.scale; if (validScale(scale)) { return Helpers.isFunction(scale) ? scale : getScaleFromName(scale); } return undefined; } export function getScaleType(props, axis): string { // if the scale was not given in props, it will be set to linear or time depending on data return ( getScaleTypeFromProps(props, axis) || getScaleTypeFromData(props, axis) ); } // Ordered type inference off of function fields. // **Note**: Brittle because reliant on d3 internals. const DUCK_TYPES = [ { name: "quantile", method: "quantiles" }, { name: "log", method: "base" }, // TODO(2214): Re-evaluate (1) duck typing approach, and (2) if duck typing, // do we need a different approach? (Multiple keys? Stringifying functions?) // https://github.com/FormidableLabs/victory/issues/2214 // Below are matches that don't seem to otherwise occur in Victory code base. // { name: "ordinal", method: "unknown" }, // { name: "pow-sqrt", method: "exponent" }, // { name: "quantize-threshold", method: "invertExtent" } ]; export function getType(scale) { if (typeof scale === "string") { return scale; } const scaleType = DUCK_TYPES.filter((type) => { return scale[type.method] !== undefined; })[0]; return scaleType ? scaleType.name : undefined; } ================================================ FILE: packages/victory-core/src/victory-util/selection.test.ts ================================================ import * as d3Scale from "victory-vendor/d3-scale"; import * as Selection from "./selection"; describe("helpers/selection", () => { describe("getBounds", () => { it("returns min / max bounds", () => { const x1 = 10; const x2 = 5; const y1 = 0; const y2 = 1; const scale = { x: d3Scale.scaleLinear(), y: d3Scale.scaleLinear() }; const props = { x1, x2, y1, y2, scale }; const bounds = Selection.getBounds(props); expect(bounds).toEqual({ x: [x2, x1], y: [y1, y2] }); }); }); describe("getDomainCoordinates", () => { it("returns coordinates corresponding to domain min max", () => { const scale = { x: d3Scale.scaleLinear(), y: d3Scale.scaleLinear(), }; const coords = Selection.getDomainCoordinates({ scale }); expect(coords).toEqual({ x: [0, 1], y: [0, 1] }); }); }); describe("getDataCoordinates", () => { it("returns coordinates corresponding to point x, y", () => { const scale = { x: d3Scale.scaleLinear(), y: d3Scale.scaleLinear(), }; const coords = Selection.getDataCoordinates({}, scale, 1, 1); expect(coords).toEqual({ x: 1, y: 1 }); }); it("returns polar coordinates corresponding to point x, y", () => { const scale = { x: d3Scale.scaleLinear().range([0, Math.PI * 2]), y: d3Scale.scaleLinear(), }; const x = Math.PI; const y = 0; const coords = Selection.getDataCoordinates({ polar: true }, scale, x, y); expect(coords).toEqual({ x: 0, y: Math.PI }); }); }); }); ================================================ FILE: packages/victory-core/src/victory-util/selection.ts ================================================ import React from "react"; import * as Collection from "./collection"; import { VictoryCommonProps } from "./common-props"; import type { DomainPropType, ScaleXYPropType } from "../types/prop-types"; // Private Functions function transformTarget( target: number, matrix: DOMMatrix, dimension: "x" | "y", ) { const { a, d, e, f } = matrix; return dimension === "y" ? d * target + f : a * target + e; } function getTransformationMatrix(svg: SVGGraphicsElement): DOMMatrix { return svg.getScreenCTM()!.inverse(); } interface ReactNativeTouchEvent extends Event { identifier?: number; locationX: number; locationY: number; } function isNativeTouchEvent( nativeEvent: Event | undefined, ): nativeEvent is ReactNativeTouchEvent { return !!( nativeEvent && (nativeEvent as ReactNativeTouchEvent).identifier !== undefined ); } function isReactTouchEvent(evt: React.SyntheticEvent): evt is React.TouchEvent { return ( (evt as React.TouchEvent).changedTouches && (evt as React.TouchEvent).changedTouches.length > 0 ); } // Exported Functions export function getParentSVG( evt: Pick & Partial>, ): SVGElement { if (isNativeTouchEvent(evt.nativeEvent)) { // @ts-expect-error Seems like a superfluous check. return undefined; } const getParent = (target) => { if (target.nodeName === "svg") { return target; } return target.parentNode ? getParent(target.parentNode) : target; }; return getParent(evt.target); } export function getSVGEventCoordinates( evt: React.SyntheticEvent, svg?: SVGElement, ): SVGCoordinateType { if (isNativeTouchEvent(evt.nativeEvent)) { // react-native override. relies on the RN.View being the _exact_ same size as its child SVG. // this should be fine: the svg is the only child of View and the View shirks to its children return { x: evt.nativeEvent.locationX, y: evt.nativeEvent.locationY, }; } const location = isReactTouchEvent(evt) ? evt.changedTouches[0] : (evt as React.MouseEvent); const matrix = getTransformationMatrix( (svg as SVGGraphicsElement) || getParentSVG(location), ); return { x: transformTarget(location.clientX, matrix, "x"), y: transformTarget(location.clientY, matrix, "y"), }; } export function getDomainCoordinates( props: Pick, domain?: DomainPropType, ) { const { horizontal } = props; const scale = props.scale as ScaleXYPropType; // FIXME: add support for DomainTuple: [number, number] const domainObj: any = domain || { x: scale.x.domain(), y: scale.y.domain(), }; return { x: horizontal ? [scale.y(domainObj.y[0]), scale.y(domainObj.y[1])] : [scale.x(domainObj.x[0]), scale.x(domainObj.x[1])], y: horizontal ? [scale.x(domainObj.x[0]), scale.x(domainObj.x[1])] : [scale.y(domainObj.y[0]), scale.y(domainObj.y[1])], }; } // eslint-disable-next-line max-params export function getDataCoordinates( props: any, scale: ScaleXYPropType, x: number, y: number, ): SVGCoordinateType { const { polar, horizontal } = props; if (!polar) { return { x: horizontal ? scale.x.invert(y) : scale.x.invert(x), y: horizontal ? scale.y.invert(x) : scale.y.invert(y), }; } const origin = props.origin || { x: 0, y: 0 }; const baseX = x - origin.x; const baseY = y - origin.y; const radius = Math.abs(baseX * Math.sqrt(1 + Math.pow(-baseY / baseX, 2))); const angle = (-Math.atan2(baseY, baseX) + Math.PI * 2) % (Math.PI * 2); return { x: scale.x.invert(angle), y: scale.y.invert(radius), }; } export function getBounds(props: ComputedCommonProps): SVGCoordinateBounds { const { x1, x2, y1, y2, scale } = props; const point1 = getDataCoordinates(props, scale, x1, y1); const point2 = getDataCoordinates(props, scale, x2, y2); const makeBound = (a: number, b: number) => { return [ Collection.getMinValue([a, b]) as number, Collection.getMaxValue([a, b]) as number, ] as const; }; return { x: makeBound(point1.x, point2.x), y: makeBound(point1.y, point2.y), }; } export type SVGCoordinateType = { x: number; y: number }; export type SVGCoordinateBounds = { x: readonly [number, number]; y: readonly [number, number]; }; type ComputedCommonProps = { scale: ScaleXYPropType; x1: number; x2: number; y1: number; y2: number; horizontal?: boolean; }; ================================================ FILE: packages/victory-core/src/victory-util/style.test.ts ================================================ import * as Style from "./style"; describe("toTransformString", () => { it("returns an empty string if no transform definitions are given", () => { expect(Style.toTransformString({})).toEqual(""); }); it("returns a string with two transform instructions when an object is given", () => { expect( Style.toTransformString({ rotate: [45, 0, 1], skewY: [65], }), ).toEqual("rotate(45,0,1) skewY(65)"); }); it("returns a string with two transform instructions when two objects are given", () => { expect( Style.toTransformString({ rotate: [45, 0, 1] }, { skewY: [65] }), ).toEqual("rotate(45,0,1) skewY(65)"); }); it("returns at least the subsequent transforms if the first is undefined", () => { expect(Style.toTransformString(undefined, { skewY: [65] })).toEqual( "skewY(65)", ); }); }); ================================================ FILE: packages/victory-core/src/victory-util/style.ts ================================================ import { VictoryTheme, VictoryThemeDefinition, } from "../victory-theme/victory-theme"; /** * Given an object with CSS/SVG transform definitions, return the string value * for use with the `transform` CSS property or SVG attribute. Note that we * can't always guarantee the order will match the author's intended order, so * authors should only use the object notation if they know that their transform * is commutative or that there is only one. * @param {Object} obj An object of transform definitions. * @returns {String} The generated transform string. */ export const toTransformString = function (obj, ...more) { if (more.length > 0) { return more .reduce((memo, currentObj) => { return [memo, toTransformString(currentObj)].join(" "); }, toTransformString(obj)) .trim(); } if (obj === undefined || obj === null || typeof obj === "string") { return obj; } const transforms = [] as string[]; for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = obj[key]; transforms.push(`${key}(${value})`); } } return transforms.join(" ").trim(); }; /** * Given the name of a color scale, getColorScale will return an array * of 5 hex string values in that color scale. If no 'name' parameter * is given, it will return the Victory default grayscale. * @param {String} name The name of the color scale to return (optional). * @param {Object} theme The theme object to retrieve the color scale from (optional). * @returns {Array} An array of 5 hex string values composing a color scale. */ export function getColorScale( name?: string, theme: VictoryThemeDefinition = VictoryTheme.material, ) { const { palette: { grayscale = ["#cccccc", "#969696", "#636363", "#252525"], qualitative = [], heatmap = [], warm = [], cool = [], red = [], blue = [], green = [], } = {}, } = theme; const scales: Record = { grayscale, qualitative, heatmap, warm, cool, red, blue, green, }; const selectedScale = name && scales[name]?.length ? scales[name] : scales.grayscale; return selectedScale; } ================================================ FILE: packages/victory-core/src/victory-util/textsize.test.ts ================================================ import * as TextSize from "./textsize"; const approximate = (text, style?) => TextSize._approximateTextSizeInternal.impl(text, style, true); const testString = "ABC"; describe("victory-util/textsize", () => { describe("convertLengthToPixels", () => { it("translate pixels as number of pixels", () => { expect(TextSize.convertLengthToPixels("20px")).toEqual(20); }); it("translate absolute measurement length units to particular number of pixels", () => { expect(TextSize.convertLengthToPixels("10pt")).toEqual(13.3); }); it("translate relative measurement length units to particular number of pixels", () => { expect(TextSize.convertLengthToPixels("1.5em", 16)).toEqual(24); }); }); describe("approximateWidth", () => { it("return zero width when no style", () => { expect(approximate(testString).width).toEqual(0); }); it("return correct width with signed angle", () => { expect( approximate(testString, { angle: -45, fontSize: 14, }).width.toFixed(2), ).toEqual("31.71"); }); it("return correct width with pixel fontsize", () => { expect( approximate(testString, { fontSize: "14px", }).width.toFixed(2), ).toEqual("28.74"); }); it("return appropriate width with defined fontSize", () => { expect( approximate(testString, { fontSize: 12, }).width.toFixed(2), ).toEqual("24.64"); }); it("consider font", () => { expect( approximate(testString, { fontSize: 16, }).width.toFixed(2), ).toEqual("32.85"); }); it("consider letterSpacing", () => { expect( approximate(testString, { fontSize: 12, letterSpacing: "1px", }).width.toFixed(2), ).toEqual("26.64"); }); it("consider angle", () => { expect( approximate(testString, { fontSize: 12, angle: 30, }).width.toFixed(2), ).toEqual("28.24"); }); it("not consider lineHeight without angle", () => { expect( approximate(testString, { fontSize: 12, lineHeight: 2, }).width.toFixed(2), ).toEqual("24.64"); }); it("consider lineHeight with angle", () => { expect( approximate(testString, { fontSize: 12, lineHeight: 2, angle: 30, }).width.toFixed(2), ).toEqual("35.14"); }); it("return width of widest string in text", () => { expect( approximate("ABC\nDEFGH\nIJK", { fontSize: 12, }).width.toFixed(2), ).toEqual("41.94"); }); it("returns width of widest string in array if array has an empty string", () => { expect( approximate(["06-14-20", ""], { fontSize: 12, }).width.toFixed(2), ).toEqual("47.93"); }); }); describe("approximateHeight", () => { it("return zero width when no style", () => { expect(approximate(testString).height).toEqual(0); }); it("return correct height with signed angle", () => { expect( approximate(testString, { angle: -45, fontSize: 14, }).height.toFixed(2), ).toEqual("33.29"); }); it("return correct height with pixel fontsize", () => { expect( approximate(testString, { fontSize: "14px", }).height.toFixed(2), ).toEqual("16.90"); }); it("return appropriate height with expected precision", () => { expect( approximate(testString, { fontSize: 12, }).height.toFixed(2), ).toEqual("14.49"); }); it("consider font", () => { expect( approximate(testString, { fontSize: 16, }).height.toFixed(2), ).toEqual("19.32"); }); it("consider angle", () => { expect( approximate(testString, { fontSize: 12, angle: 30, }).height.toFixed(2), ).toEqual("25.48"); }); it("not consider letterSpacing without angle", () => { expect( approximate(testString, { fontSize: 12, letterSpacing: "1px", }).height.toFixed(2), ).toEqual("14.49"); }); it("consider letterSpacing with angle", () => { expect( approximate(testString, { fontSize: 12, angle: 30, letterSpacing: "1px", }).height.toFixed(2), ).toEqual("26.53"); }); it("consider lineHeight", () => { expect( approximate(testString, { fontSize: 12, lineHeight: 2, }).height.toFixed(2), ).toEqual("28.98"); }); it("consider multiLines text", () => { expect( approximate(`ABC\n${"DBCDEFG"}\n123`, { fontSize: 12, }).height.toFixed(2), ).toEqual("43.47"); }); }); }); ================================================ FILE: packages/victory-core/src/victory-util/textsize.ts ================================================ // http://www.pearsonified.com/2012/01/characters-per-line.php /* eslint-disable no-magic-numbers */ import defaults from "lodash/defaults"; import memoize from "lodash/memoize"; // Based on measuring specific character widths // as in the following example https://bl.ocks.org/tophtucker/62f93a4658387bb61e4510c37e2e97cf // For new fonts: pull gist, open index.html locally, add font files (if not generic), enter font name in `font-family` input // prettier-ignore const fonts = { "American Typewriter": { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.25,0.4203125,0.3296875,0.6,0.6375,0.8015625,0.8203125,0.1875,0.45625,0.45625,0.6375,0.5,0.2734375,0.309375,0.2734375,0.4390625,0.6375,0.6375,0.6375,0.6375,0.6375,0.6375,0.6375,0.6375,0.6375,0.6375,0.2734375,0.2734375,0.5,0.5,0.5,0.6,0.6921875,0.7640625,0.6921875,0.6375,0.728125,0.6734375,0.6203125,0.7109375,0.784375,0.3828125,0.6421875,0.7859375,0.6375,0.9484375,0.7640625,0.65625,0.6375,0.65625,0.7296875,0.6203125,0.6375,0.7109375,0.740625,0.940625,0.784375,0.7578125,0.6203125,0.4375,0.5,0.4375,0.5,0.5,0.4921875,0.5734375,0.5890625,0.5109375,0.6,0.528125,0.43125,0.5578125,0.6375,0.3109375,0.40625,0.6234375,0.309375,0.928125,0.6375,0.546875,0.6,0.58125,0.4921875,0.4921875,0.4,0.6203125,0.625,0.825,0.6375,0.640625,0.528125,0.5,0.5,0.5,0.6671875], avg: 0.5793421052631578 }, Arial: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.278125,0.278125,0.35625,0.55625,0.55625,0.890625,0.6671875,0.1921875,0.334375,0.334375,0.390625,0.584375,0.278125,0.334375,0.278125,0.278125,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.278125,0.278125,0.584375,0.584375,0.584375,0.55625,1.015625,0.6703125,0.6671875,0.7234375,0.7234375,0.6671875,0.6109375,0.778125,0.7234375,0.278125,0.5,0.6671875,0.55625,0.834375,0.7234375,0.778125,0.6671875,0.778125,0.7234375,0.6671875,0.6109375,0.7234375,0.6671875,0.9453125,0.6671875,0.6671875,0.6109375,0.278125,0.278125,0.278125,0.4703125,0.584375,0.334375,0.55625,0.55625,0.5,0.55625,0.55625,0.3125,0.55625,0.55625,0.2234375,0.2703125,0.5,0.2234375,0.834375,0.55625,0.55625,0.55625,0.55625,0.346875,0.5,0.278125,0.55625,0.5,0.7234375,0.5,0.5,0.5,0.334375,0.2609375,0.334375,0.584375], avg: 0.528733552631579 }, "Arial Black": { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.33125,0.334375,0.5,0.6609375,0.6671875,1,0.890625,0.278125,0.390625,0.390625,0.55625,0.6609375,0.334375,0.334375,0.334375,0.28125,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.334375,0.334375,0.6609375,0.6609375,0.6609375,0.6109375,0.7453125,0.78125,0.778125,0.778125,0.778125,0.7234375,0.6671875,0.834375,0.834375,0.390625,0.6671875,0.834375,0.6671875,0.9453125,0.834375,0.834375,0.7234375,0.834375,0.78125,0.7234375,0.7234375,0.834375,0.7796875,1.003125,0.78125,0.78125,0.7234375,0.390625,0.28125,0.390625,0.6609375,0.5125,0.334375,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.41875,0.6671875,0.6671875,0.334375,0.384375,0.6671875,0.334375,1,0.6671875,0.6671875,0.6671875,0.6671875,0.4703125,0.6109375,0.4453125,0.6671875,0.6140625,0.946875,0.6671875,0.615625,0.55625,0.390625,0.278125,0.390625,0.6609375], avg: 0.6213157894736842 }, Baskerville: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.25,0.25,0.40625,0.6671875,0.490625,0.875,0.7015625,0.178125,0.2453125,0.246875,0.4171875,0.6671875,0.25,0.3125,0.25,0.521875,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.25,0.25,0.6671875,0.6671875,0.6671875,0.396875,0.9171875,0.684375,0.615625,0.71875,0.7609375,0.625,0.553125,0.771875,0.803125,0.3546875,0.515625,0.78125,0.6046875,0.928125,0.75,0.8234375,0.5625,0.96875,0.7296875,0.5421875,0.6984375,0.771875,0.7296875,0.9484375,0.771875,0.678125,0.6359375,0.3640625,0.521875,0.3640625,0.46875,0.5125,0.334375,0.46875,0.521875,0.428125,0.521875,0.4375,0.3890625,0.4765625,0.53125,0.25,0.359375,0.4640625,0.240625,0.803125,0.53125,0.5,0.521875,0.521875,0.365625,0.334375,0.2921875,0.521875,0.4640625,0.678125,0.4796875,0.465625,0.428125,0.4796875,0.5109375,0.4796875,0.6671875], avg: 0.5323519736842108 }, Courier: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.5984375,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6078125,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.61875,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.615625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6140625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625], avg: 0.6020559210526316 }, "Courier New": { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.5984375,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625], avg: 0.6015296052631579 }, cursive: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1921875,0.24375,0.40625,0.5671875,0.3984375,0.721875,0.909375,0.2328125,0.434375,0.365625,0.4734375,0.5578125,0.19375,0.3484375,0.19375,0.7734375,0.503125,0.4171875,0.5453125,0.45,0.6046875,0.4703125,0.5984375,0.55625,0.503125,0.5546875,0.20625,0.2,0.5625,0.5546875,0.546875,0.403125,0.70625,0.734375,0.7078125,0.64375,0.85,0.753125,0.75,0.6484375,1.0765625,0.44375,0.5359375,0.8359375,0.653125,1.0109375,1.1515625,0.6796875,0.6984375,1.0625,0.8234375,0.5125,0.9234375,0.8546875,0.70625,0.9109375,0.7421875,0.715625,0.6015625,0.4640625,0.3359375,0.4109375,0.5421875,0.5421875,0.4328125,0.5125,0.5,0.3859375,0.7375,0.359375,0.75625,0.540625,0.5328125,0.3203125,0.5296875,0.5015625,0.484375,0.7890625,0.5640625,0.4203125,0.703125,0.471875,0.4734375,0.35,0.4125,0.5640625,0.471875,0.6484375,0.5296875,0.575,0.4140625,0.415625,0.20625,0.3796875,0.5421875], avg: 0.5604440789473684 }, fantasy: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.215625,0.2625,0.3265625,0.6109375,0.534375,0.7625,0.7828125,0.2,0.4359375,0.4359375,0.3765625,0.5109375,0.2796875,0.4609375,0.2796875,0.5296875,0.6640625,0.253125,0.521875,0.4765625,0.6640625,0.490625,0.528125,0.5546875,0.496875,0.5421875,0.2796875,0.2796875,0.5625,0.4609375,0.5625,0.4828125,0.609375,0.740625,0.7234375,0.740625,0.8265625,0.7234375,0.6171875,0.7359375,0.765625,0.240625,0.5453125,0.715625,0.6078125,0.8640625,0.653125,0.9125,0.6484375,0.946875,0.6921875,0.653125,0.6953125,0.8015625,0.58125,0.784375,0.671875,0.6265625,0.690625,0.4359375,0.5296875,0.4359375,0.53125,0.5,0.2875,0.5375,0.603125,0.4984375,0.60625,0.53125,0.434375,0.6421875,0.56875,0.209375,0.4671875,0.5484375,0.2203125,0.709375,0.55,0.5984375,0.6140625,0.5765625,0.40625,0.4734375,0.3734375,0.559375,0.4421875,0.6421875,0.4890625,0.578125,0.4484375,0.2546875,0.2203125,0.2546875,0.55], avg: 0.536496710526316 }, Geneva: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.3328125,0.3046875,0.5,0.6671875,0.6671875,0.90625,0.728125,0.3046875,0.446875,0.446875,0.5078125,0.6671875,0.3046875,0.3796875,0.3046875,0.5390625,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.6671875,0.3046875,0.3046875,0.6671875,0.6671875,0.6671875,0.56875,0.871875,0.728125,0.6375,0.6515625,0.7015625,0.5765625,0.5546875,0.675,0.690625,0.2421875,0.4921875,0.6640625,0.584375,0.7890625,0.709375,0.7359375,0.584375,0.78125,0.60625,0.60625,0.640625,0.6671875,0.728125,0.946875,0.6109375,0.6109375,0.5765625,0.446875,0.5390625,0.446875,0.6671875,0.6671875,0.5921875,0.5546875,0.6109375,0.546875,0.603125,0.5765625,0.390625,0.6109375,0.584375,0.2359375,0.334375,0.5390625,0.2359375,0.8953125,0.584375,0.60625,0.603125,0.603125,0.3875,0.509375,0.44375,0.584375,0.565625,0.78125,0.53125,0.571875,0.5546875,0.4515625,0.246875,0.4515625,0.6671875], avg: 0.5762664473684211 }, Georgia: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2421875,0.33125,0.4125,0.64375,0.6109375,0.81875,0.7109375,0.215625,0.375,0.375,0.4734375,0.64375,0.2703125,0.375,0.2703125,0.46875,0.6140625,0.4296875,0.559375,0.553125,0.565625,0.5296875,0.5671875,0.503125,0.596875,0.5671875,0.3125,0.3125,0.64375,0.64375,0.64375,0.4796875,0.9296875,0.715625,0.6546875,0.6421875,0.75,0.6546875,0.6,0.7265625,0.815625,0.390625,0.51875,0.7203125,0.6046875,0.928125,0.7671875,0.7453125,0.6109375,0.7453125,0.7234375,0.5625,0.61875,0.7578125,0.70625,0.99375,0.7125,0.6640625,0.6015625,0.375,0.46875,0.375,0.64375,0.65,0.5,0.5046875,0.56875,0.4546875,0.575,0.484375,0.39375,0.509375,0.5828125,0.29375,0.3671875,0.546875,0.2875,0.88125,0.5921875,0.5390625,0.571875,0.5640625,0.4109375,0.4328125,0.3453125,0.5765625,0.5203125,0.75625,0.50625,0.5171875,0.4453125,0.43125,0.375,0.43125,0.64375], avg: 0.5551809210526316 }, "Gill Sans": { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2765625,0.271875,0.3546875,0.584375,0.5421875,0.6765625,0.625,0.1890625,0.3234375,0.3234375,0.4171875,0.584375,0.2203125,0.3234375,0.2203125,0.28125,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.2203125,0.2296875,0.584375,0.584375,0.584375,0.334375,1.0109375,0.6671875,0.5640625,0.709375,0.75,0.5,0.4703125,0.740625,0.7296875,0.25,0.3125,0.65625,0.490625,0.78125,0.78125,0.8234375,0.5109375,0.8234375,0.6046875,0.459375,0.6046875,0.709375,0.6046875,1.0421875,0.709375,0.6046875,0.646875,0.334375,0.28125,0.334375,0.4703125,0.5828125,0.334375,0.428125,0.5,0.4390625,0.5109375,0.4796875,0.296875,0.428125,0.5,0.2203125,0.2265625,0.5,0.2203125,0.771875,0.5,0.553125,0.5,0.5,0.3984375,0.3859375,0.334375,0.5,0.4390625,0.7203125,0.5,0.4390625,0.4171875,0.334375,0.2609375,0.334375,0.584375], avg: 0.4933717105263159 }, Helvetica: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2796875,0.2765625,0.3546875,0.5546875,0.5546875,0.8890625,0.665625,0.190625,0.3328125,0.3328125,0.3890625,0.5828125,0.2765625,0.3328125,0.2765625,0.3015625,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.5546875,0.2765625,0.2765625,0.584375,0.5828125,0.584375,0.5546875,1.0140625,0.665625,0.665625,0.721875,0.721875,0.665625,0.609375,0.7765625,0.721875,0.2765625,0.5,0.665625,0.5546875,0.8328125,0.721875,0.7765625,0.665625,0.7765625,0.721875,0.665625,0.609375,0.721875,0.665625,0.94375,0.665625,0.665625,0.609375,0.2765625,0.3546875,0.2765625,0.4765625,0.5546875,0.3328125,0.5546875,0.5546875,0.5,0.5546875,0.5546875,0.2765625,0.5546875,0.5546875,0.221875,0.240625,0.5,0.221875,0.8328125,0.5546875,0.5546875,0.5546875,0.5546875,0.3328125,0.5,0.2765625,0.5546875,0.5,0.721875,0.5,0.5,0.5,0.3546875,0.259375,0.353125,0.5890625], avg: 0.5279276315789471 }, "Helvetica Neue": { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.278125,0.259375,0.4265625,0.55625,0.55625,1,0.6453125,0.278125,0.2703125,0.26875,0.353125,0.6,0.278125,0.3890625,0.278125,0.36875,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.278125,0.278125,0.6,0.6,0.6,0.55625,0.8,0.6625,0.6859375,0.7234375,0.7046875,0.6125,0.575,0.759375,0.7234375,0.259375,0.5203125,0.6703125,0.55625,0.871875,0.7234375,0.7609375,0.6484375,0.7609375,0.6859375,0.6484375,0.575,0.7234375,0.6140625,0.9265625,0.6125,0.6484375,0.6125,0.259375,0.36875,0.259375,0.6,0.5,0.25625,0.5375,0.59375,0.5375,0.59375,0.5375,0.2984375,0.575,0.55625,0.2234375,0.2375,0.5203125,0.2234375,0.853125,0.55625,0.575,0.59375,0.59375,0.334375,0.5,0.315625,0.55625,0.5,0.759375,0.51875,0.5,0.48125,0.334375,0.2234375,0.334375,0.6], avg: 0.5279440789473684 }, "Hoefler Text": { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2359375,0.2234375,0.3921875,0.7125,0.49375,0.8859375,0.771875,0.2125,0.3078125,0.309375,0.375,0.4234375,0.234375,0.3125,0.234375,0.3,0.5828125,0.365625,0.434375,0.3921875,0.5234375,0.3984375,0.5125,0.4328125,0.46875,0.5125,0.234375,0.234375,0.515625,0.4234375,0.515625,0.340625,0.7609375,0.7359375,0.6359375,0.721875,0.8125,0.6375,0.5875,0.8078125,0.853125,0.4296875,0.503125,0.78125,0.609375,0.9609375,0.8515625,0.8140625,0.6125,0.8140625,0.71875,0.49375,0.7125,0.76875,0.771875,1.125,0.7765625,0.7734375,0.65625,0.321875,0.3078125,0.321875,0.3546875,0.5,0.3375,0.446875,0.5359375,0.45,0.5296875,0.4546875,0.425,0.4921875,0.54375,0.2671875,0.240625,0.5390625,0.25,0.815625,0.5375,0.5234375,0.5390625,0.5421875,0.365625,0.36875,0.35625,0.5171875,0.5015625,0.75,0.5,0.509375,0.44375,0.2421875,0.14375,0.2421875,0.35], avg: 0.5116447368421051 }, "Inter": { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2796875,0.2890625,0.4671875,0.634375,0.6421875,0.9828125,0.6453125,0.3,0.365625,0.365625,0.5015625,0.6625,0.2890625,0.4609375,0.2890625,0.3609375,0.63125,0.4078125,0.6109375,0.61875,0.646875,0.59375,0.6203125,0.5671875,0.61875,0.6203125,0.2890625,0.303125,0.6625,0.6625,0.6625,0.5125,0.9671875,0.690625,0.6546875,0.73125,0.721875,0.6015625,0.590625,0.746875,0.74375,0.26875,0.571875,0.671875,0.565625,0.9046875,0.7546875,0.765625,0.6390625,0.765625,0.64375,0.6421875,0.646875,0.7453125,0.690625,0.9859375,0.6828125,0.6796875,0.6296875,0.365625,0.3609375,0.365625,0.471875,0.45625,0.3234375,0.5625,0.6125,0.571875,0.6125,0.584375,0.3703125,0.6140625,0.5921875,0.2421875,0.2548828125,0.55,0.2421875,0.8765625,0.5921875,0.6,0.6125,0.6125,0.3765625,0.528125,0.328125,0.5921875,0.5625,0.81875,0.546875,0.5625,0.553125,0.4265625,0.3328125,0.4265625,0.6625], avg: 0.5624362664473683 }, "Montserrat": { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2625,0.2609375,0.3734375,0.696875,0.615625,0.8296875,0.6703125,0.203125,0.3296875,0.3296875,0.3875,0.575,0.2125,0.3828125,0.2125,0.3953125,0.6625,0.3625,0.56875,0.5640625,0.6625,0.5671875,0.609375,0.5890625,0.6390625,0.609375,0.2125,0.2125,0.575,0.575,0.575,0.5671875,1.034375,0.7171875,0.7546875,0.7203125,0.8265625,0.6703125,0.634375,0.7734375,0.8140625,0.303125,0.5078125,0.7125,0.5890625,0.95625,0.8140625,0.8390625,0.71875,0.8390625,0.7234375,0.615625,0.575,0.7921875,0.6984375,1.1125,0.65625,0.6359375,0.6515625,0.31875,0.396875,0.31875,0.5765625,0.5,0.6,0.590625,0.678125,0.5640625,0.678125,0.6046875,0.375,0.6875,0.678125,0.2703125,0.365625,0.6015625,0.2703125,1.0625,0.678125,0.628125,0.678125,0.678125,0.4015625,0.4890625,0.40625,0.6734375,0.5421875,0.8796875,0.534375,0.5671875,0.5125,0.334375,0.2953125,0.334375,0.575], avg: 0.571792763157895 }, monospace: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.5984375,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6078125,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.61875,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.615625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6140625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625,0.6015625], avg: 0.6020559210526316 }, Overpass: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2296875,0.2765625,0.4203125,0.68125,0.584375,0.8515625,0.7015625,0.2203125,0.3453125,0.3453125,0.53125,0.63125,0.2234375,0.3953125,0.2234375,0.509375,0.65,0.4046875,0.6171875,0.60625,0.6484375,0.60625,0.6015625,0.5375,0.615625,0.6015625,0.2234375,0.2234375,0.63125,0.63125,0.63125,0.5015625,0.8203125,0.696875,0.6671875,0.65,0.6859375,0.6015625,0.559375,0.690625,0.7078125,0.2953125,0.565625,0.678125,0.58125,0.8046875,0.7109375,0.740625,0.6421875,0.740625,0.6765625,0.6046875,0.590625,0.696875,0.6640625,0.853125,0.65,0.6671875,0.6625,0.3734375,0.509375,0.3734375,0.63125,0.5125,0.4,0.5328125,0.5625,0.51875,0.5625,0.546875,0.3359375,0.5625,0.565625,0.25625,0.3203125,0.55,0.265625,0.85,0.565625,0.5671875,0.5625,0.5625,0.4046875,0.4765625,0.3796875,0.565625,0.521875,0.7265625,0.53125,0.5390625,0.5125,0.3671875,0.275,0.3671875,0.63125], avg: 0.5430756578947369 }, Palatino: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.25,0.278125,0.371875,0.60625,0.5,0.840625,0.778125,0.209375,0.334375,0.334375,0.390625,0.60625,0.2578125,0.334375,0.25,0.60625,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.25,0.25,0.60625,0.60625,0.60625,0.4453125,0.7484375,0.778125,0.6109375,0.709375,0.775,0.6109375,0.55625,0.7640625,0.8328125,0.3375,0.346875,0.7265625,0.6109375,0.946875,0.83125,0.7875,0.6046875,0.7875,0.66875,0.525,0.6140625,0.778125,0.7234375,1,0.6671875,0.6671875,0.6671875,0.334375,0.60625,0.334375,0.60625,0.5,0.334375,0.5,0.565625,0.4453125,0.6109375,0.4796875,0.340625,0.55625,0.5828125,0.2921875,0.2671875,0.5640625,0.2921875,0.8828125,0.5828125,0.546875,0.6015625,0.5609375,0.3953125,0.425,0.3265625,0.603125,0.565625,0.834375,0.5171875,0.55625,0.5,0.334375,0.60625,0.334375,0.60625], avg: 0.5408552631578947 }, "RedHatText": { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2328125,0.2203125,0.35625,0.6890625,0.55,0.7390625,0.6703125,0.2140625,0.4015625,0.4015625,0.4546875,0.53125,0.2203125,0.45625,0.2203125,0.515625,0.6609375,0.3078125,0.5484375,0.5875,0.61875,0.5703125,0.6203125,0.559375,0.6140625,0.6203125,0.2203125,0.2234375,0.465625,0.534375,0.465625,0.5125,0.7671875,0.6609375,0.6703125,0.7265625,0.728125,0.6203125,0.6109375,0.8,0.73125,0.253125,0.6,0.6125,0.6078125,0.8625,0.7390625,0.8109375,0.6546875,0.809375,0.6484375,0.6234375,0.6171875,0.7125,0.6609375,0.8984375,0.6546875,0.646875,0.60625,0.3625,0.5203125,0.3625,0.540625,0.4609375,0.5234375,0.5265625,0.584375,0.509375,0.5828125,0.5578125,0.3703125,0.5828125,0.553125,0.2234375,0.24375,0.4890625,0.2234375,0.8453125,0.553125,0.58125,0.584375,0.5828125,0.353125,0.453125,0.378125,0.553125,0.5015625,0.6984375,0.4875,0.4984375,0.459375,0.3953125,0.2921875,0.3953125,0.58125], avg: 0.5341940789473685 }, "sans-serif": { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.278125,0.278125,0.35625,0.55625,0.55625,0.890625,0.6671875,0.1921875,0.334375,0.334375,0.390625,0.584375,0.278125,0.334375,0.278125,0.303125,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.55625,0.278125,0.278125,0.5859375,0.584375,0.5859375,0.55625,1.015625,0.6671875,0.6671875,0.7234375,0.7234375,0.6671875,0.6109375,0.778125,0.7234375,0.278125,0.5,0.6671875,0.55625,0.834375,0.7234375,0.778125,0.6671875,0.778125,0.7234375,0.6671875,0.6109375,0.7234375,0.6671875,0.9453125,0.6671875,0.6671875,0.6109375,0.278125,0.35625,0.278125,0.478125,0.55625,0.334375,0.55625,0.55625,0.5,0.55625,0.55625,0.278125,0.55625,0.55625,0.2234375,0.2421875,0.5,0.2234375,0.834375,0.55625,0.55625,0.55625,0.55625,0.334375,0.5,0.278125,0.55625,0.5,0.7234375,0.5,0.5,0.5,0.35625,0.2609375,0.3546875,0.590625], avg: 0.5293256578947368 }, Seravek: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.215625,0.296875,0.4171875,0.6734375,0.4953125,0.9125,0.740625,0.2421875,0.3375,0.3375,0.409375,0.60625,0.2609375,0.35625,0.25625,0.41875,0.5921875,0.3515625,0.475,0.4875,0.5375,0.509375,0.5484375,0.4546875,0.5421875,0.5484375,0.25625,0.2546875,0.5875,0.6171875,0.5875,0.4578125,0.8140625,0.6765625,0.5703125,0.6109375,0.684375,0.5109375,0.4953125,0.678125,0.6859375,0.2625,0.2625,0.5859375,0.4734375,0.846875,0.709375,0.740625,0.509375,0.740625,0.584375,0.5015625,0.528125,0.675,0.5953125,0.9453125,0.596875,0.540625,0.540625,0.359375,0.4203125,0.359375,0.5109375,0.421875,0.4046875,0.5015625,0.5421875,0.446875,0.5453125,0.484375,0.38125,0.5140625,0.5546875,0.240625,0.2640625,0.490625,0.2765625,0.8625,0.5546875,0.546875,0.5453125,0.5453125,0.3625,0.41875,0.3890625,0.5453125,0.4703125,0.7546875,0.4921875,0.4609375,0.453125,0.4015625,0.2640625,0.4015625,0.58125], avg: 0.5044078947368421 }, serif: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2484375,0.334375,0.409375,0.5,0.5,0.834375,0.778125,0.18125,0.334375,0.334375,0.5,0.5640625,0.25,0.334375,0.25,0.278125,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.278125,0.278125,0.5640625,0.5640625,0.5640625,0.4453125,0.921875,0.7234375,0.6671875,0.6671875,0.7234375,0.6109375,0.55625,0.7234375,0.7234375,0.334375,0.390625,0.7234375,0.6109375,0.890625,0.7234375,0.7234375,0.55625,0.7234375,0.6671875,0.55625,0.6109375,0.7234375,0.7234375,0.9453125,0.7234375,0.7234375,0.6109375,0.334375,0.340625,0.334375,0.4703125,0.5,0.3453125,0.4453125,0.5,0.4453125,0.5,0.4453125,0.3828125,0.5,0.5,0.278125,0.3359375,0.5,0.278125,0.778125,0.5,0.5,0.5,0.5,0.3375,0.390625,0.2796875,0.5,0.5,0.7234375,0.5,0.5,0.4453125,0.48125,0.2015625,0.48125,0.5421875], avg: 0.5126315789473684 }, Tahoma: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.3109375,0.3328125,0.4015625,0.728125,0.546875,0.9765625,0.70625,0.2109375,0.3828125,0.3828125,0.546875,0.728125,0.303125,0.3640625,0.303125,0.3953125,0.546875,0.546875,0.546875,0.546875,0.546875,0.546875,0.546875,0.546875,0.546875,0.546875,0.3546875,0.3546875,0.728125,0.728125,0.728125,0.475,0.909375,0.6109375,0.590625,0.6015625,0.6796875,0.5625,0.521875,0.66875,0.6765625,0.3734375,0.4171875,0.6046875,0.4984375,0.771875,0.66875,0.7078125,0.5515625,0.7078125,0.6375,0.5578125,0.5875,0.65625,0.60625,0.903125,0.58125,0.5890625,0.559375,0.3828125,0.39375,0.3828125,0.728125,0.5625,0.546875,0.525,0.553125,0.4625,0.553125,0.5265625,0.3546875,0.553125,0.5578125,0.2296875,0.328125,0.51875,0.2296875,0.840625,0.5578125,0.54375,0.553125,0.553125,0.3609375,0.446875,0.3359375,0.5578125,0.4984375,0.7421875,0.4953125,0.4984375,0.4453125,0.48125,0.3828125,0.48125,0.728125], avg: 0.5384374999999998 }, "Times New Roman": { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2484375,0.334375,0.409375,0.5,0.5,0.834375,0.778125,0.18125,0.334375,0.334375,0.5,0.5640625,0.25,0.334375,0.25,0.28125,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.278125,0.278125,0.5640625,0.5640625,0.5640625,0.4453125,0.921875,0.7234375,0.6671875,0.6671875,0.7234375,0.6109375,0.55625,0.7234375,0.7234375,0.334375,0.390625,0.73125,0.6109375,0.890625,0.7375,0.7234375,0.55625,0.7234375,0.6765625,0.55625,0.6109375,0.7234375,0.7234375,0.9453125,0.7234375,0.7234375,0.6109375,0.334375,0.28125,0.334375,0.4703125,0.51875,0.334375,0.4453125,0.503125,0.4453125,0.503125,0.4453125,0.4359375,0.5,0.5,0.278125,0.35625,0.50625,0.278125,0.778125,0.5,0.5,0.5046875,0.5,0.340625,0.390625,0.2796875,0.5,0.5,0.7234375,0.5,0.5,0.4453125,0.48125,0.2015625,0.48125,0.5421875], avg: 0.5134375 }, "Trebuchet MS": { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.3015625,0.3671875,0.325,0.53125,0.525,0.6015625,0.70625,0.1609375,0.3671875,0.3671875,0.3671875,0.525,0.3671875,0.3671875,0.3671875,0.525,0.525,0.525,0.525,0.525,0.525,0.525,0.525,0.525,0.525,0.525,0.3671875,0.3671875,0.525,0.525,0.525,0.3671875,0.771875,0.590625,0.5671875,0.5984375,0.6140625,0.5359375,0.525,0.6765625,0.6546875,0.2796875,0.4765625,0.5765625,0.5078125,0.7109375,0.6390625,0.675,0.5578125,0.7421875,0.5828125,0.48125,0.58125,0.6484375,0.5875,0.853125,0.5578125,0.5703125,0.5515625,0.3671875,0.3578125,0.3671875,0.525,0.53125,0.525,0.5265625,0.5578125,0.4953125,0.5578125,0.546875,0.375,0.503125,0.546875,0.2859375,0.3671875,0.5046875,0.2953125,0.83125,0.546875,0.5375,0.5578125,0.5578125,0.3890625,0.40625,0.396875,0.546875,0.490625,0.7453125,0.5015625,0.49375,0.475,0.3671875,0.525,0.3671875,0.525], avg: 0.5085197368421052 }, Verdana: { widths: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.35,0.39375,0.459375,0.81875,0.6359375,1.0765625,0.759375,0.26875,0.4546875,0.4546875,0.6359375,0.81875,0.3640625,0.4546875,0.3640625,0.4703125,0.6359375,0.6359375,0.6359375,0.6359375,0.6359375,0.6359375,0.6359375,0.6359375,0.6359375,0.6359375,0.4546875,0.4546875,0.81875,0.81875,0.81875,0.546875,1,0.684375,0.6859375,0.6984375,0.771875,0.6328125,0.575,0.7765625,0.7515625,0.421875,0.4546875,0.69375,0.5578125,0.84375,0.7484375,0.7875,0.603125,0.7875,0.7,0.684375,0.6171875,0.7328125,0.684375,0.9890625,0.6859375,0.615625,0.6859375,0.4546875,0.46875,0.4546875,0.81875,0.6421875,0.6359375,0.6015625,0.6234375,0.521875,0.6234375,0.596875,0.384375,0.6234375,0.6328125,0.275,0.3765625,0.5921875,0.275,0.9734375,0.6328125,0.6078125,0.6234375,0.6234375,0.43125,0.521875,0.3953125,0.6328125,0.5921875,0.81875,0.5921875,0.5921875,0.5265625,0.6359375,0.4546875,0.6359375,0.81875], avg: 0.6171875000000003 } } // https://developer.mozilla.org/en/docs/Web/CSS/length // Absolute sizes in pixels for obsolete measurement units. const absoluteMeasurementUnitsToPixels = { mm: 3.8, sm: 38, pt: 1.33, pc: 16, in: 96, px: 1, }; const relativeMeasurementUnitsCoef = { em: 1, ex: 0.5, }; const coefficients = { heightOverlapCoef: 1.05, // Coefficient for height value to prevent overlap. lineCapitalCoef: 1.15, // Coefficient for height value. Reserve space for capital chars. }; const defaultStyle = { lineHeight: 1, letterSpacing: "0px", fontSize: 0, angle: 0, fontFamily: "", }; const _degreeToRadian = (angle) => (angle * Math.PI) / 180; const _getFontData = (fontFamily) => { const possibleFonts = fontFamily.split(",").map((f) => f.replace(/'|"/g, "")); const fontMatch = possibleFonts.find((f) => fonts[f]) || "Helvetica"; return fonts[fontMatch]; }; const _splitToLines = (text: string | string[]) => { return Array.isArray(text) ? text : text.toString().split(/\r\n|\r|\n/g); }; const _getSizeWithRotate = (axisSize, dependentSize, angle) => { const angleInRadian = _degreeToRadian(angle); return ( Math.abs(Math.cos(angleInRadian) * axisSize) + Math.abs(Math.sin(angleInRadian) * dependentSize) ); }; /** * Convert length-type parameters from specific measurement units to pixels * @param {string} length Css length string value. * @param {number} fontSize Current text font-size. * @returns {number} Approximate Css length in pixels. */ export const convertLengthToPixels = ( length: string, fontSize?: number, ): number => { const attribute = length.match(/[a-zA-Z%]+/)?.[0]; const value = Number(length.match(/[0-9.,]+/)); let result; if (!attribute) { result = value || 0; } else if (absoluteMeasurementUnitsToPixels.hasOwnProperty(attribute)) { result = value * absoluteMeasurementUnitsToPixels[attribute]; } else if (relativeMeasurementUnitsCoef.hasOwnProperty(attribute)) { result = (fontSize ? value * fontSize : value * defaultStyle.fontSize) * relativeMeasurementUnitsCoef[attribute]; } else { result = value; } return result; }; const _prepareParams = (inputStyle, index) => { const lineStyle = Array.isArray(inputStyle) ? inputStyle[index] : inputStyle; const style = defaults({}, lineStyle, defaultStyle); return Object.assign({}, style, { fontFamily: style.fontFamily, letterSpacing: typeof style.letterSpacing === "number" ? style.letterSpacing : convertLengthToPixels(String(style.letterSpacing), style.fontSize), fontSize: typeof style.fontSize === "number" ? style.fontSize : convertLengthToPixels(String(style.fontSize)), }); }; const _approximateTextWidthInternal = (text: string | string[], style) => { if (text === undefined || text === "" || text === null) { return 0; } const widths = _splitToLines(text).map((line, index) => { const len = line.toString().length; const { fontSize, letterSpacing, fontFamily } = _prepareParams( style, index, ); const fontData = _getFontData(fontFamily); const width = line .toString() .split("") .map((c) => { return c.charCodeAt(0) < fontData.widths.length ? fontData.widths[c.charCodeAt(0)] : fontData.avg; }) .reduce((cur, acc) => acc + cur, 0) * fontSize; return width + letterSpacing * Math.max(len - 1, 0); }); return Math.max(...widths); }; const _approximateTextHeightInternal = (text: string | string[], style) => { if (text === undefined || text === "" || text === null) { return 0; } return _splitToLines(text).reduce((total, line, index) => { const lineStyle = _prepareParams(style, index); const containsCaps = line.toString().match(/[(A-Z)(0-9)]/); const height = containsCaps ? lineStyle.fontSize * coefficients.lineCapitalCoef : lineStyle.fontSize; return total + lineStyle.lineHeight * height; }, 0); }; const _approximateDimensionsInternal = ( text: string | string[], style?: TextSizeStyleInterface, ) => { const angle = Array.isArray(style) ? style[0] && style[0].angle : style && style.angle; const height = _approximateTextHeightInternal(text, style); const width = _approximateTextWidthInternal(text, style); const widthWithRotate = angle ? _getSizeWithRotate(width, height, angle) : width; const heightWithRotate = angle ? _getSizeWithRotate(height, width, angle) : height; return { width: widthWithRotate, height: heightWithRotate * coefficients.heightOverlapCoef, }; }; const _getMeasurementContainer = memoize(() => { const element = document.createElementNS("http://www.w3.org/2000/svg", "svg"); element.setAttribute("xlink", "http://www.w3.org/1999/xlink"); element.setAttribute("width", "300"); element.setAttribute("height", "300"); element.setAttribute("viewBox", "0 0 300 300"); element.setAttribute("aria-hidden", "true"); const containerElement = document.createElementNS( "http://www.w3.org/2000/svg", "text", ); element.appendChild(containerElement); element.style.position = "fixed"; element.style.top = "-9999px"; element.style.left = "-9999px"; document.body.appendChild(element); return containerElement; }); const styleToKeyComponent = (style) => { if (!style) { return "null"; } return `${style.angle}:${style.fontFamily}:${style.fontSize}:${style.letterSpacing}:${style.lineHeight}`; }; const _measureDimensionsInternal = memoize( (text: string | string[], style?: TextSizeStyleInterface) => { let containerElement = _getMeasurementContainer(); if (!containerElement.isConnected) { _getMeasurementContainer.cache.clear?.(); containerElement = _getMeasurementContainer(); } const lines = _splitToLines(text); let heightAcc = 0; for (const [i, line] of lines.entries()) { const textElement = document.createElementNS( "http://www.w3.org/2000/svg", "tspan", ); const params = _prepareParams(style, i); textElement.style.fontFamily = params.fontFamily; textElement.style.fontSize = `${params.fontSize}px`; textElement.style.lineHeight = params.lineHeight; textElement.style.fontFamily = params.fontFamily; textElement.style.letterSpacing = params.letterSpacing; textElement.textContent = line; textElement.setAttribute("x", "0"); textElement.setAttribute("y", `${heightAcc}`); containerElement.appendChild(textElement); heightAcc += params.lineHeight * textElement.getBoundingClientRect().height; } const { width } = containerElement.getBoundingClientRect(); containerElement.innerHTML = ""; return { width: style?.angle ? _getSizeWithRotate(width, heightAcc, style?.angle) : width, height: style?.angle ? _getSizeWithRotate(heightAcc, width, style?.angle) : heightAcc, }; }, (text, style) => { const totalText = Array.isArray(text) ? text.join() : text; const totalStyle = Array.isArray(style) ? style.map(styleToKeyComponent).join() : styleToKeyComponent(style); return `${totalText}::${totalStyle}`; }, ); export interface TextSizeStyleInterface { angle?: number; fontFamily?: string; fontSize?: number | string; letterSpacing?: string; lineHeight?: number; } // Stubbable implementation. export const _approximateTextSizeInternal = { impl: ( text: string | string[], style?: TextSizeStyleInterface, __debugForceApproximate = false, ) => { // Attempt to first measure the element in DOM. If there is no DOM, fallback // to the less accurate approximation algorithm. const isClient = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined"; if (!isClient || __debugForceApproximate) { return _approximateDimensionsInternal(text, style); } return _measureDimensionsInternal(text, style); }, }; /** * Predict text size by font params. * @param {string|string[]} text Content for width calculation. * @param {Object} style Text styles, ,fontFamily, fontSize, etc. * @param {string} style.fontFamily Text fontFamily. * @param {(number|string)} style.fontSize Text fontSize. * @param {number} style.angle Text rotate angle. * @param {string} style.letterSpacing Text letterSpacing(space between letters). * @param {number} style.lineHeight Line height coefficient. * @returns {number} Approximate text label height. */ export const approximateTextSize = ( text: string | string[], style?: TextSizeStyleInterface, ): { width: number; height: number } => _approximateTextSizeInternal.impl(text, style); ================================================ FILE: packages/victory-core/src/victory-util/timer-context.ts ================================================ import React from "react"; import Timer from "./timer"; /** * The React context object consumers may use to access or override the global * timer. */ const TimerContext = React.createContext({ transitionTimer: new Timer(), animationTimer: new Timer(), }); TimerContext.displayName = "TimerContext"; export default TimerContext; ================================================ FILE: packages/victory-core/src/victory-util/timer.ts ================================================ import { timer, now, Timer as D3Timer } from "victory-vendor/d3-timer"; type TimerCallback = (elapsed: number, duration: number) => void; export default class Timer { private shouldAnimate: boolean; private readonly subscribers: Array<{ callback: TimerCallback; startTime: number; duration: number; }>; private activeSubscriptions: number; private timer: D3Timer | null; constructor() { this.shouldAnimate = true; this.subscribers = []; this.timer = null; this.activeSubscriptions = 0; } bypassAnimation() { this.shouldAnimate = false; } resumeAnimation() { this.shouldAnimate = true; } loop = () => { this.subscribers.forEach((s) => { s.callback(now() - s.startTime, s.duration); }); }; start() { if (!this.timer) { this.timer = timer(this.loop); } } stop() { if (this.timer) { this.timer.stop(); this.timer = null; } } subscribe(callback: TimerCallback, duration: number) { const subscriptionID = this.subscribers.push({ startTime: now(), callback, duration: this.shouldAnimate ? duration : 0, }); this.activeSubscriptions++; this.start(); return subscriptionID; } unsubscribe(id) { if (id !== null && this.subscribers[id - 1]) { delete this.subscribers[id - 1]; this.activeSubscriptions--; } if (this.activeSubscriptions === 0) { this.stop(); } } } ================================================ FILE: packages/victory-core/src/victory-util/transitions.test.ts ================================================ import React from "react"; import * as Transitions from "./transitions"; describe("getInitialTransitionState", () => { const makeChild = (data) => { return React.createElement("div", { data }); }; it("returns a 'falsey' transition object if children are not given", () => { const result = Transitions.getInitialTransitionState(null, null); expect(result).toEqual({ childrenTransitions: [], nodesWillExit: false, nodesWillEnter: false, nodesShouldEnter: false, }); }); it("it returns childTransitions entering and exiting false for identical data", () => { const child = makeChild([ { x: 1, y: 1 }, { x: 2, y: 3 }, ]); const result = Transitions.getInitialTransitionState(child, child); expect(result).toEqual({ childrenTransitions: [{ entering: false, exiting: false }], nodesWillExit: false, nodesWillEnter: false, nodesShouldEnter: false, }); }); it("it returns childTransitions with exiting data", () => { const child1 = makeChild([ { x: 1, y: 1 }, { x: 2, y: 3 }, ]); const child2 = makeChild([{ x: 1, y: 1 }]); const result = Transitions.getInitialTransitionState(child1, child2); expect(result).toEqual({ childrenTransitions: [{ entering: false, exiting: { 1: true } }], nodesWillExit: true, nodesWillEnter: false, nodesShouldEnter: false, }); }); it("it returns childTransitions with entering data", () => { const child1 = makeChild([{ x: 1, y: 1 }]); const child2 = makeChild([ { x: 1, y: 1 }, { x: 2, y: 3 }, ]); const result = Transitions.getInitialTransitionState(child1, child2); expect(result).toEqual({ childrenTransitions: [{ entering: { 1: true }, exiting: false }], nodesWillExit: false, nodesWillEnter: true, nodesShouldEnter: false, }); }); }); describe("getTransitionPropsFactory", () => { const toZero = jest.fn().mockImplementation(() => ({ y: 0 })); const makeChild = (data) => { return { type: { defaultTransitions: { onExit: { duration: 1, before: toZero }, onEnter: { duration: 2, before: toZero }, }, }, props: { data, animate: { duration: 0 } }, }; }; const callback = jest.fn(); it("returns a function that describes data exiting", () => { const exitingState = { childrenTransitions: [{ entering: false, exiting: { 1: true } }], nodesWillExit: true, nodesWillEnter: false, nodesShouldEnter: false, nodesShouldLoad: true, nodesDoneLoad: true, }; const result = Transitions.getTransitionPropsFactory( {}, exitingState, callback, ); const child = makeChild([ { x: 1, y: 1 }, { x: 2, y: 3 }, ]); const calledResult = result(child, 0); expect(result).toBeInstanceOf(Function); expect(Object.keys(calledResult)).toEqual( expect.arrayContaining(["animate", "data"]), ); expect(calledResult.data).toEqual([ { x: 1, y: 1 }, { x: 2, y: 0 }, ]); expect(calledResult.animate!.duration).toEqual( child.type.defaultTransitions.onExit.duration, ); }); it("returns a function that describes data entering", () => { const enteringState = { childrenTransitions: [{ entering: { 1: true }, exiting: false }], nodesWillExit: false, nodesWillEnter: true, nodesShouldEnter: false, nodesShouldLoad: true, nodesDoneLoad: true, }; const result = Transitions.getTransitionPropsFactory( {}, enteringState, callback, ); const child = makeChild([ { x: 1, y: 1 }, { x: 2, y: 3 }, ]); const calledResult = result(child, 0); expect(result).toBeInstanceOf(Function); expect(calledResult).toBeInstanceOf(Object); expect(calledResult).toBeInstanceOf(Object); expect(Object.keys(calledResult)).toEqual( expect.arrayContaining(["animate", "data"]), ); expect(calledResult.data).toEqual([ { x: 1, y: 1 }, { x: 2, y: 0 }, ]); }); }); ================================================ FILE: packages/victory-core/src/victory-util/transitions.ts ================================================ import React from "react"; import defaults from "lodash/defaults"; import identity from "lodash/identity"; import { AnimatePropTypeInterface } from "../types/prop-types"; function getDatumKey(datum, idx) { return (datum.key || idx).toString(); } function getKeyedData(data) { return data.reduce((keyedData, datum, idx) => { const key = getDatumKey(datum, idx); keyedData[key] = datum; return keyedData; }, {}); } function getKeyedDataDifference(a, b) { let hasDifference = false; const difference = Object.keys(a).reduce((_difference, key) => { if (!(key in b)) { hasDifference = true; _difference[key] = true; } return _difference; }, {}); return hasDifference && difference; } /** * Calculate which data-points exist in oldData and not nextData - * these are the `exiting` data-points. Also calculate which * data-points exist in nextData and not oldData - these are the * `entering` data-points. * * @param {Array} oldData this.props.data Array * @param {Array} nextData this.props.data Array * * @return {Object} Object with `entering` and `exiting` properties. * entering[datum.key] will be true if the data is * entering, and similarly for `exiting`. */ function getNodeTransitions(oldData, nextData) { const oldDataKeyed = oldData && getKeyedData(oldData); const nextDataKeyed = nextData && getKeyedData(nextData); return { entering: oldDataKeyed && getKeyedDataDifference(nextDataKeyed, oldDataKeyed), exiting: nextDataKeyed && getKeyedDataDifference(oldDataKeyed, nextDataKeyed), }; } function getChildData(child) { if (child.type && child.type.getData) { return child.type.getData(child.props); } return (child.props && child.props.data) || false; } /** * If a parent component has animation enabled, calculate the transitions * for any data of any child component that supports data transitions. * Data transitions are defined as any two datasets where data nodes exist * in the first set and not the second, in the second and not the first, * or both. * * @param {Children} oldChildren this.props.children from old props * @param {Children} nextChildren this.props.children from next props * * @return {Object} Object with the following properties: * - nodesWillExit * - nodesWillEnter * - childrenTransitions * - nodesShouldEnter */ export function getInitialTransitionState(oldChildren, nextChildren) { let nodesWillExit = false; let nodesWillEnter = false; const getTransition = (oldChild, newChild) => { if (!newChild || oldChild.type !== newChild.type) { return {}; } const { entering, exiting } = getNodeTransitions(getChildData(oldChild), getChildData(newChild)) || {}; nodesWillExit = nodesWillExit || !!exiting; nodesWillEnter = nodesWillEnter || !!entering; return { entering: entering || false, exiting: exiting || false }; }; const getTransitionsFromChildren = (old, next) => { return old.map((child, idx) => { if (child && child.props && child.props.children && next[idx]) { return getTransitionsFromChildren( React.Children.toArray(old[idx].props.children), React.Children.toArray(next[idx].props.children), ); } // get Transition entering and exiting nodes return getTransition(child, next[idx]); }); }; const childrenTransitions = getTransitionsFromChildren( React.Children.toArray(oldChildren), React.Children.toArray(nextChildren), ); return { nodesWillExit, nodesWillEnter, childrenTransitions, // TODO: This may need to be refactored for the following situation. // The component receives new props, and the data provided // is a perfect match for the previous data and domain except // for new nodes. In this case, we wouldn't want a delay before // the new nodes appear. nodesShouldEnter: false, }; } type TransitionProps = { data; animate?: AnimatePropTypeInterface; clipWidth?: number; }; function getInitialChildProps(animate, data): TransitionProps { const after = animate.onEnter && animate.onEnter.after ? animate.onEnter.after : identity; return { data: data.map((datum, idx) => Object.assign({}, datum, after(datum, idx, data)), ), }; } // eslint-disable-next-line max-params function getChildBeforeLoad(animate, child, data, cb): TransitionProps { const newAnimate = Object.assign({}, animate, { onEnd: cb }); if (newAnimate && newAnimate.onLoad && !newAnimate.onLoad.duration) { return { animate: newAnimate, data }; } const before = newAnimate.onLoad && newAnimate.onLoad.before ? newAnimate.onLoad.before : identity; // If nodes need to exit, transform them with the provided onLoad.before function. const newData = data.map((datum, idx) => { return Object.assign({}, datum, before(datum, idx, data)); }); return { animate: newAnimate, data: newData, clipWidth: 0 }; } function getChildOnLoad(animate, data, cb): TransitionProps { const newAnimate = Object.assign({}, animate, { onEnd: cb }); let newData = data; if (newAnimate && newAnimate.onLoad && !newAnimate.onLoad.duration) { return { animate, data }; } const after = animate.onLoad && animate.onLoad.after ? animate.onLoad.after : identity; // If nodes need to exit, transform them with the provided onLoad.after function. newData = data.map((datum, idx) => { return Object.assign({}, datum, after(datum, idx, data)); }); return { animate: newAnimate, data: newData }; } // eslint-disable-next-line max-params function getChildPropsOnExit( animate, child, data, exitingNodes, cb, ): TransitionProps { // Whether or not _this_ child has exiting nodes, we want the exit- // transition for all children to have the same duration, delay, etc. const onExit = animate && animate.onExit; const newAnimate = Object.assign({}, animate, onExit); let newData = data; if (exitingNodes) { // After the exit transition occurs, trigger the animations for // nodes that are neither exiting nor entering. animate.onEnd = cb; const before = animate.onExit && animate.onExit.before ? animate.onExit.before : identity; // If nodes need to exit, transform them with the provided onExit.before function. newData = data.map((datum, idx) => { const key = (datum.key || idx).toString(); return exitingNodes[key] ? Object.assign({}, datum, before(datum, idx, data)) : datum; }); } return { animate: newAnimate, data: newData }; } // eslint-disable-next-line max-params function getChildPropsBeforeEnter( animate, child, data, enteringNodes, cb, ): TransitionProps { let newAnimate = animate; let newData = data; if (enteringNodes) { // Perform a normal animation here, except - when it finishes - trigger // the transition for entering nodes. newAnimate = Object.assign({}, animate, { onEnd: cb }); const before = animate.onEnter && animate.onEnter.before ? animate.onEnter.before : identity; // We want the entering nodes to be included in the transition target // domain. However, we may not want these nodes to be displayed initially, // so perform the `onEnter.before` transformation on each node. newData = data.map((datum, idx) => { const key = (datum.key || idx).toString(); return enteringNodes[key] ? Object.assign({}, datum, before(datum, idx, data)) : datum; }); } return { animate: newAnimate, data: newData }; } // eslint-disable-next-line max-params function getChildPropsOnEnter( animate, data, enteringNodes, cb, ): TransitionProps { // Whether or not _this_ child has entering nodes, we want the entering- // transition for all children to have the same duration, delay, etc. const onEnter = animate && animate.onEnter; const newAnimate = Object.assign({}, animate, onEnter); let newData = data; if (enteringNodes) { // Old nodes have been transitioned to their new values, and the // domain should encompass the nodes that will now enter. So perform // the `onEnter.after` transformation on each node. newAnimate.onEnd = cb; const after = newAnimate.onEnter && newAnimate.onEnter.after ? newAnimate.onEnter.after : identity; newData = data.map((datum, idx) => { const key = getDatumKey(datum, idx); return enteringNodes[key] ? Object.assign({}, datum, after(datum, idx, data)) : datum; }); } return { animate: newAnimate, data: newData }; } /** * getTransitionPropsFactory - putting the Java in JavaScript. This will return a * function that returns prop transformations for a child, given that child's props * and its index in the parent's children array. * * In particular, this will include an `animate` object that is set appropriately * so that each child will be synchronized for each stage of a transition * animation. It will also include a transformed `data` object, where each datum * is transformed by `animate.onExit` and `animate.onEnter` `before` and `after` * functions. * * @param {Object} props `this.props` for the parent component. * @param {Object} state `this.state` for the parent component. * @param {Function} setState Function that, when called, will `this.setState` on * the parent component with the provided object. * * @return {Function} Child-prop transformation function. */ export function getTransitionPropsFactory(props, state, setState) { const nodesWillExit = state && state.nodesWillExit; const nodesWillEnter = state && state.nodesWillEnter; const nodesShouldEnter = state && state.nodesShouldEnter; const nodesShouldLoad = state && state.nodesShouldLoad; const nodesDoneLoad = state && state.nodesDoneLoad; const childrenTransitions = (state && state.childrenTransitions) || []; const transitionDurations = { enter: props.animate && props.animate.onEnter && props.animate.onEnter.duration, exit: props.animate && props.animate.onExit && props.animate.onExit.duration, load: props.animate && props.animate.onLoad && props.animate.onLoad.duration, move: props.animate && props.animate.duration, }; const onLoad = (child, data, animate) => { if (nodesShouldLoad) { return getChildOnLoad(animate, data, () => { setState({ nodesShouldLoad: false, nodesDoneLoad: true }); }); } return getChildBeforeLoad(animate, child, data, () => { setState({ nodesDoneLoad: true }); }); }; // eslint-disable-next-line max-params const onExit = (nodes, child, data, animate) => { return getChildPropsOnExit(animate, child, data, nodes, () => { setState({ nodesWillExit: false }); }); }; // eslint-disable-next-line max-params const onEnter = (nodes, child, data, animate) => { if (nodesShouldEnter) { return getChildPropsOnEnter(animate, data, nodes, () => { setState({ nodesWillEnter: false }); }); } return getChildPropsBeforeEnter(animate, child, data, nodes, () => { setState({ nodesShouldEnter: true }); }); }; const getChildTransitionDuration = function (child, type) { const animate = child.props.animate; if (!child.type) { return {}; } const defaultTransitions = child.props && child.props.polar ? child.type.defaultPolarTransitions || child.type.defaultTransitions : child.type.defaultTransitions; if (defaultTransitions) { const animationDuration = animate[type] && animate[type].duration; return animationDuration !== undefined ? animationDuration : defaultTransitions[type] && defaultTransitions[type].duration; } return {}; }; return function getTransitionProps(child, index): TransitionProps { const data = getChildData(child) || []; const animate: AnimatePropTypeInterface = defaults( {}, props.animate, child.props.animate, ); const defaultTransitions = child.props.polar ? child.type.defaultPolarTransitions || child.type.defaultTransitions : child.type.defaultTransitions; animate.onExit = defaults( {}, animate.onExit, defaultTransitions && defaultTransitions.onExit, ); animate.onEnter = defaults( {}, animate.onEnter, defaultTransitions && defaultTransitions.onEnter, ); animate.onLoad = defaults( {}, animate.onLoad, defaultTransitions && defaultTransitions.onLoad, ); const childTransitions = childrenTransitions[index] || childrenTransitions[0]; if (!nodesDoneLoad) { // should do onLoad animation const load = transitionDurations.load !== undefined ? transitionDurations.load : getChildTransitionDuration(child, "onLoad"); const animation = { duration: load }; return onLoad(child, data, Object.assign({}, animate, animation)); } else if (nodesWillExit) { const exitingNodes = childTransitions && childTransitions.exiting; const exit = transitionDurations.exit !== undefined ? transitionDurations.exit : getChildTransitionDuration(child, "onExit"); // if nodesWillExit, but this child has no exiting nodes, set a delay instead of a duration const animation = exitingNodes ? { duration: exit } : { delay: exit }; return onExit( exitingNodes, child, data, Object.assign({}, animate, animation), ); } else if (nodesWillEnter) { const enteringNodes = childTransitions && childTransitions.entering; const enter = transitionDurations.enter !== undefined ? transitionDurations.enter : getChildTransitionDuration(child, "onEnter"); const move = transitionDurations.move !== undefined ? transitionDurations.move : child.props.animate && child.props.animate.duration; const animation = { duration: nodesShouldEnter && enteringNodes ? enter : move, }; return onEnter( enteringNodes, child, data, Object.assign({}, animate, animation), ); } else if (!state && animate && animate.onExit) { // This is the initial render, and nodes may enter when props change. Because // animation interpolation is determined by old- and next- props, data may need // to be augmented with certain properties. // // For example, it may be desired that exiting nodes go from `opacity: 1` to // `opacity: 0`. Without setting this on a per-datum basis, the interpolation // might go from `opacity: undefined` to `opacity: 0`, which would result in // interpolated `opacity: NaN` values. // return getInitialChildProps(animate, data); } return { animate, data }; }; } ================================================ FILE: packages/victory-core/src/victory-util/type-helpers.ts ================================================ import { ForAxes, AxisType, Tuple, Datum, DatumValue, } from "../types/prop-types"; export function hasValueForAxis( value: unknown | ForAxes, axis: AxisType, ): value is ForAxes { if (typeof value === "object" && value !== null) { return axis in value; } return false; } export function isTuple(value: unknown): value is Tuple { return Array.isArray(value) && value.length === 2; } export function getValueForAxis( value: T | ForAxes | undefined, axis: AxisType, ): T | undefined { if (hasValueForAxis(value, axis)) { return value[axis] as T; } return value; } export function isDate(value: unknown): value is Date { return value instanceof Date; } export function isKeyValueObject( datum: Datum, ): datum is { [key: string]: DatumValue } { return ( typeof datum === "object" && !Array.isArray(datum) && !(datum instanceof Date) ); } ================================================ FILE: packages/victory-core/src/victory-util/user-props.ts ================================================ import * as React from "react"; import { evaluateProp } from "./helpers"; /* USER_PROPS_SAFELIST is to contain any string deemed safe for user props. The startsWidth array will contain the start of any accepted user-prop that starts with these characters. The exactMatch will contain a list of exact prop names that are accepted. */ const USER_PROPS_SAFELIST = { startsWith: ["data-", "aria-"] as const, exactMatch: [] as string[], }; // Normally we'd use Template Literal Types, but we're avoiding it to maximize TS compatibility with TS < 4.1 type SafeAttribute = string; // `data-${string}` | `aria-${string}`; /** * doesPropStartWith: Function that takes a prop's key and runs it against all * options in the USER_PROPS_SAFELIST and checks to see if it starts with any * of those options. * @param {string} key: prop key to be tested against whitelist * @returns {Boolean}: returns true if the key starts with an option or false if * otherwise */ const doesPropStartWith = (key: string): key is SafeAttribute => { let startsWith = false; USER_PROPS_SAFELIST.startsWith.forEach((starterString) => { const regex = new RegExp(`\\b(${starterString})(\\w|-)+`, "g"); if (regex.test(key)) startsWith = true; }); return startsWith; }; /** * isExactMatch: checks to see if the given key matches any of the 'exactMatch' * items in the whitelist * @param {String} key: prop key to be tested against the whitelist-exact match * array. * @returns {Boolean}: return true if whitelist contains that key, otherwise * returns false. */ const isExactMatch = (key: string): key is SafeAttribute => USER_PROPS_SAFELIST.exactMatch.includes(key); /** * testIfSafeProp: tests prop's key against both startsWith and exactMatch values * @param {String} key: prop key to be tested against the whitelist * @returns {Boolean}: returns true if found in whitelist, otherwise returns false */ const testIfSafeProp = (key: string): key is SafeAttribute => { if (doesPropStartWith(key) || isExactMatch(key)) return true; return false; }; /** * Asserts that value is not null or undefined, throwing an error if it is. * @param value The value to assert * @param message The error message to throw */ export function assert( value: T, message?: string, ): asserts value is NonNullable { if (value === undefined || value === null) { throw new Error(message); } } /** * getSafeUserProps - function that takes in a props object and removes any * key-value entries that do not match filter strings in the USER_PROPS_SAFELIST * object. * * @param {Object} props: props to be filtered against USER_PROPS_SAFELIST * @returns {Object}: object containing remaining acceptable props */ export const getSafeUserProps = ( props: T extends Record ? T : never, ): Record => { const propsToFilter = { ...props }; return Object.fromEntries( Object.entries(propsToFilter) .filter(([key]) => testIfSafeProp(key)) .map(([key, value]) => { return [key, evaluateProp(value, props)]; }), ); }; /** * Wraps a component and adds safe user props * * @param {ReactElement} component: parent component * @param {Object} props: props to be filtered * @returns {ReactElement} modified component */ export const withSafeUserProps = (component: React.ReactElement, props) => { return React.cloneElement(component, getSafeUserProps(props)); }; ================================================ FILE: packages/victory-core/src/victory-util/wrapper.test.tsx ================================================ /* eslint-disable react/no-multi-comp */ import React from "react"; import * as Wrapper from "./wrapper"; const MockVictoryAxis = (props) =>
; MockVictoryAxis.role = "axis"; const MockVictoryLine = (props) =>
; MockVictoryLine.role = "line"; describe("helpers/wrapper", () => { describe("getDomain", () => { const victoryLine = ; const xAxis = ; const yAxis = ; const childComponents = [victoryLine, xAxis, yAxis]; it("calculates a domain from props", () => { const props = { domain: { x: [1, 2], y: [2, 3] } }; const domainResultX = Wrapper.getDomain(props, "x", childComponents); expect(domainResultX).toEqual([1, 2]); }); it("calculates a domain from child components", () => { const props = { children: childComponents }; const domainResultX = Wrapper.getDomain(props, "x", childComponents); expect(domainResultX).toEqual(victoryLine.props.domain); }); }); describe("getStringsFromData", () => { it("returns an array of strings from a data prop", () => { const props = { data: [ { x: "one", y: 1 }, { x: "red", y: 2 }, { x: "cat", y: 3 }, ], }; const childComponents = []; const dataStrings = Wrapper.getStringsFromData(childComponents).x; expect(dataStrings).toEqual(["one", "red", "cat"]); }); it("returns an array of strings from array-type data", () => { const props = { data: [ ["one", 1], ["red", 2], ["cat", 3], ], x: 0, y: 1, }; const childComponents = []; const dataStrings = Wrapper.getStringsFromData(childComponents).x; expect(dataStrings).toEqual(["one", "red", "cat"]); }); it("only returns strings, if data is mixed", () => { const props = { data: [ { x: 1, y: 1 }, { x: "three", y: 3 }, ], }; const childComponents = []; expect(Wrapper.getStringsFromData(childComponents).x).toEqual(["three"]); }); it("returns an empty array when no strings are present", () => { const props = { data: [ { x: 1, y: 1 }, { x: 3, y: 3 }, ], }; const childComponents = []; expect(Wrapper.getStringsFromData(childComponents).x).toEqual([]); }); it("returns an empty array when no children are given", () => { expect(Wrapper.getStringsFromData([])).toEqual({ x: [], y: [] }); }); }); describe("getStringsFromCategories", () => { it("gets string from all options", () => { expect(Wrapper.getStringsFromChildrenCategories([], "x")).toEqual([]); expect( Wrapper.getStringsFromChildrenCategories( [], "x", ), ).toEqual(["one", "two", "three"]); expect( Wrapper.getStringsFromChildrenCategories( [], "y", ), ).toEqual(["one", "two", "three"]); expect( Wrapper.getStringsFromChildrenCategories( [ , ], "x", ), ).toEqual(["one", "two", "three"]); expect( Wrapper.getStringsFromChildrenCategories( [ , , ], "x", ), ).toEqual(["one", "two", "three", "four", "five", "six"]); expect( Wrapper.getStringsFromChildrenCategories( [ , ], "x", ), ).toEqual([]); }); }); }); ================================================ FILE: packages/victory-core/src/victory-util/wrapper.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import uniq from "lodash/uniq"; import groupBy from "lodash/groupBy"; import uniqBy from "lodash/uniqBy"; import * as Axis from "./axis"; import * as Style from "./style"; import * as Data from "./data"; import * as Domain from "./domain"; import * as Events from "./events"; import * as Collection from "./collection"; import * as Helpers from "./helpers"; import * as Scale from "./scale"; import * as Log from "./log"; export function addBinsToParentPropsIfHistogram({ children, props, childComponents, parentProps, }) { const someChildrenAreHistograms = children.some((child) => { return child.type && child.type.role === "histogram"; }); const allChildrenAreHistograms = someChildrenAreHistograms && children.length && children.every((child) => { return child.type && child.type.role === "histogram"; }); if (someChildrenAreHistograms && !allChildrenAreHistograms) { Log.warn( "VictoryHistogram only supports being stacked with other VictoryHistogram components. Check to make sure that you are only passing VictoryHistogram components to VictoryStack", ); } // if we are stacking histograms, we need to generate explicit bins // or else each histogram may end up having different bins if (!allChildrenAreHistograms) { return parentProps; } let childBins = props.bins || childComponents[0].props.bins; // if we have explicit bins then we don't need to calculate them if (!Array.isArray(childBins)) { const combinedData = children.reduce((memo, child) => { const xAccessor = Helpers.createAccessor(child.props.x || "x"); return memo.concat( child.props.data.map((datum) => ({ x: xAccessor(datum) })), ); }, []); // use the same function to generate bins as VictoryHistogram but with // the combined data from above, then get explicit bins from that const getFormattedHistogramData = children[0].type.getFormattedData; childBins = getFormattedHistogramData({ data: combinedData, bins: childBins, }).reduce( (memo, { x0, x1 }, index) => index === 0 ? memo.concat([x0, x1]) : memo.concat(x1), [], ); } return { ...parentProps, bins: childBins }; } export function getDataFromChildren(props, childComponents) { const { polar, startAngle, endAngle, categories, minDomain, maxDomain } = props; let parentProps = { polar, startAngle, endAngle, categories, minDomain, maxDomain, }; let stack = 0; const children = childComponents ? childComponents.slice(0) : React.Children.toArray(props.children); parentProps = addBinsToParentPropsIfHistogram({ children, props, childComponents, parentProps, }); const iteratee = (child, childName, parent) => { const childProps = Object.assign({}, child.props, parentProps); let childData; let childElement = child; if (!Data.isDataComponent(child)) { return null; } else if (child.type && Helpers.isFunction(child.type.getData)) { childElement = parent ? React.cloneElement(child, parent.props) : child; childData = childElement.type.getData(childProps); } else { childData = Data.getData(childProps); } stack += 1; return childData.map((datum, index) => Object.assign({ _stack: stack, _group: index }, datum), ); }; const stacked = children.filter( (c) => c.type && c.type.role === "stack", ).length; const combine = (memo, val) => memo.concat(uniqBy(val, "_group")); const datasets = Helpers.reduceChildren( children, iteratee, props, [], combine, ); const group = stacked ? "_group" : "_stack"; return Object.values(groupBy(datasets, group)); } export function getData(props, childComponents) { if (props.data) { return Data.getData(props); } return getDataFromChildren( props, childComponents || React.Children.toArray(props.children), ); } export function getWidth(props, groupLength?, seriesLength?) { const { datasets, horizontal } = props; const range = horizontal ? Helpers.getRange(props, "y") : Helpers.getRange(props, "x"); const extent = Math.abs(range[1] - range[0]); const seriesLengthValue = seriesLength !== undefined ? seriesLength : (Array.isArray(datasets[0]) && datasets[0].length) || 1; const groupLengthValue = groupLength || datasets.length; const bars = groupLengthValue * seriesLengthValue; const barRatio = 0.5; return Math.round((barRatio * extent) / bars); } export function getDefaultDomainPadding(props, axis, childComponents) { if (props.polar || axis !== "x") { return undefined; } const groupComponent = childComponents.filter((child) => { return child.type && child.type.role && child.type.role === "group"; }); if (groupComponent.length < 1) { return undefined; } const { offset, children } = groupComponent[0].props; if (!offset) { return undefined; } const firstChild = Array.isArray(children) && children[0]; if (!firstChild) { return undefined; } let barWidth = firstChild.props.barWidth; let dataLength = (firstChild.props.data && firstChild.props.data.length) || 1; if (firstChild && firstChild.type.role === "stack") { const nestedChild = firstChild.props.children && firstChild.props.children[0]; if (!nestedChild) { return undefined; } barWidth = nestedChild.props.barWidth; dataLength = firstChild.props.children.length; } const width = barWidth || getWidth(props, children.length, dataLength); return { x: (width * children.length) / 2 + (offset - width * ((children.length - 1) / 2)), }; } export function getDomainFromChildren(props, axis, childComponents) { const children = childComponents ? childComponents.slice(0) : React.Children.toArray(props.children); const parentData = props.data ? Data.getData(props) : undefined; const { polar, startAngle, endAngle, categories, minDomain, maxDomain, horizontal, } = props; const baseParentProps = { horizontal, polar, startAngle, endAngle, minDomain, maxDomain, categories, }; const parentProps = parentData ? Object.assign(baseParentProps, { data: parentData }) : baseParentProps; const iteratee = (child) => { const sharedProps = Object.assign({}, child.props, parentProps); if (!Domain.isDomainComponent(child)) { return null; } else if (child.type && Helpers.isFunction(child.type.getDomain)) { return child.props && child.type.getDomain(sharedProps, axis); } return Domain.getDomain(sharedProps, axis); }; const childDomains = Helpers.reduceChildren(children, iteratee, props); const min = childDomains.length === 0 ? 0 : Collection.getMinValue(childDomains); const max = childDomains.length === 0 ? 1 : Collection.getMaxValue(childDomains); return [min, max]; } export function getDomain(props, axis, childComponents) { const children = childComponents || React.Children.toArray(props.children); const propsDomain = Domain.getDomainFromProps(props, axis); const domainPadding = getDefaultDomainPadding(props, axis, children); let domain; if (propsDomain) { domain = propsDomain; } else { const minDomain = Domain.getMinFromProps(props, axis); const maxDomain = Domain.getMaxFromProps(props, axis); const dataset = (props.data || props.y) && Data.getData(props); const dataDomain = dataset ? Domain.getDomainFromData(props, axis, dataset)! : []; const childDomain = getDomainFromChildren(props, axis, children); const min = minDomain || Collection.getMinValue([...dataDomain, ...childDomain]); const max = maxDomain || Collection.getMaxValue([...dataDomain, ...childDomain]); domain = Domain.getDomainFromMinMax(min, max); } return Domain.formatDomain( domain, Object.assign({ domainPadding }, props), axis, ); } export function getScale(props, axis, childComponents?) { if (props.data) { return Scale.getBaseScale(props, axis); } const children = childComponents ? childComponents.slice(0) : React.Children.toArray(props.children); const iteratee = (child) => { const sharedProps = Object.assign({}, child.props, { horizontal: props.horizontal, }); return Scale.getScaleType(sharedProps, axis); }; const childScale: string[] = uniq( Helpers.reduceChildren(children, iteratee, props), ); // default to linear scale if more than one uniq scale type is given by children return childScale.length > 1 ? Scale.getScaleFromName("linear") : Scale.getScaleFromName(childScale[0]); } export function getAllEvents(props) { const components = ["groupComponent", "containerComponent", "labelComponent"]; const componentEvents = Events.getComponentEvents(props, components); let events = props.events; if (Array.isArray(componentEvents)) { events = Array.isArray(props.events) ? componentEvents.concat(...props.events) : componentEvents; } return events || []; } // eslint-disable-next-line max-params export function getColor(calculatedProps, child, index, theme) { // check for styles first const { style } = calculatedProps; let { colorScale, color } = calculatedProps; if (style && style.data && style.data.fill) { return style.data.fill; } colorScale = child.props && child.props.colorScale ? child.props.colorScale : colorScale; color = child.props && child.props.color ? child.props.color : color; if (!colorScale && !color) { return undefined; } const colors = Array.isArray(colorScale) ? colorScale : Style.getColorScale(colorScale, theme); return color || colors[index % colors.length]; } export function getStyle(theme, style, role) { const defaultStyle = theme && theme[role] && theme[role].style ? theme[role].style : {}; return Helpers.getStyles(style, defaultStyle); } // eslint-disable-next-line max-params export function getChildStyle(child, index, calculatedProps, theme) { const { style, role } = calculatedProps; const childStyle = child.props.style || {}; if (Array.isArray(childStyle)) { return childStyle; } const childRole = child.type && child.type.role; const defaultFill = childRole === "stack" ? undefined : getColor(calculatedProps, child, index, theme); const defaultColor = childRole === "line" ? { fill: "none", stroke: defaultFill } : { fill: defaultFill }; const dataWidth = role === "stack" ? {} : { width: getWidth(calculatedProps) }; const dataStyle = defaults( {}, childStyle.data, Object.assign({}, dataWidth, style.data, defaultColor), ); const labelsStyle = defaults({}, childStyle.labels, style.labels); return { ...childStyle, parent: style.parent, data: dataStyle, labels: labelsStyle, }; } export function getStringsFromChildrenCategories(childComponents, axis) { const iteratee = (child) => { if (!Domain.isDomainComponent(child)) { return null; } const childProps = child.props || {}; return Data.getStringsFromCategories(childProps, axis); }; return Helpers.reduceChildren(childComponents.slice(0), iteratee); } export function getStringsFromData(childComponents) { const iteratee = (child) => { const childProps = child.props || {}; let data; if (!Data.isDataComponent(child)) { return null; } else if (child.type && Helpers.isFunction(child.type.getData)) { data = child.type.getData(childProps); } else { data = Data.getData(childProps); } return data.map((d) => ({ x: d.xName, y: d.yName })); }; const initialMemo = { x: [] as number[], y: [] as number[] }; const combine = ( memo: typeof initialMemo, datum: NonNullable>, ) => { const x = Array.isArray(datum) ? datum.map((d) => d.x).filter(Boolean) : datum.x; const y = Array.isArray(datum) ? datum.map((d) => d.y).filter(Boolean) : datum.y; return { x: x !== undefined ? memo.x.concat(x) : memo.x, y: y !== undefined ? memo.y.concat(y) : memo.y, }; }; return Helpers.reduceChildren( childComponents.slice(0), iteratee, {}, initialMemo, combine, ); } export function getCategoryAndAxisStringsFromChildren( props, axis, childComponents, ) { const categories = Data.getStringsFromCategories(props, axis); const axisComponent = Axis.getAxisComponent(childComponents, axis); const axisStrings = axisComponent ? Data.getStringsFromAxes(axisComponent.props, axis) : []; const categoryStrings = categories.length ? categories : getStringsFromChildrenCategories(childComponents, axis); return uniq([...categoryStrings, ...axisStrings].flat()); } export function getStringsFromChildren(props, childComponents) { const children = childComponents || React.Children.toArray(props.children); const xStrings = getCategoryAndAxisStringsFromChildren(props, "x", children); const yStrings = getCategoryAndAxisStringsFromChildren(props, "y", children); const dataStrings = getStringsFromData(children); return { x: uniq([...xStrings, ...dataStrings.x].flat()), y: uniq([...yStrings, ...dataStrings.y].flat()), }; } export function getCategories(props, childComponents, allStrings?) { const xPropCategories = props.categories && Data.getStringsFromCategories(props, "x"); const yPropCategories = props.categories && Data.getStringsFromCategories(props, "y"); const fallbackRequired = !xPropCategories || !yPropCategories; const fallbackProps = fallbackRequired ? allStrings || getStringsFromChildren(props, childComponents) : {}; const xCategories = xPropCategories || fallbackProps.x; const yCategories = yPropCategories || fallbackProps.y; return { x: xCategories.length > 0 ? xCategories : undefined, y: yCategories.length > 0 ? yCategories : undefined, }; } ================================================ FILE: packages/victory-core/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-core/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-create-container/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-create-container/CHANGELOG.md ================================================ # victory-create-container ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ### Patch Changes - update createContainer types to be backwards compatibile ([#2933](https://github.com/FormidableLabs/victory/pull/2933)) ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Refactor containers and portal to function components ([#2799](https://github.com/FormidableLabs/victory/pull/2799)) * Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash keys with native code ([#2811](https://github.com/FormidableLabs/victory/pull/2811)) * Replace lodash array utils with native code ([#2810](https://github.com/FormidableLabs/victory/pull/2810)) - Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Migrate victory-create-container to TypeScript ([#2731](https://github.com/FormidableLabs/victory/pull/2731)) * Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies [[`c5b4f660c`](https://github.com/FormidableLabs/victory/commit/c5b4f660cb91d8d1979d216b846a44f0c5030ec1)]: - victory-zoom-container@36.6.8 - victory-brush-container@36.6.8 - victory-core@36.6.8 - victory-cursor-container@36.6.8 - victory-selection-container@36.6.8 - victory-voronoi-container@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-brush-container@36.6.7 - victory-core@36.6.7 - victory-cursor-container@36.6.7 - victory-selection-container@36.6.7 - victory-voronoi-container@36.6.7 - victory-zoom-container@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies [[`6c6764f73`](https://github.com/FormidableLabs/victory/commit/6c6764f734b9655170c75798b7fe1c6d9e63f2a5)]: - victory-brush-container@36.6.6 - victory-cursor-container@36.6.6 - victory-selection-container@36.6.6 - victory-voronoi-container@36.6.6 - victory-zoom-container@36.6.6 - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416), [`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-zoom-container@36.6.5 - victory-core@36.6.5 - victory-brush-container@36.6.5 - victory-cursor-container@36.6.5 - victory-selection-container@36.6.5 - victory-voronoi-container@36.6.5 ## 36.6.4 ### Patch Changes - Added explicit `any` type defs (fixes [#2358](https://github.com/FormidableLabs/victory/issues/2358)) ([`57ed0fe30`](https://github.com/FormidableLabs/victory/commit/57ed0fe304dbc8753da1126a02d44de8004e96aa)) * Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) * Updated dependencies [[`57ed0fe30`](https://github.com/FormidableLabs/victory/commit/57ed0fe304dbc8753da1126a02d44de8004e96aa), [`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-selection-container@36.6.4 - victory-voronoi-container@36.6.4 - victory-zoom-container@36.6.4 - victory-core@36.6.4 - victory-brush-container@36.6.4 - victory-cursor-container@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-brush-container@36.6.3 - victory-core@36.6.3 - victory-cursor-container@36.6.3 - victory-selection-container@36.6.3 - victory-voronoi-container@36.6.3 - victory-zoom-container@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies [[`877c16923`](https://github.com/FormidableLabs/victory/commit/877c169230c24ab02f570a21dca10a2dce0dcf4e)]: - victory-cursor-container@36.6.2 - victory-brush-container@36.6.2 - victory-core@36.6.2 - victory-selection-container@36.6.2 - victory-voronoi-container@36.6.2 - victory-zoom-container@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 - victory-cursor-container@36.6.1 - victory-brush-container@36.6.1 - victory-selection-container@36.6.1 - victory-voronoi-container@36.6.1 - victory-zoom-container@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`f7bf134e5`](https://github.com/FormidableLabs/victory/commit/f7bf134e58bc1660c28f83f0eede3b19048d2656), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 - victory-brush-container@36.6.0 - victory-cursor-container@36.6.0 - victory-selection-container@36.6.0 - victory-voronoi-container@36.6.0 - victory-zoom-container@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-create-container/README.md ================================================ # createContainer `victory-create-container@^30.0.0` exports `createContainer`, `combineContainerMixins` and `makeCreateContainerFunction` To view documentation for `createContainer` please see https://commerce.nearform.com/open-source/victory/docs/create-container To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/create-container.md ================================================ FILE: packages/victory-create-container/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-create-container/package.json ================================================ { "name": "victory-create-container", "version": "37.3.6", "description": "Container Helper for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-brush-container": "37.3.6", "victory-core": "37.3.6", "victory-cursor-container": "37.3.6", "victory-selection-container": "37.3.6", "victory-voronoi-container": "37.3.6", "victory-zoom-container": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-brush-container:build:lib:esm", "../victory-core:build:lib:esm", "../victory-cursor-container:build:lib:esm", "../victory-selection-container:build:lib:esm", "../victory-voronoi-container:build:lib:esm", "../victory-zoom-container:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-brush-container:build:lib:cjs", "../victory-core:build:lib:cjs", "../victory-cursor-container:build:lib:cjs", "../victory-selection-container:build:lib:cjs", "../victory-voronoi-container:build:lib:cjs", "../victory-zoom-container:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-brush-container:build:lib:esm", "../victory-core:build:lib:esm", "../victory-cursor-container:build:lib:esm", "../victory-selection-container:build:lib:esm", "../victory-voronoi-container:build:lib:esm", "../victory-zoom-container:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-brush-container:build:lib:esm", "../victory-core:build:lib:esm", "../victory-cursor-container:build:lib:esm", "../victory-selection-container:build:lib:esm", "../victory-voronoi-container:build:lib:esm", "../victory-zoom-container:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-brush-container:types:create", "../victory-core:types:create", "../victory-cursor-container:types:create", "../victory-selection-container:types:create", "../victory-voronoi-container:types:create", "../victory-zoom-container:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-brush-container:types:create", "../victory-core:types:create", "../victory-cursor-container:types:create", "../victory-selection-container:types:create", "../victory-voronoi-container:types:create", "../victory-zoom-container:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-brush-container:types:create", "../victory-core:types:create", "../victory-cursor-container:types:create", "../victory-selection-container:types:create", "../victory-voronoi-container:types:create", "../victory-zoom-container:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-brush-container:types:create", "../victory-core:types:create", "../victory-cursor-container:types:create", "../victory-selection-container:types:create", "../victory-voronoi-container:types:create", "../victory-zoom-container:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-brush-container:build", "../victory-core:build", "../victory-cursor-container:build", "../victory-selection-container:build", "../victory-voronoi-container:build", "../victory-zoom-container:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-create-container/src/create-container.tsx ================================================ import React from "react"; import forOwn from "lodash/forOwn"; import groupBy from "lodash/groupBy"; import isEmpty from "lodash/isEmpty"; import toPairs from "lodash/toPairs"; import { VictoryZoomContainer, useVictoryZoomContainer, } from "victory-zoom-container"; import { VictorySelectionContainer, useVictorySelectionContainer, } from "victory-selection-container"; import { VictoryContainer, VictoryContainerProps } from "victory-core"; import { VictoryVoronoiContainer, useVictoryVoronoiContainer, } from "victory-voronoi-container"; import { VictoryCursorContainer, useVictoryCursorContainer, } from "victory-cursor-container"; import { VictoryBrushContainer, useVictoryBrushContainer, } from "victory-brush-container"; function ensureArray(thing: T): [] | T | T[] { if (!thing) { return []; } else if (!Array.isArray(thing)) { return [thing]; } return thing; } const combineEventHandlers = (eventHandlersArray: any[]) => { // takes an array of event handler objects and produces one eventHandlers object // creates a custom combinedHandler() for events with multiple conflicting handlers return eventHandlersArray.reduce((localHandlers, finalHandlers) => { forOwn(localHandlers, (localHandler, eventName) => { const existingHandler = finalHandlers[eventName]; if (existingHandler) { // create new handler for event that concats the existing handler's mutations with new ones finalHandlers[eventName] = function combinedHandler(...params) { // named for debug clarity // sometimes handlers return undefined; use empty array instead, for concat() const existingMutations = ensureArray(existingHandler(...params)); const localMutations = ensureArray(localHandler(...params)); return existingMutations.concat(localMutations); }; } else { finalHandlers[eventName] = localHandler; } }); return finalHandlers; }); }; const combineDefaultEvents = (defaultEvents: any[]) => { // takes a defaultEvents array and returns one equal or lesser length, // by combining any events that have the same target const eventsByTarget = groupBy(defaultEvents, "target"); const events = toPairs(eventsByTarget).map(([target, eventsArray]) => { const newEventsArray = eventsArray.filter(Boolean); return isEmpty(newEventsArray) ? null : { target, eventHandlers: combineEventHandlers( eventsArray.map((event) => event.eventHandlers), ), // note: does not currently handle eventKey or childName }; }); return events.filter(Boolean); }; export type ContainerType = | "zoom" | "selection" | "brush" | "cursor" | "voronoi"; /** * Container hooks are used to provide the container logic to the container components through props and a modified children object * - These hooks contain shared logic for both web and Victory Native containers. * - In this utility, we call multiple of these hooks with the props returned by the previous to combine the container logic. */ const CONTAINER_HOOKS = { zoom: useVictoryZoomContainer, selection: useVictorySelectionContainer, brush: useVictoryBrushContainer, cursor: useVictoryCursorContainer, voronoi: useVictoryVoronoiContainer, }; /** * Container hooks are wrappers that return a VictoryContainer with the props provided by their respective hooks, and the modified children. * - These containers are specific to the web. Victory Native has its own container components. * - For this utility, we only need the container components to extract the defaultEvents. */ const CONTAINER_COMPONENTS_WEB = { zoom: VictoryZoomContainer, selection: VictorySelectionContainer, brush: VictoryBrushContainer, cursor: VictoryCursorContainer, voronoi: VictoryVoronoiContainer, }; type ContainerComponents = Record< ContainerType, React.ComponentType & { defaultEvents: (props: any) => any[]; } >; export function makeCreateContainerFunction< TContainerComponents extends ContainerComponents, >( containerComponents: TContainerComponents, VictoryContainerBase: typeof VictoryContainer, ) { type ContainerProps = React.ComponentProps< TContainerComponents[T] >; // Helper type to support backwards compatibility with old types type CombinedContainerProps = A extends ContainerType ? B extends ContainerType ? ContainerProps & ContainerProps // New style: infer from container types : A & B // Old style: expect props as generic types : A & B; return function combineContainers< TContainerA extends ContainerType | VictoryContainerProps, TContainerB extends ContainerType | VictoryContainerProps, >( containerA: TContainerA extends ContainerType ? TContainerA : ContainerType, containerB: TContainerB extends ContainerType ? TContainerB : ContainerType, ) { const ContainerA = containerComponents[containerA as ContainerType]; const ContainerB = containerComponents[containerB as ContainerType]; const useContainerA = CONTAINER_HOOKS[containerA as ContainerType]; const useContainerB = CONTAINER_HOOKS[containerB as ContainerType]; const CombinedContainer = ( props: CombinedContainerProps, ) => { const { children: childrenA, props: propsA } = useContainerA(props); const { children: combinedChildren, props: combinedProps } = useContainerB({ ...propsA, children: childrenA, }); return ( {combinedChildren} ); }; CombinedContainer.displayName = `Victory${containerA}${containerB}Container`; CombinedContainer.role = "container"; CombinedContainer.defaultEvents = ( props: CombinedContainerProps, ) => combineDefaultEvents([ ...ContainerA.defaultEvents(props), ...ContainerB.defaultEvents(props), ]); return CombinedContainer; }; } export const createContainer = makeCreateContainerFunction( CONTAINER_COMPONENTS_WEB, VictoryContainer, ); ================================================ FILE: packages/victory-create-container/src/index.ts ================================================ export * from "./create-container"; ================================================ FILE: packages/victory-create-container/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-create-container/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-cursor-container/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-cursor-container/CHANGELOG.md ================================================ # victory-cursor-container ## 37.3.6 ## 37.3.5 ## 37.3.4 ### Patch Changes - Fix regression in cursor rendering caused by #2999 ([#3001](https://github.com/FormidableLabs/victory/pull/3001)) ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) * Improve types in victory-core helpers ([#2999](https://github.com/FormidableLabs/victory/pull/2999)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Refactor containers and portal to function components ([#2799](https://github.com/FormidableLabs/victory/pull/2799)) * Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash array utils with native code ([#2810](https://github.com/FormidableLabs/victory/pull/2810)) * Replace lodash values and mapValues with native code ([#2808](https://github.com/FormidableLabs/victory/pull/2808)) - Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ### Patch Changes - Fix victory-native component prop types ([#2785](https://github.com/FormidableLabs/victory/pull/2785)) ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Improved the exported types (fixes [#2451](https://github.com/FormidableLabs/victory/issues/2451)) ([#2452](https://github.com/FormidableLabs/victory/pull/2452)) - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Fixed broken import (fixes [#2426](https://github.com/FormidableLabs/victory/issues/2426)) ([#2427](https://github.com/FormidableLabs/victory/pull/2427)) - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - - Removed Template Literal Types to increase TS compatibility (fixes [#2418](https://github.com/FormidableLabs/victory/issues/2418)) ([#2420](https://github.com/FormidableLabs/victory/pull/2420)) - Improved type for `VictoryLabelProps["textAnchor"]` (fixes [#2361](https://github.com/FormidableLabs/victory/issues/2361)) - Fixed exported types for `VictoryAxis`, `VictoryBoxPlot`, `VictoryErrorBar`, and `VictoryScatter` (fixes [#2411](https://github.com/FormidableLabs/victory/issues/2411)) - Migrate `victory-cursor-container` to TS (fixes [#2402](https://github.com/FormidableLabs/victory/issues/2402)) - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-cursor-container/README.md ================================================ # VictoryCursorContainer `victory-cursor-container@^30.0.0` exports `VictoryCursorContainer`, `cursorContainerMixin` and `CursorHelpers` To view documentation for `VictoryCursorContainer` please see https://commerce.nearform.com/open-source/victory/docs/victory-cursor-container To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-cursor-container.md ================================================ FILE: packages/victory-cursor-container/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-cursor-container/package.json ================================================ { "name": "victory-cursor-container", "version": "37.3.6", "description": "Interactive Cursor Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-cursor-container/src/cursor-helpers.tsx ================================================ import { Helpers, Selection, SVGCoordinateType } from "victory-core"; import throttle from "lodash/throttle"; const ON_MOUSE_MOVE_THROTTLE_MS = 16; class CursorHelpersClass { getDimension(props) { const { horizontal, cursorDimension } = props; if (!horizontal || !cursorDimension) { return cursorDimension; } return cursorDimension === "x" ? "y" : "x"; } withinBounds(point, bounds) { const { x1, x2, y1, y2 } = Helpers.mapValues(bounds, Number); const { x, y } = Helpers.mapValues(point, Number); return ( x >= Math.min(x1, x2) && x <= Math.max(x1, x2) && y >= Math.min(y1, y2) && y <= Math.max(y1, y2) ); } private handleMouseMove = (evt, targetProps) => { const { onCursorChange, domain } = targetProps; const cursorDimension = this.getDimension(targetProps); const parentSVG = targetProps.parentSVG || Selection.getParentSVG(evt); const cursorSVGPosition = Selection.getSVGEventCoordinates(evt, parentSVG); let cursorValue: SVGCoordinateType | null = Selection.getDataCoordinates( targetProps, targetProps.scale, cursorSVGPosition.x, cursorSVGPosition.y, ); const inBounds = this.withinBounds(cursorValue, { x1: domain.x[0], x2: domain.x[1], y1: domain.y[0], y2: domain.y[1], }); if (!inBounds) { cursorValue = null; } if (Helpers.isFunction(onCursorChange)) { if (cursorValue) { const value = cursorDimension ? cursorValue[cursorDimension] : cursorValue; onCursorChange(value, targetProps); } else if (cursorValue !== targetProps.cursorValue) { onCursorChange(targetProps.defaultCursorValue || null, targetProps); } } return [ { target: "parent", eventKey: "parent", mutation: () => ({ cursorValue, parentSVG }), }, ]; }; onMouseMove = throttle(this.handleMouseMove, ON_MOUSE_MOVE_THROTTLE_MS, { leading: true, trailing: false, }); onMouseLeave = this.handleMouseMove; onTouchEnd = (evt, targetProps) => { const { onCursorChange } = targetProps; if (Helpers.isFunction(targetProps.onCursorChange)) { onCursorChange(null, targetProps); } return [ { target: "parent", eventKey: "parent", mutation: () => ({ cursorValue: null }), }, ]; }; } export const CursorHelpers = new CursorHelpersClass(); /* { ...CursorHelpers, onMouseMove: throttle( CursorHelpers.onMouseMove.bind(CursorHelpers), ON_MOUSE_MOVE_THROTTLE_MS, { leading: true, trailing: false, }, ), onMouseLeave: CursorHelpers.onMouseMove.bind(CursorHelpers), onTouchEnd: CursorHelpers.onTouchEnd.bind(CursorHelpers), }; */ ================================================ FILE: packages/victory-cursor-container/src/index.tsx ================================================ export * from "./cursor-helpers"; export * from "./victory-cursor-container"; ================================================ FILE: packages/victory-cursor-container/src/victory-cursor-container.tsx ================================================ import React from "react"; import { Helpers, VictoryContainerProps, CoordinatesPropType, VictoryLabelProps, ValueOrAccessor, VictoryLabel, LineSegment, VictoryContainer, VictoryEventHandler, DomainTuple, PaddingProps, } from "victory-core"; import defaults from "lodash/defaults"; import isObject from "lodash/isObject"; import { CursorHelpers } from "./cursor-helpers"; export type CursorCoordinatesPropType = CoordinatesPropType | number; export interface VictoryCursorContainerProps extends VictoryContainerProps { cursorComponent?: React.ReactElement; cursorDimension?: "x" | "y"; cursorLabel?: ValueOrAccessor; cursorLabelComponent?: React.ReactElement; cursorLabelOffset?: CursorCoordinatesPropType; defaultCursorValue?: CursorCoordinatesPropType; disable?: boolean; horizontal?: boolean; padding?: PaddingProps; onCursorChange?: ( value: CursorCoordinatesPropType, props: VictoryCursorContainerProps, ) => void; } interface VictoryCursorContainerMutatedProps extends VictoryCursorContainerProps { cursorValue: CoordinatesPropType | null; domain: { x: DomainTuple; y: DomainTuple }; } export const VICTORY_CURSOR_CONTAINER_DEFAULT_PROPS = { cursorLabelComponent: , cursorLabelOffset: { x: 5, y: -10, }, cursorComponent: , }; export const useVictoryCursorContainer = ( initialProps: VictoryCursorContainerProps, ) => { const props = { ...VICTORY_CURSOR_CONTAINER_DEFAULT_PROPS, ...(initialProps as VictoryCursorContainerMutatedProps), }; const { children } = props; const getCursorPosition = () => { const { cursorValue, defaultCursorValue, domain, cursorDimension } = props; if (cursorValue) { return cursorValue; } if (typeof defaultCursorValue === "number") { return { x: ((domain.x[0] as number) + (domain.x[1] as number)) / 2, y: ((domain.y[0] as number) + (domain.y[1] as number)) / 2, ...(cursorDimension ? { [cursorDimension]: defaultCursorValue } : {}), }; } return defaultCursorValue; }; const getCursorLabelOffset = () => { const { cursorLabelOffset } = props; if (typeof cursorLabelOffset === "number") { return { x: cursorLabelOffset, y: cursorLabelOffset, }; } return cursorLabelOffset; }; const getPadding = () => { if (props.padding === undefined) { const child = Array.isArray(props.children) ? props.children.find((c: any) => { return isObject(c.props) && c.props.padding !== undefined; }) : props.children; return Helpers.getPadding(child?.props?.padding); } return Helpers.getPadding(props.padding); }; const getCursorElements = () => { const { scale, cursorLabelComponent, cursorLabel, cursorComponent, width, height, name, horizontal, theme, } = props; const cursorDimension = CursorHelpers.getDimension(props); const cursorValue = getCursorPosition(); const cursorLabelOffset = getCursorLabelOffset(); if (!cursorValue) { return []; } const newElements: React.ReactElement[] = []; const padding = getPadding(); const cursorCoordinates = scale && "x" in scale && "y" in scale && typeof scale.y === "function" && typeof scale.x === "function" ? { x: horizontal ? scale.y(cursorValue.y) : scale.x(cursorValue.x), y: horizontal ? scale.x(cursorValue.x) : scale.y(cursorValue.y), } : { x: cursorValue.x, y: cursorValue.y, }; if (cursorLabel) { let labelProps = defaults({ active: true }, cursorLabelComponent.props, { x: cursorCoordinates.x + cursorLabelOffset.x, y: cursorCoordinates.y + cursorLabelOffset.y, datum: cursorValue, active: true, key: `${name}-cursor-label`, }); if (Helpers.isTooltip(cursorLabelComponent)) { const tooltipTheme = (theme && theme.tooltip) || {}; labelProps = defaults({}, labelProps, tooltipTheme); } newElements.push( React.cloneElement( cursorLabelComponent, defaults({}, labelProps, { text: Helpers.evaluateProp(cursorLabel, labelProps), }), ), ); } const cursorStyle = Object.assign( { stroke: "black" }, cursorComponent.props.style, ); if (cursorDimension === "x" || cursorDimension === undefined) { newElements.push( React.cloneElement(cursorComponent, { key: `${name}-x-cursor`, x1: cursorCoordinates.x, x2: cursorCoordinates.x, y1: padding.top, y2: (typeof height === "number" ? height : 0) - padding.bottom, style: cursorStyle, }), ); } if (cursorDimension === "y" || cursorDimension === undefined) { newElements.push( React.cloneElement(cursorComponent, { key: `${name}-y-cursor`, x1: padding.left, x2: (typeof width === "number" ? width : 0) - padding.right, y1: cursorCoordinates.y, y2: cursorCoordinates.y, style: cursorStyle, }), ); } return newElements; }; return { props, children: [ ...React.Children.toArray(children), ...getCursorElements(), ] as React.ReactElement[], }; }; export const VictoryCursorContainer = ( initialProps: VictoryCursorContainerProps, ) => { const { props, children } = useVictoryCursorContainer(initialProps); return {children}; }; VictoryCursorContainer.role = "container"; VictoryCursorContainer.defaultEvents = ( initialProps: VictoryCursorContainerProps, ) => { const props = { ...VICTORY_CURSOR_CONTAINER_DEFAULT_PROPS, ...initialProps }; const createEventHandler = (handler: VictoryEventHandler, disabled?: boolean): VictoryEventHandler => // eslint-disable-next-line max-params (event, targetProps, eventKey, context) => disabled || props.disable ? {} : handler(event, { ...props, ...targetProps }, eventKey, context); return [ { target: "parent", eventHandlers: { onMouseLeave: createEventHandler(CursorHelpers.onMouseLeave), onMouseMove: createEventHandler(CursorHelpers.onMouseMove), onTouchMove: createEventHandler(CursorHelpers.onMouseMove), }, }, ]; }; ================================================ FILE: packages/victory-cursor-container/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-cursor-container/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-errorbar/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-errorbar/CHANGELOG.md ================================================ # victory-errorbar ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ### Patch Changes - Remove duplicate types from interfaces ([#2940](https://github.com/FormidableLabs/victory/pull/2940)) ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash isNil and isNan with native code ([#2800](https://github.com/FormidableLabs/victory/pull/2800)) * Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ### Patch Changes - Migrate victory-native to TypeScript ([#2739](https://github.com/FormidableLabs/victory/pull/2739)) ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) * Replace instances of lodash.range with equivalent native code ([#2760](https://github.com/FormidableLabs/victory/pull/2760)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - - Removed Template Literal Types to increase TS compatibility (fixes [#2418](https://github.com/FormidableLabs/victory/issues/2418)) ([#2420](https://github.com/FormidableLabs/victory/pull/2420)) - Improved type for `VictoryLabelProps["textAnchor"]` (fixes [#2361](https://github.com/FormidableLabs/victory/issues/2361)) - Fixed exported types for `VictoryAxis`, `VictoryBoxPlot`, `VictoryErrorBar`, and `VictoryScatter` (fixes [#2411](https://github.com/FormidableLabs/victory/issues/2411)) - Migrate `victory-cursor-container` to TS (fixes [#2402](https://github.com/FormidableLabs/victory/issues/2402)) - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Minor Changes - Migration of victory-errorbar to TypeScript ([#2395](https://github.com/FormidableLabs/victory/pull/2395)) ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-errorbar/README.md ================================================ # VictoryErrorBar `victory-errorbar@^30.0.0` exports `VictoryErrorBar` and `ErrorBar` components To view documentation for `VictoryErrorBar` please see https://commerce.nearform.com/open-source/victory/docs/victory-error-bar To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-errorbar.md ================================================ FILE: packages/victory-errorbar/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-errorbar/package.json ================================================ { "name": "victory-errorbar", "version": "37.3.6", "description": "Error Bar Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6" }, "devDependencies": { "victory-vendor": "*" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-errorbar/src/error-bar.test.tsx ================================================ import { render } from "@testing-library/react"; import forEach from "lodash/forEach"; import omit from "lodash/omit"; import React from "react"; import * as d3Scale from "victory-vendor/d3-scale"; import { ErrorBar } from "./error-bar"; describe("victory-primitives/error-bar", () => { const baseProps = { x: 4, y: 5, errorX: [1, 3], errorY: [0.2, 2], scale: { x: d3Scale.scaleLinear(), y: d3Scale.scaleLinear(), }, borderWidth: 20, }; const compareLineCoordinates = (line, coordinates) => { forEach(coordinates, (coordinateValue, coordinateName) => { expect(parseFloat(line.getAttribute(coordinateName))).toEqual( parseFloat(coordinateValue), ); }); }; it("should render eight lines", () => { const { container } = render(, { wrapper: "svg" as any, }); const lines = container.querySelectorAll("line"); const expectedCoordinates = [ // Right Border (positiveErrorX, positiveErrorX, y - borderWidth, y + borderWidth) { x1: 1, x2: 1, y1: -15, y2: 25 }, // Left Border(negativeErrorX, negativeErrorX, y - borderWidth, y + borderWidth) { x1: 3, x2: 3, y1: -15, y2: 25 }, // Bottom Border(x - borderWidth, x + borderWidth, negativeErrorY, negativeErrorY) { x1: -16, x2: 24, y1: 0.2, y2: 0.2 }, // Top Border(x - borderWidth, x + borderWidth, positiveErrorY, positiveErrorY) { x1: -16, x2: 24, y1: 2, y2: 2 }, // Right Cross(x, positiveErrorX, y, y) { x1: 4, x2: 1, y1: 5, y2: 5 }, // Left Cross(x, negativeErrorX, y, y) { x1: 4, x2: 3, y1: 5, y2: 5 }, // Bottom Cross(x, x, y, negativeErrorY) { x1: 4, x2: 4, y1: 5, y2: 0.2 }, // Bottom Cross(x, x, y, positiveErrorY) { x1: 4, x2: 4, y1: 5, y2: 2 }, ]; expect(lines).toHaveLength(8); lines.forEach((line, i) => { compareLineCoordinates(line, expectedCoordinates[i]); }); }); it("should render four lines when only x error type is supplied", () => { const xErrorProps = omit(baseProps, ["errorY"]); const { container } = render(, { wrapper: "svg" as any, }); const lines = container.querySelectorAll("line"); const expectedCoordinates = [ // Right Border (positiveErrorX, positiveErrorX, y - borderWidth, y + borderWidth) { x1: 1, x2: 1, y1: -15, y2: 25 }, // Left Border(negativeErrorX, negativeErrorX, y - borderWidth, y + borderWidth) { x1: 3, x2: 3, y1: -15, y2: 25 }, // Right Cross(x, positiveErrorX, y, y) { x1: 4, x2: 1, y1: 5, y2: 5 }, // Left Cross(x, negativeErrorX, y, y) { x1: 4, x2: 3, y1: 5, y2: 5 }, ]; expect(lines.length).toEqual(4); lines.forEach((line, i) => { compareLineCoordinates(line, expectedCoordinates[i]); }); }); it("should render four lines when only y error type is supplied", () => { const yErrorProps = omit(baseProps, ["errorX"]); const { container } = render(, { wrapper: "svg" as any, }); const lines = container.querySelectorAll("line"); const expectedCoordinates = [ // Bottom Border(x - borderWidth, x + borderWidth, negativeErrorY, negativeErrorY) { x1: -16, x2: 24, y1: 0.2, y2: 0.2 }, // Top Border(x - borderWidth, x + borderWidth, positiveErrorY, positiveErrorY) { x1: -16, x2: 24, y1: 2, y2: 2 }, // Bottom Cross(x, x, y, negativeErrorY) { x1: 4, x2: 4, y1: 5, y2: 0.2 }, // Bottom Cross(x, x, y, positiveErrorY) { x1: 4, x2: 4, y1: 5, y2: 2 }, ]; expect(lines.length).toEqual(4); lines.forEach((line, i) => { compareLineCoordinates(line, expectedCoordinates[i]); }); }); }); ================================================ FILE: packages/victory-errorbar/src/error-bar.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import { Helpers, Line, UserProps, VictoryCommonPrimitiveProps, EventsMixinClass, } from "victory-core"; const renderBorder = (props, error, type) => { const vertical = type === "right" || type === "left"; return React.cloneElement(props.lineComponent, { ...props.events, role: props.role, shapeRendering: props.shapeRendering, className: props.className, style: props.style, transform: props.transform, key: `${props.id}-border-${type}`, x1: vertical ? error[type] : props.x - props.borderWidth, x2: vertical ? error[type] : props.x + props.borderWidth, y1: vertical ? props.y - props.borderWidth : error[type], y2: vertical ? props.y + props.borderWidth : error[type], "data-type": `border-${type}`, }); }; const renderCross = (props, error, type) => { const vertical = type === "top" || type === "bottom"; return React.cloneElement(props.lineComponent, { ...props.events, role: props.role, shapeRendering: props.shapeRendering, className: props.className, style: props.style, transform: props.transform, key: `${props.id}-cross-${type}`, x1: props.x, x2: vertical ? props.x : error[type], y1: props.y, y2: vertical ? error[type] : props.y, "data-type": `cross-${type}`, }); }; const calculateError = (props) => { const { errorX, errorY } = props; const settings = { right: { error: errorX, errorIndex: 0 }, left: { error: errorX, errorIndex: 1 }, top: { error: errorY, errorIndex: 1 }, bottom: { error: errorY, errorIndex: 0 }, }; const getError = (direction) => { const { error, errorIndex } = settings[direction]; return error ? error[errorIndex] : undefined; }; const result = ["right", "left", "top", "bottom"].reduce((memo, dir) => { memo[dir] = getError(dir); return memo; }, {}); return result; }; const evaluateProps = (props) => { /** * Potential evaluated props are: * `ariaLabel` * `id` * `style` * `tabIndex` */ const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const id = Helpers.evaluateProp(props.id, props); const style = Helpers.evaluateStyle( Object.assign({ stroke: "black" }, props.style), props, ); const tabIndex = Helpers.evaluateProp(props.tabIndex, props); return Object.assign({}, props, { ariaLabel, id, style, tabIndex }); }; export interface ErrorBarProps extends VictoryCommonPrimitiveProps { borderWidth?: number; datum?: any; errorX?: number | any[] | boolean; errorY?: number | any[] | boolean; groupComponent?: React.ReactElement; lineComponent?: React.ReactElement; x?: number; y?: number; } // ErrorProps for calculateError export interface ErrorProps { right?: { error: any; errorIndex: number }; left?: { error: any; errorIndex: number }; bottom?: { error: any; errorIndex: number }; top?: { error: any; errorIndex: number }; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface ErrorBar extends EventsMixinClass {} const defaultProps = { groupComponent: , lineComponent: , role: "presentation", shapeRendering: "auto", }; export const ErrorBar = ( initialProps: ErrorBarProps & typeof ErrorBar.default, ) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); const { groupComponent } = props; const userProps = UserProps.getSafeUserProps(props); const { tabIndex, ariaLabel } = props; const error: ErrorProps = calculateError(props); const children = [ error.right ? renderBorder(props, error, "right") : null, error.left ? renderBorder(props, error, "left") : null, error.bottom ? renderBorder(props, error, "bottom") : null, error.top ? renderBorder(props, error, "top") : null, error.right ? renderCross(props, error, "right") : null, error.left ? renderCross(props, error, "left") : null, error.bottom ? renderCross(props, error, "bottom") : null, error.top ? renderCross(props, error, "top") : null, ].filter(Boolean); return React.cloneElement( groupComponent, { tabIndex, "aria-label": ariaLabel, ...userProps }, children, ); }; ================================================ FILE: packages/victory-errorbar/src/helper-methods.tsx ================================================ import assign from "lodash/assign"; import defaults from "lodash/defaults"; import { Helpers, LabelHelpers, Scale, Domain, Data, Collection, } from "victory-core"; const getErrors = (props, datum, axis) => { /** * check if it is asymmetric error or symmetric error, asymmetric error should be an array * and the first value is the positive error, the second is the negative error * @param {Boolean} isArray(errorX) * @return {String or Array} */ const errorNames = { x: "_errorX", y: "_errorY" }; const errors = datum[errorNames[axis]]; if (errors === 0) { return false; } const scale = props.scale[axis]; return Array.isArray(errors) ? [ errors[0] === 0 ? false : scale(errors[0] + datum[`_${axis}`]), errors[1] === 0 ? false : scale(datum[`_${axis}`] - errors[1]), ] : [scale(errors + datum[`_${axis}`]), scale(datum[`_${axis}`] - errors)]; }; export const getData = (props) => { const accessorTypes = ["x", "y", "errorX", "errorY"]; if (props.data) { return Data.formatData(props.data, props, accessorTypes); } const generatedData = props.errorX || props.errorY ? Data.generateData(props) : []; return Data.formatData(generatedData, props, accessorTypes); }; const getDomainFromData = (props, axis) => { const minDomain = Domain.getMinFromProps(props, axis); const maxDomain = Domain.getMaxFromProps(props, axis); const dataset = getData(props); if (dataset.length < 1) { return minDomain !== undefined && maxDomain !== undefined ? Domain.getDomainFromMinMax(minDomain, maxDomain) : undefined; } const error = axis === "x" ? "_errorX" : "_errorY"; const reduceErrorData = (type) => { const baseCondition = type === "min" ? Infinity : -Infinity; const errorIndex = type === "min" ? 1 : 0; const sign = type === "min" ? -1 : 1; return dataset.reduce((memo, datum) => { const currentError = Array.isArray(datum[error]) ? datum[error][errorIndex] : datum[error]; const current = datum[`_${axis}`] + sign * (currentError || 0); return (memo < current && type === "min") || (memo > current && type === "max") ? memo : current; }, baseCondition); }; const min = minDomain !== undefined ? minDomain : reduceErrorData("min"); const max = maxDomain !== undefined ? maxDomain : reduceErrorData("max"); return Domain.getDomainFromMinMax(min, max); }; export const getDomain = (props, axis) => { return Domain.createDomainFunction(getDomainFromData)(props, axis); }; // This method will edit or remove errorbar data points that fall outside of the desired domain const formatDataFromDomain = (datum, domain) => { const minDomainX = Collection.getMinValue(domain.x); const maxDomainX = Collection.getMaxValue(domain.x); const minDomainY = Collection.getMinValue(domain.y); const maxDomainY = Collection.getMaxValue(domain.y); let { _x, _y } = datum; // if either x or y center point is outside of the domain, null the entire data point if (_x < minDomainX || _x > maxDomainX || _y < minDomainY || _y > maxDomainY) _x = _y = null; return Object.assign({}, datum, { _x, _y }); }; const getCalculatedValues = (props) => { const defaultStyles = Helpers.getDefaultStyles(props, "errorbar"); const style = Helpers.getStyles(props.style, defaultStyles) || {}; const data = getData(props); const range = { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; const domain = { x: getDomain(props, "x"), y: getDomain(props, "y"), }; const scale = { x: Scale.getBaseScale(props, "x") .domain(domain.x) .range(props.horizontal ? range.y : range.x), y: Scale.getBaseScale(props, "y") .domain(domain.y) .range(props.horizontal ? range.x : range.y), }; const origin = props.polar ? props.origin || Helpers.getPolarOrigin(props) : undefined; return { domain, data, scale, style, origin }; }; const getLabelProps = (dataProps, text, style) => { const { x, y, index, scale, errorY, errorX, horizontal, labelComponent, theme, disableInlineStyles, } = dataProps; const getError = (type = "x") => { const baseError = type === "y" ? errorY : errorX; const error = baseError && Array.isArray(baseError) ? baseError[0] : baseError; return error || dataProps[type]; }; const labelStyle = style.labels || {}; const padding = labelStyle.padding || 0; const textAnchor = horizontal ? "start" : "middle"; const verticalAnchor = horizontal ? "middle" : "end"; const labelProps = { style: labelStyle, y: horizontal ? y : getError("y"), x: horizontal ? getError("x") : x, dy: horizontal ? 0 : -padding, dx: horizontal ? padding : 0, text, index, scale, datum: dataProps.datum, data: dataProps.data, textAnchor: labelStyle.textAnchor || textAnchor, verticalAnchor: labelStyle.verticalAnchor || verticalAnchor, angle: labelStyle.angle, horizontal, disableInlineStyles, }; if (!Helpers.isTooltip(labelComponent)) { return labelProps; } const tooltipTheme = (theme && theme.tooltip) || {}; return defaults({}, labelProps, Helpers.omit(tooltipTheme, ["style"])); }; export const getBaseProps = (initialProps, fallbackProps) => { const modifiedProps = Helpers.modifyProps( initialProps, fallbackProps, "errorbar", ); const props = Object.assign( {}, modifiedProps, getCalculatedValues(modifiedProps), ); const { borderWidth, data, domain, events, groupComponent, height, horizontal, labels, name, origin, padding, polar, scale, sharedEvents, standalone, style, theme, width, disableInlineStyles, } = props; const initialChildProps = { parent: { data, domain, height, horizontal, name, origin, padding, polar, scale, standalone, style: style.parent, theme, width, }, }; const DEFAULT_BORDER_WIDTH = 8; return data.reduce((childProps, datum, index) => { const eventKey = !Helpers.isNil(datum.eventKey) ? datum.eventKey : index; const { x, y } = Helpers.scalePoint(assign({}, props, { scale }), datum); const formattedDatum = formatDataFromDomain(datum, domain); const errorX = getErrors(props, formattedDatum, "x"); const errorY = getErrors(props, formattedDatum, "y"); const dataProps = { borderWidth: borderWidth ? borderWidth : DEFAULT_BORDER_WIDTH, data, datum: formattedDatum, errorX: horizontal ? errorY : errorX, errorY: horizontal ? errorX : errorY, groupComponent, horizontal, index, scale, style: disableInlineStyles ? {} : style.data, x, y, disableInlineStyles, }; childProps[eventKey] = { data: dataProps, }; const text = LabelHelpers.getText(props, datum, index); if ( (text !== undefined && text !== null) || (labels && (events || sharedEvents)) ) { childProps[eventKey].labels = getLabelProps( Object.assign({}, props, dataProps), text, style, ); } return childProps; }, initialChildProps); }; ================================================ FILE: packages/victory-errorbar/src/index.ts ================================================ export * from "./victory-errorbar"; export * from "./error-bar"; ================================================ FILE: packages/victory-errorbar/src/victory-errorbar.tsx ================================================ import React from "react"; import { Helpers, VictoryLabel, addEvents, VictoryContainer, VictoryTheme, DefaultTransitions, UserProps, EventPropTypeInterface, StringOrNumberOrCallback, StringOrNumberOrList, VictoryDatableProps, VictoryCommonProps, VictoryMultiLabelableProps, VictoryStyleInterface, EventsMixinClass, } from "victory-core"; import { ErrorBar } from "./error-bar"; import { getBaseProps, getDomain, getData } from "./helper-methods"; const fallbackProps = { width: 450, height: 300, padding: 50, }; const defaultData = [ { x: 1, y: 1, errorX: 0.1, errorY: 0.1 }, { x: 2, y: 2, errorX: 0.2, errorY: 0.2 }, { x: 3, y: 3, errorX: 0.3, errorY: 0.3 }, { x: 4, y: 4, errorX: 0.4, errorY: 0.4 }, ]; export type VictoryErrorBarTTargetType = "data" | "labels" | "parent"; export type ErrorType = | StringOrNumberOrList | ((...args: any[]) => StringOrNumberOrList); export interface VictoryErrorBarProps extends Omit, VictoryDatableProps, VictoryMultiLabelableProps { borderWidth?: number; errorX?: ErrorType; errorY?: ErrorType; events?: EventPropTypeInterface< VictoryErrorBarTTargetType, StringOrNumberOrCallback >[]; style?: VictoryStyleInterface; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryErrorBarBase extends EventsMixinClass {} class VictoryErrorBarBase extends React.Component { static animationWhitelist = [ "data", "domain", "height", "padding", "samples", "style", "width", "errorX", "errorY", "borderWidth", ]; static displayName = "VictoryErrorBar"; static role = "errorbar"; static defaultTransitions = DefaultTransitions.discreteTransitions(); static defaultProps: VictoryErrorBarProps = { containerComponent: , data: defaultData, dataComponent: , labelComponent: , groupComponent: , samples: 50, sortOrder: "ascending", standalone: true, theme: VictoryTheme.grayscale, }; static getDomain(props, axis) { return getDomain(props, axis); } static getData(props) { return getData(props); } static getBaseProps(props) { return getBaseProps(props, fallbackProps); } static expectedComponents = [ "dataComponent", "labelComponent", "groupComponent", "containerComponent", ]; // Overridden in native versions shouldAnimate() { return !!this.props.animate; } render(): React.ReactElement { const { animationWhitelist, role } = VictoryErrorBar; const props = Helpers.modifyProps(this.props, fallbackProps, role); if (this.shouldAnimate()) { return this.animateComponent(props, animationWhitelist); } const children = this.renderData(props); const component = props.standalone ? this.renderContainer(props.containerComponent, children) : children; return UserProps.withSafeUserProps(component, props); } } export const VictoryErrorBar = addEvents(VictoryErrorBarBase); ================================================ FILE: packages/victory-errorbar/src/victory-errorbars.test.tsx ================================================ import React from "react"; import { fireEvent, render, screen } from "@testing-library/react"; import { fromJS } from "immutable"; import { Helpers } from "victory-core"; import * as d3Scale from "victory-vendor/d3-scale"; import { ErrorBar } from "./error-bar"; import { VictoryErrorBar } from "./victory-errorbar"; const defaultProps = { dataComponent: ( props.datum._x} data-y={(props) => props.datum._y} /> ), }; const getCoordinatesForLineWithType = (node, type) => { const line = node.querySelector(`line[data-type="${type}"]`); return ["x1", "x2", "y1", "y2"].map((attr) => parseFloat(line.getAttribute(attr)), ); }; describe("components/victory-errorbar", () => { describe("default component rendering", () => { it("accepts user props", () => { render( , ); expect(screen.getByTestId("victory-errorbar")).toBeDefined(); expect(screen.getByLabelText("Chart")).toBeDefined(); }); it("renders an svg with the correct width and height", () => { const { container } = render(); const svg = container.querySelector("svg"); expect(svg!.style.width).toEqual("100%"); expect(svg!.style.height).toEqual("100%"); }); it("renders an svg with the correct viewBox", () => { const { container } = render(); const svg = container.querySelector("svg"); const viewBoxValue = `0 0 ${450} ${300}`; expect(svg!.getAttribute("viewBox")).toEqual(viewBoxValue); }); it("renders 4 errors", () => { render(); const errorbars = screen.getAllByTestId("error-bar"); expect(errorbars.length).toEqual(4); }); }); it("does not render data with null x or y values", () => { const data = [ { x: 15, y: 35, errorX: 1, errorY: 3 }, { x: null, y: 42, errorX: 3, errorY: 2 }, { x: 25, y: null, errorX: 5, errorY: 5 }, ]; render(); expect(screen.getAllByTestId("error-bar")).toHaveLength(1); }); const immutableRenderDataTest = { createData: (x) => fromJS(x), testLabel: "with immutable data", }; const renderDataTest = { createData: (x) => x, testLabel: "with js data", }; [renderDataTest, immutableRenderDataTest].forEach( ({ createData, testLabel }) => { describe(`symmetric error, rendering data ${testLabel}`, () => { it("renders injected errors for {x, y}", () => { const data = createData( Helpers.range(10).map((i) => ({ x: i, y: i, errorX: 0.1, errorY: 0.2, })), ); render(); const errors = screen.getAllByTestId("error-bar"); expect(errors).toHaveLength(10); }); it("renders errors for {x, y}", () => { const data = createData( Helpers.range(10).map((i) => ({ x: i, y: i, errorX: 0.1, errorY: 0.2, })), ); render(); const errors = screen.getAllByTestId("error-bar"); expect(errors.length).toEqual(10); }); it("sorts data by sortKey getAttribute", () => { const data = createData( Helpers.range(5) .map((i) => ({ x: i, y: i, errorX: 0.1, errorY: 0.2 })) .reverse(), ); render(); const xValues = screen .getAllByTestId("error-bar") .map((node) => parseInt(node.getAttribute("data-x")!)); expect(xValues).toEqual([0, 1, 2, 3, 4]); }); it("reversed sorted data with the sortOrder getAttribute", () => { const data = createData( Helpers.range(5) .map((i) => ({ x: i, y: i, errorX: 0.1, errorY: 0.2 })) .reverse(), ); render( , ); const yValues = screen .getAllByTestId("error-bar") .map((node) => parseInt(node.getAttribute("data-y")!)); expect(yValues).toEqual([4, 3, 2, 1, 0]); }); it("renders errors with error bars, check total svg lines", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const { container } = render( , ); expect(container.querySelectorAll("line")).toHaveLength(24); }); it("should check right border of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const borderWidth = 10; const data = [ { x: 0, y: 0, errorX: 0.1, errorY: 0.2 }, { x: 2, y: 3, errorX: 0.1, errorY: 0.2 }, { x: 5, y: 5, errorX: 0.1, errorY: 0.2 }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.1, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.2, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); bars.forEach((node, i) => { const errorX = xScale(data[i].x + data[i].errorX); const xScaleMax = xScale.range()[1]; const positiveErrorX = errorX >= xScaleMax ? xScaleMax : errorX; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "border-right", ); expect(x1).toEqual(positiveErrorX); expect(x2).toEqual(positiveErrorX); expect(y1).toEqual(yScale(data[i].y) - borderWidth); expect(y2).toEqual(yScale(data[i].y) + borderWidth); }); }); it("should check left border of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const borderWidth = 10; const data = [ { x: 0, y: 0, errorX: 0.1, errorY: 0.2 }, { x: 2, y: 3, errorX: 0.1, errorY: 0.2 }, { x: 5, y: 5, errorX: 0.1, errorY: 0.2 }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.1, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.2, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); bars.forEach((node, i) => { const errorX = xScale(data[i].x - data[i].errorX); const xScaleMin = xScale.range()[0]; const negativeErrorX = errorX <= xScaleMin ? xScaleMin : errorX; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "border-left", ); expect(x1).toEqual(negativeErrorX); expect(x2).toEqual(negativeErrorX); expect(y1).toEqual(yScale(data[i].y) - borderWidth); expect(y2).toEqual(yScale(data[i].y) + borderWidth); }); }); it("should check bottom border of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const borderWidth = 10; const data = [ { x: 0, y: 0, errorX: 0.1, errorY: 0.2 }, { x: 2, y: 3, errorX: 0.1, errorY: 0.2 }, { x: 5, y: 5, errorX: 0.1, errorY: 0.2 }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.1, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.2, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); bars.forEach((node, i) => { const errorY = yScale(data[i].y + data[i].errorY); const yScaleMin = yScale.range()[1]; const negativeErrorY = errorY <= yScaleMin ? yScaleMin : errorY; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "border-bottom", ); expect(x1).toEqual(xScale(data[i].x) - borderWidth); expect(x2).toEqual(xScale(data[i].x) + borderWidth); expect(y1).toEqual(negativeErrorY); expect(y2).toEqual(negativeErrorY); }); }); it("should check top border of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const borderWidth = 10; const data = [ { x: 0, y: 0, errorX: 0.1, errorY: 0.2 }, { x: 2, y: 3, errorX: 0.1, errorY: 0.2 }, { x: 5, y: 5, errorX: 0.1, errorY: 0.2 }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.1, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.2, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorY = yScale(data[i].y - data[i].errorY); const yScaleMax = yScale.range()[0]; const positiveErrorY = errorY >= yScaleMax ? yScaleMax : errorY; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "border-top", ); expect(x1).toEqual(xScale(data[i].x) - borderWidth); expect(x2).toEqual(xScale(data[i].x) + borderWidth); expect(y1).toEqual(positiveErrorY); expect(y2).toEqual(positiveErrorY); }); }); it("should check top cross line of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const data = [ { x: 0, y: 0, errorX: 0.1, errorY: 0.2 }, { x: 2, y: 3, errorX: 0.1, errorY: 0.2 }, { x: 5, y: 5, errorX: 0.1, errorY: 0.2 }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.1, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.2, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorY = yScale(data[i].y - data[i].errorY); const yScaleMax = yScale.range()[0]; const positiveErrorY = errorY >= yScaleMax ? yScaleMax : errorY; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "cross-top", ); expect(x1).toEqual(xScale(data[i].x)); expect(x2).toEqual(xScale(data[i].x)); expect(y1).toEqual(yScale(data[i].y)); expect(y2).toEqual(positiveErrorY); }); }); it("should check bottom cross line of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const data = [ { x: 0, y: 0, errorX: 0.1, errorY: 0.2 }, { x: 2, y: 3, errorX: 0.1, errorY: 0.2 }, { x: 5, y: 5, errorX: 0.1, errorY: 0.2 }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.1, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.2, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorY = yScale(data[i].y + data[i].errorY); const yScaleMin = yScale.range()[1]; const negativeErrorY = errorY <= yScaleMin ? yScaleMin : errorY; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "cross-bottom", ); expect(x1).toEqual(xScale(data[i].x)); expect(x2).toEqual(xScale(data[i].x)); expect(y1).toEqual(yScale(data[i].y)); expect(y2).toEqual(negativeErrorY); }); }); it("should check left cross line of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const data = [ { x: 0, y: 0, errorX: 0.1, errorY: 0.2 }, { x: 2, y: 3, errorX: 0.1, errorY: 0.2 }, { x: 5, y: 5, errorX: 0.1, errorY: 0.2 }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.1, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.2, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorX = xScale(data[i].x - data[i].errorX); const xScaleMin = xScale.range()[0]; const negativeErrorX = errorX <= xScaleMin ? xScaleMin : errorX; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "cross-left", ); expect(x1).toEqual(xScale(data[i].x)); expect(x2).toEqual(negativeErrorX); expect(y1).toEqual(yScale(data[i].y)); expect(y2).toEqual(yScale(data[i].y)); }); }); it("should check right cross line of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const data = [ { x: 0, y: 0, errorX: 0.1, errorY: 0.2 }, { x: 2, y: 3, errorX: 0.1, errorY: 0.2 }, { x: 5, y: 5, errorX: 0.1, errorY: 0.2 }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.1, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.2, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorX = xScale(data[i].errorX + data[i].x); const xScaleMax = xScale.range()[1]; const positiveErrorX = errorX >= xScaleMax ? xScaleMax : errorX; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "cross-right", ); expect(x1).toEqual(xScale(data[i].x)); expect(x2).toEqual(positiveErrorX); expect(y1).toEqual(yScale(data[i].y)); expect(y2).toEqual(yScale(data[i].y)); }); }); }); describe(`asymmetric error, rendering data ${testLabel}`, () => { it("renders injected errors for {x, y}", () => { const data = createData( Helpers.range(10).map((i) => ({ x: i, y: i, errorX: [0.1, 0.2], errorY: [0.2, 0.5], })), ); render(); const errors = screen.getAllByTestId("error-bar"); expect(errors).toHaveLength(10); }); it("renders errors for {x, y}", () => { const data = createData( Helpers.range(10).map((i) => ({ x: i, y: i, errorX: [0.1, 0.2], errorY: [0.2, 1], })), ); render(); const errors = screen.getAllByTestId("error-bar"); expect(errors).toHaveLength(10); }); it("renders errors with error bars, check total svg lines", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const { container } = render( , ); expect(container.querySelectorAll("line")).toHaveLength(24); }); it("should check right border of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const borderWidth = 10; const data = [ { x: 0, y: 0, errorX: [0.1, 0.3], errorY: [0.2, 0.5] }, { x: 2, y: 3, errorX: [0.1, 0.2], errorY: [0.2, 0.3] }, { x: 5, y: 5, errorX: [0.1, 0.6], errorY: [0.2, 0.1] }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.3, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.5, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorX = xScale(data[i].x + data[i].errorX[0]); const xScaleMax = xScale.range()[1]; const positiveErrorX = errorX >= xScaleMax ? xScaleMax : errorX; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "border-right", ); expect(x1).toEqual(positiveErrorX); expect(x2).toEqual(positiveErrorX); expect(y1).toEqual(yScale(data[i].y) - borderWidth); expect(y2).toEqual(yScale(data[i].y) + borderWidth); }); }); it("should check left border of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const borderWidth = 10; const data = [ { x: 0, y: 0, errorX: [0.1, 0.3], errorY: [0.2, 0.5] }, { x: 2, y: 3, errorX: [0.1, 0.2], errorY: [0.2, 0.3] }, { x: 5, y: 5, errorX: [0.1, 0.6], errorY: [0.2, 0.1] }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.3, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.5, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorX = xScale(data[i].x - data[i].errorX[1]); const xScaleMin = xScale.range()[0]; const negativeErrorX = errorX <= xScaleMin ? xScaleMin : errorX; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "border-left", ); expect(x1).toEqual(negativeErrorX); expect(x2).toEqual(negativeErrorX); expect(y1).toEqual(yScale(data[i].y) - borderWidth); expect(y2).toEqual(yScale(data[i].y) + borderWidth); }); }); it("should check bottom border of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const borderWidth = 10; const data = [ { x: 0, y: 0, errorX: [0.1, 0.3], errorY: [0.2, 0.5] }, { x: 2, y: 3, errorX: [0.1, 0.2], errorY: [0.2, 0.3] }, { x: 5, y: 5, errorX: [0.1, 0.6], errorY: [0.2, 0.1] }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.3, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.5, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorY = yScale(data[i].y + data[i].errorY[0]); const yScaleMin = yScale.range()[1]; const negativeErrorY = errorY <= yScaleMin ? yScaleMin : errorY; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "border-bottom", ); expect(x1).toEqual(xScale(data[i].x) - borderWidth); expect(x2).toEqual(xScale(data[i].x) + borderWidth); expect(y1).toEqual(negativeErrorY); expect(y2).toEqual(negativeErrorY); }); }); it("should check top border of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const borderWidth = 10; const data = [ { x: 0, y: 0, errorX: [0.1, 0.3], errorY: [0.2, 0.5] }, { x: 2, y: 3, errorX: [0.1, 0.2], errorY: [0.2, 0.3] }, { x: 5, y: 5, errorX: [0.1, 0.6], errorY: [0.2, 0.1] }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.3, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.5, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorY = yScale(data[i].y - data[i].errorY[1]); const yScaleMax = yScale.range()[0]; const positiveErrorY = errorY >= yScaleMax ? yScaleMax : errorY; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "border-top", ); expect(x1).toEqual(xScale(data[i].x) - borderWidth); expect(x2).toEqual(xScale(data[i].x) + borderWidth); expect(y1).toEqual(positiveErrorY); expect(y2).toEqual(positiveErrorY); }); }); it("should check top cross line of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const data = [ { x: 0, y: 0, errorX: [0.1, 0.3], errorY: [0.2, 0.5] }, { x: 2, y: 3, errorX: [0.1, 0.2], errorY: [0.2, 0.3] }, { x: 5, y: 5, errorX: [0.1, 0.6], errorY: [0.2, 0.1] }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.3, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.5, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorY = yScale(data[i].y - data[i].errorY[1]); const yScaleMax = yScale.range()[0]; const positiveErrorY = errorY >= yScaleMax ? yScaleMax : errorY; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "cross-top", ); expect(x1).toEqual(xScale(data[i].x)); expect(x2).toEqual(xScale(data[i].x)); expect(y1).toEqual(yScale(data[i].y)); expect(y2).toEqual(positiveErrorY); }); }); it("should check bottom cross line of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const data = [ { x: 0, y: 0, errorX: [0.1, 0.3], errorY: [0.2, 0.5] }, { x: 2, y: 3, errorX: [0.1, 0.2], errorY: [0.2, 0.3] }, { x: 5, y: 5, errorX: [0.1, 0.6], errorY: [0.2, 0.1] }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.3, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.5, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorY = yScale(data[i].y + data[i].errorY[0]); const yScaleMin = yScale.range()[1]; const negativeErrorY = errorY <= yScaleMin ? yScaleMin : errorY; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "cross-bottom", ); expect(x1).toEqual(xScale(data[i].x)); expect(x2).toEqual(xScale(data[i].x)); expect(y1).toEqual(yScale(data[i].y)); expect(y2).toEqual(negativeErrorY); }); }); it("should check left cross line of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const data = [ { x: 0, y: 0, errorX: [0.1, 0.3], errorY: [0.2, 0.5] }, { x: 2, y: 3, errorX: [0.1, 0.2], errorY: [0.2, 0.3] }, { x: 5, y: 5, errorX: [0.1, 0.6], errorY: [0.2, 0.1] }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.3, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.5, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorX = xScale(data[i].x - data[i].errorX[1]); const xScaleMin = xScale.range()[0]; const negativeErrorX = errorX <= xScaleMin ? xScaleMin : errorX; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "cross-left", ); expect(x1).toEqual(xScale(data[i].x)); expect(x2).toEqual(negativeErrorX); expect(y1).toEqual(yScale(data[i].y)); expect(y2).toEqual(yScale(data[i].y)); }); }); it("should check right cross line of error bars positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const data = [ { x: 0, y: 0, errorX: [0.1, 0.3], errorY: [0.2, 0.5] }, { x: 2, y: 3, errorX: [0.1, 0.2], errorY: [0.2, 0.3] }, { x: 5, y: 5, errorX: [0.1, 0.6], errorY: [0.2, 0.1] }, ]; render( , ); const xScale = d3Scale .scaleLinear() .domain([-0.3, 5.1]) .range([ svgDimensions.padding, svgDimensions.width - svgDimensions.padding, ]); const yScale = d3Scale .scaleLinear() .domain([-0.5, 5.2]) .range([ svgDimensions.height - svgDimensions.padding, svgDimensions.padding, ]); const bars = screen.getAllByTestId("error-bar"); expect(bars).toHaveLength(3); bars.forEach((node, i) => { const errorX = xScale(data[i].x + data[i].errorX[0]); const xScaleMax = xScale.range()[1]; const positiveErrorX = errorX >= xScaleMax ? xScaleMax : errorX; const [x1, x2, y1, y2] = getCoordinatesForLineWithType( node, "cross-right", ); expect(x1).toEqual(xScale(data[i].x)); expect(x2).toEqual(positiveErrorX); expect(y1).toEqual(yScale(data[i].y)); expect(y2).toEqual(yScale(data[i].y)); }); }); }); }, ); describe("event handling", () => { it("attaches an event to the parent svg", () => { const clickHandler = jest.fn(); const { container } = render( , ); const svg = container.querySelector("svg"); if (svg) fireEvent.click(svg); expect(clickHandler).toBeCalled(); }); it("attaches an event to data, click border lines", () => { const clickHandler = jest.fn(); render( , ); const bars = screen.getAllByTestId("error-bar"); bars.forEach((node) => { // click the border line fireEvent.click(node.querySelectorAll("line")[3]); expect(clickHandler).toBeCalled(); }); }); it("attaches an event to data, click cross lines", () => { const clickHandler = jest.fn(); render( , ); const bars = screen.getAllByTestId("error-bar"); bars.forEach((node) => { // click the cross line fireEvent.click(node.querySelectorAll("line")[7]); expect(clickHandler).toBeCalled(); }); }); describe("accessibility", () => { it("adds an aria label-label and tabIndex to Error Bar primitive", () => { const data = [ { x: 35, y: 50, error: 0.2 }, { x: 10, y: 43, error: 0.15 }, { x: 45, y: 65, error: 0.5 }, ]; const { container } = render( `error bar chart, x ${datum.x}`} tabIndex={({ index }) => index + 2} /> } />, ); expect(container.querySelectorAll("g")).toHaveLength(4); expect(screen.getAllByTestId("error-bar")).toHaveLength(3); screen.getAllByTestId("error-bar").forEach((g, i) => { expect(g.getAttribute("aria-label")).toEqual( `error bar chart, x ${data[i].x}`, ); expect(parseInt(g.getAttribute("tabindex")!, 10)).toEqual(i + 2); }); }); }); }); }); ================================================ FILE: packages/victory-errorbar/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-errorbar/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-group/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-group/CHANGELOG.md ================================================ # victory-group ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) * Improve types in victory-core helpers ([#2999](https://github.com/FormidableLabs/victory/pull/2999)) ## 37.3.2 ## 37.3.1 ### Patch Changes - Remove duplicate types from interfaces ([#2940](https://github.com/FormidableLabs/victory/pull/2940)) ## 37.3.0 ## 37.2.0 ### Minor Changes - Minor updates for clean theme ([#2909](https://github.com/FormidableLabs/victory/pull/2909)) ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ## 36.8.2 ### Patch Changes - Fix victory-group animation ([#2717](https://github.com/FormidableLabs/victory/pull/2717)) ## 36.8.1 ## 36.8.0 ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 - victory-shared-events@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 - victory-shared-events@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 - victory-shared-events@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 - victory-shared-events@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 - victory-shared-events@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 - victory-shared-events@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 - victory-shared-events@36.6.2 ## 36.6.1 ### Patch Changes - - Migrate `victory-group` to TypeScript ([#2422](https://github.com/FormidableLabs/victory/pull/2422)) - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 - victory-shared-events@36.6.1 ## 36.6.0 ### Patch Changes - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 - victory-shared-events@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-group/README.md ================================================ # VictoryGroup `victory-group@^30.0.0` exports `VictoryGroup` To view documentation for `VictoryGroup` please see https://commerce.nearform.com/open-source/victory/docs/victory-group To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-group.md ================================================ FILE: packages/victory-group/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-group/package.json ================================================ { "name": "victory-group", "version": "37.3.6", "description": "Group Layout Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Lauren Eastridge", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", "victory-core": "37.3.6", "victory-shared-events": "37.3.6" }, "devDependencies": { "victory-bar": "*" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-shared-events:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs", "../victory-shared-events:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-shared-events:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-shared-events:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-shared-events:types:create", "../victory-bar:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create", "../victory-shared-events:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-shared-events:types:create", "../victory-bar:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-shared-events:types:create", "../victory-bar:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-shared-events:build", "../victory-bar:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-group/src/helper-methods.tsx ================================================ import React from "react"; import { Data, Helpers, Scale, Wrapper } from "victory-core"; import isEqual from "react-fast-compare"; const fallbackProps = { width: 450, height: 300, padding: 50, offset: 0, }; export function getCalculatedProps(initialProps, childComponents) { const role = "group"; const props = Helpers.modifyProps(initialProps, fallbackProps, role); const style = Wrapper.getStyle(props.theme, props.style, role); const { offset, colorScale, color, polar, horizontal } = props; const categories = props.categories || Wrapper.getCategories(props, childComponents, null); const datasets = props.datasets || Wrapper.getDataFromChildren(props, null); const domain = { x: Wrapper.getDomain( Object.assign({}, props, { categories }), "x", childComponents, ), y: Wrapper.getDomain( Object.assign({}, props, { categories }), "y", childComponents, ), }; const range = props.range || { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; const baseScale = { x: Scale.getScaleFromProps(props, "x") || Wrapper.getScale(props, "x"), y: Scale.getScaleFromProps(props, "y") || Wrapper.getScale(props, "y"), }; const scale = { x: baseScale.x.domain(domain.x).range(props.horizontal ? range.y : range.x), y: baseScale.y.domain(domain.y).range(props.horizontal ? range.x : range.y), }; const origin = polar ? props.origin : Helpers.getPolarOrigin(props); const padding = Helpers.getPadding(props.padding); return { datasets, categories, range, domain, horizontal, scale, style, colorScale, color, offset, origin, padding, }; } // We need to remove sharedEvents in order to memoize the calculated data // With shared events, the props change on every event, and every value is re-calculated const withoutSharedEvents = (props) => { const { children } = props; const modifiedChildren = React.Children.toArray(children).map( (child: Record | any) => { return { ...child, props: Helpers.omit(child.props, ["sharedEvents"]), }; }, ); props.children = modifiedChildren; return props; }; export function useMemoizedProps(initialProps) { const modifiedProps = withoutSharedEvents(initialProps); const [props, setProps] = React.useState(modifiedProps); // React.memo uses shallow equality to compare objects. This way props // will only be re-calculated when they change. React.useEffect(() => { if (!isEqual(modifiedProps, props)) { setProps(modifiedProps); } }, [props, setProps, modifiedProps]); return React.useMemo(() => { return getCalculatedProps(props, props.children); }, [props]); } function pixelsToValue(props, axis, calculatedProps) { if (!props.offset) { return 0; } const currentAxis = Helpers.getCurrentAxis(axis, props.horizontal); const domain = calculatedProps.domain[axis]; const range = calculatedProps.range[currentAxis]; const domainExtent = Math.max(...domain) - Math.min(...domain); const rangeExtent = Math.max(...range) - Math.min(...range); return (domainExtent / rangeExtent) * props.offset; } // eslint-disable-next-line max-params function getX0(props, calculatedProps, index, role) { const groupLength = role === "stack" ? calculatedProps.datasets[0].length : calculatedProps.datasets.length; const center = (groupLength - 1) / 2; const totalWidth = pixelsToValue(props, "x", calculatedProps); return (index - center) * totalWidth; } // eslint-disable-next-line max-params function getPolarX0(props, calculatedProps, index, role) { const groupLength = role === "stack" ? calculatedProps.datasets[0].length : calculatedProps.datasets.length; const center = (groupLength - 1) / 2; const width = getAngularWidth(props, calculatedProps); return (index - center) * width; } function getAngularWidth(props, calculatedProps) { const { range } = calculatedProps; const angularRange = Math.abs(range.x[1] - range.x[0]); const r = Math.max(...range.y); return (props.offset / (2 * Math.PI * r)) * angularRange; } function getLabels(props, datasets, index) { if (!props.labels) { return undefined; } return Math.floor(datasets.length / 2) === index ? props.labels : undefined; } function getChildProps(props, calculatedProps) { const { categories, domain, range, scale, horizontal, origin, padding } = calculatedProps; const { width, height, theme, polar } = props; return { height, width, theme, polar, origin, categories, domain, range, scale, horizontal, padding, standalone: false, }; } function getColorScale(props, child) { const role = child.type && child.type.role; const colorScaleOptions = child.props.colorScale || props.colorScale; if (role !== "group" && role !== "stack") { return undefined; } return props.theme && props.theme.group ? colorScaleOptions || props.theme.group.colorScale : colorScaleOptions; } function getDataWithOffset(props, defaultDataset = [], offset) { const dataset = props.data || props.y ? Data.getData(props) : defaultDataset; const xOffset = offset || 0; return dataset.map((datum) => { const _x1 = datum._x instanceof Date ? new Date(datum._x.getTime() + xOffset) : datum._x + xOffset; return Object.assign({}, datum, { _x1 }); }); } export function getChildren(initialProps, childComponents?, calculatedProps?) { const props = Helpers.modifyProps(initialProps, fallbackProps, "stack"); const children = childComponents || React.Children.toArray(props.children); const newCalculatedProps = calculatedProps || getCalculatedProps(props, children); const { datasets } = newCalculatedProps; const { labelComponent, polar, theme } = props; const childProps = getChildProps(props, newCalculatedProps); const parentName = props.name || "group"; return children.map((child, index) => { const role = child.type && child.type.role; const xOffset = polar ? getPolarX0(props, newCalculatedProps, index, role) : getX0(props, newCalculatedProps, index, role); const style = role === "voronoi" || role === "tooltip" || role === "label" ? child.props.style : Wrapper.getChildStyle(child, index, newCalculatedProps, theme); const labels = props.labels ? getLabels(props, datasets, index) : child.props.labels; const name = child.props.name || `${parentName}-${role}-${index}`; return React.cloneElement( child, Object.assign( { labels, style, key: `${name}-key-${index}`, name, data: getDataWithOffset(props, datasets[index], xOffset), colorScale: getColorScale(props, child), labelComponent: labelComponent || child.props.labelComponent, xOffset, }, childProps, ), ); }); } ================================================ FILE: packages/victory-group/src/index.ts ================================================ export * from "./victory-group"; ================================================ FILE: packages/victory-group/src/victory-group.test.tsx ================================================ import { render, screen } from "@testing-library/react"; import React from "react"; import { VictoryBar } from "victory-bar"; import { VictoryGroup } from "./victory-group"; describe("components/victory-group", () => { it("has a static role", () => { expect(VictoryGroup.role).toEqual("group"); }); describe("default component rendering", () => { it("renders an svg with the correct width and height", () => { const { container } = render( , ); const svg = container.querySelector("svg"); expect(svg?.style.width).toEqual("100%"); expect(svg?.style.height).toEqual("100%"); }); it("renders an svg with the correct viewBox", () => { const { container } = render( , ); const svg = container.querySelector("svg"); const viewBoxValue = `0 0 ${450} ${300}`; expect(svg?.getAttribute("viewBox")).toEqual(viewBoxValue); }); it("accepts user props", () => { render( , ); expect(screen.getByTestId("victory-group")).toBeDefined(); expect(screen.getByLabelText("Group")).toBeDefined(); }); }); }); ================================================ FILE: packages/victory-group/src/victory-group.tsx ================================================ import defaults from "lodash/defaults"; import isEmpty from "lodash/isEmpty"; import React from "react"; import { EventPropTypeInterface, Helpers, Hooks, StringOrNumberOrCallback, UserProps, VictoryCommonProps, VictoryContainer, VictoryDatableProps, VictoryMultiLabelableProps, VictoryStyleInterface, VictoryTheme, Wrapper, VictoryComponentConfiguration, } from "victory-core"; import { VictorySharedEvents } from "victory-shared-events"; import { getChildren, useMemoizedProps } from "./helper-methods"; import isEqual from "react-fast-compare"; const fallbackProps = { width: 450, height: 300, padding: 50, offset: 0, }; export type VictoryGroupTTargetType = "data" | "labels" | "parent"; export interface VictoryGroupProps extends VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps { children?: React.ReactNode; color?: string; events?: EventPropTypeInterface< VictoryGroupTTargetType, StringOrNumberOrCallback >[]; eventKey?: StringOrNumberOrCallback; offset?: number; style?: VictoryStyleInterface; displayName?: string; } const defaultProps = { containerComponent: , groupComponent: , samples: 50, standalone: true, theme: VictoryTheme.grayscale, }; const VictoryGroupBase: React.FC = (initialProps) => { const role = VictoryGroup?.role; const { getAnimationProps, setAnimationState, getProps } = Hooks.useAnimationState(); const propsWithDefaults = React.useMemo( () => defaults({}, initialProps, defaultProps), [initialProps], ); const props = getProps(propsWithDefaults); const modifiedProps = Helpers.modifyProps(props, fallbackProps, role); const { eventKey, containerComponent, standalone, groupComponent, externalEventMutations, width, height, theme, polar, horizontal, name, } = modifiedProps; const childComponents = React.Children.toArray(modifiedProps.children); const calculatedProps = useMemoizedProps(modifiedProps); const { domain, scale, style, origin } = calculatedProps; const newChildren = React.useMemo(() => { const children = getChildren(props, childComponents, calculatedProps); return children.map((child, index) => { const childProps = Object.assign( { animate: getAnimationProps(props, child, index) }, child.props, ); return React.cloneElement(child, childProps); }); }, [props, childComponents, calculatedProps, getAnimationProps]); const containerProps = React.useMemo(() => { if (standalone) { return { domain, scale, width, height, standalone, theme, style: style.parent, horizontal, polar, origin, name, }; } return {}; }, [ standalone, domain, scale, width, height, theme, style, horizontal, polar, origin, name, ]); const userProps = React.useMemo( () => UserProps.getSafeUserProps(propsWithDefaults), [propsWithDefaults], ); const container = React.useMemo(() => { if (standalone) { const defaultContainerProps = defaults( {}, containerComponent.props, containerProps, userProps, ); return React.cloneElement(containerComponent, defaultContainerProps); } return React.cloneElement(groupComponent, userProps); }, [ groupComponent, standalone, containerComponent, containerProps, userProps, ]); const events = React.useMemo(() => { return Wrapper.getAllEvents(props); }, [props]); const previousProps = Hooks.usePreviousProps(propsWithDefaults); React.useEffect(() => { // This is called before dismount to keep state in sync return () => { if (propsWithDefaults.animate) { setAnimationState(previousProps, props); } }; }, [setAnimationState, previousProps, propsWithDefaults, props]); if (!isEmpty(events)) { return ( {newChildren} ); } return React.cloneElement(container, container.props, newChildren); }; const componentConfig: VictoryComponentConfiguration = { role: "group", expectedComponents: [ "groupComponent", "containerComponent", "labelComponent", ], getChildren, }; export const VictoryGroup = Object.assign( React.memo(VictoryGroupBase, isEqual), componentConfig, ); VictoryGroup.displayName = "VictoryGroup"; ================================================ FILE: packages/victory-group/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-group/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-histogram/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-histogram/CHANGELOG.md ================================================ # victory-histogram ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ## 37.3.2 ## 37.3.1 ### Patch Changes - Remove duplicate types from interfaces ([#2940](https://github.com/FormidableLabs/victory/pull/2940)) ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash isNil and isNan with native code ([#2800](https://github.com/FormidableLabs/victory/pull/2800)) * Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) * Replace instances of lodash.range with equivalent native code ([#2760](https://github.com/FormidableLabs/victory/pull/2760)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Migrate victory-histogram to TypeScript ([#2719](https://github.com/FormidableLabs/victory/pull/2719)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-bar@36.6.8 - victory-core@36.6.8 - victory-vendor@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-bar@36.6.7 - victory-core@36.6.7 - victory-vendor@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-bar@36.6.6 - victory-core@36.6.6 - victory-vendor@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416), [`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 - victory-vendor@36.6.5 - victory-bar@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 - victory-bar@36.6.4 - victory-vendor@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-bar@36.6.3 - victory-core@36.6.3 - victory-vendor@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-bar@36.6.2 - victory-core@36.6.2 - victory-vendor@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 - victory-bar@36.6.1 - victory-vendor@36.6.1 ## 36.6.0 ### Patch Changes - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 - victory-bar@36.6.0 - victory-vendor@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-histogram/README.md ================================================ # VictoryHistogram ================================================ FILE: packages/victory-histogram/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-histogram/package.json ================================================ { "name": "victory-histogram", "version": "37.3.6", "description": "Histogram Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", "victory-bar": "37.3.6", "victory-core": "37.3.6", "victory-vendor": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-bar:build:lib:esm", "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-bar:build:lib:cjs", "../victory-core:build:lib:cjs", "../victory-vendor:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-bar:build:lib:esm", "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-bar:build:lib:esm", "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-bar:types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-bar:types:create", "../victory-core:types:create", "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-bar:types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-bar:types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-bar:build", "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-histogram/src/helper-methods.ts ================================================ import { Helpers, LabelHelpers, Data, Domain, Scale } from "victory-core"; import { getBarPosition } from "victory-bar"; import isEqual from "react-fast-compare"; import * as d3Array from "victory-vendor/d3-array"; import * as d3Scale from "victory-vendor/d3-scale"; import { VictoryHistogramProps } from "./victory-histogram"; const cacheLastValue = (func) => { let called = false; let lastArgs; let lastReturnVal; return (...args) => { if (called && isEqual(lastArgs, args)) { return lastReturnVal; } const value = func(...args); called = true; lastReturnVal = value; lastArgs = args; return value; }; }; const dataOrBinsContainDates = ({ data, bins, x, }: Pick) => { const xAccessor = Helpers.createAccessor(x || "x"); const dataIsDates = data?.some((datum) => xAccessor(datum) instanceof Date) || false; const binsHasDates = Array.isArray(bins) && bins.some((bin) => bin instanceof Date); return dataIsDates || binsHasDates; }; const getBinningFunc = ({ data, x, bins, dataOrBinsContainsDates }) => { const xAccessor = Helpers.createAccessor(x || "x"); const bin = d3Array.bin().value(xAccessor); const niceScale = ( (dataOrBinsContainsDates ? d3Scale.scaleTime() : d3Scale.scaleLinear()) as any ) .domain(d3Array.extent(data, xAccessor)) .nice(); if (Array.isArray(bins)) { bin.domain([bins[0], bins[bins.length - 1]]); bin.thresholds(bins.slice(1, bins.length - 1)); return bin; } if (Number.isInteger(bins)) { bin.domain(niceScale.domain()); bin.thresholds(bins); return bin; } if (dataOrBinsContainsDates) { bin.domain(niceScale.domain()); bin.thresholds(niceScale.ticks()); return bin as unknown as d3Array.HistogramGeneratorDate; } bin.domain(niceScale.domain()); return bin; }; export const getFormattedData = cacheLastValue( ({ data = [], x, bins, }: Pick) => { if ((!data || !data.length) && !Array.isArray(bins)) { return []; } const dataOrBinsContainsDates = dataOrBinsContainDates({ data, bins, x }); const binFunc = getBinningFunc({ data, x, bins, dataOrBinsContainsDates }); const foo = binFunc(data); const binnedData = [...foo].filter(({ x0, x1 }) => { if (x0 instanceof Date && x1 instanceof Date) { return new Date(x0).getTime() !== new Date(x1).getTime(); } return x0 !== x1; }); const formattedData = binnedData.map((bin) => { const x0 = dataOrBinsContainsDates ? new Date(bin.x0 as Date) : bin.x0 || 0; const x1 = dataOrBinsContainsDates ? new Date(bin.x1 as Date) : bin.x1 || 0; return { x0, x1, x: dataOrBinsContainsDates ? new Date(((x0 as Date).getTime() + (x1 as Date).getTime()) / 2) : ((x0 as number) + (x1 as number)) / 2, y: bin.length, binnedData: [...bin], }; }); return formattedData; }, ); export const getData = (props: VictoryHistogramProps) => { const { bins, data, x } = props; const dataIsPreformatted = data?.some(({ _y }) => !Helpers.isNil(_y)); const formattedData = dataIsPreformatted ? data : getFormattedData({ data, x, bins }); return Data.getData({ ...props, data: formattedData, x: "x" }); }; export const getDomain = (props: VictoryHistogramProps, axis: "x" | "y") => { const data = getData(props); if (!data.length) { return [0, 1]; } if (axis === "x") { const firstBin = data[0]; const lastBin = data[data.length - 1]; return Domain.getDomainWithZero( { ...props, data: [{ x: firstBin.x0 }, { x: lastBin.x1 }], x: "x" }, "x", ); } return props.data?.length ? Domain.getDomainWithZero({ ...props, data }, "y") : [0, 1]; }; const getCalculatedValues = (props) => { const defaultStyles = Helpers.getDefaultStyles(props, "histogram"); const style = Helpers.getStyles(props.style, defaultStyles); const range = props.range || { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; const domain = { x: getDomain(props, "x"), y: getDomain(props, "y"), }; let data = getData(props); data = Data.formatDataFromDomain(data, domain, 0); const scale = { x: Scale.getBaseScale(props, "x") .domain(domain.x) .range(props.horizontal ? range.y : range.x), y: Scale.getBaseScale(props, "y") .domain(domain.y) .range(props.horizontal ? range.x : range.y), }; return { style, data, scale, domain }; }; export const getBaseProps = (initialProps, fallbackProps) => { const modifiedProps = Helpers.modifyProps( initialProps, fallbackProps, "histogram", ); const props = Object.assign( {}, modifiedProps, getCalculatedValues(modifiedProps), ); const { binSpacing, cornerRadius, data, domain, events, height, horizontal, padding, scale, sharedEvents, standalone, style, theme, width, labels, name, getPath, disableInlineStyles, } = props; const initialChildProps = { parent: { horizontal, domain, scale, width, height, data, standalone, name, theme, padding, style: style.parent, }, }; const getDistance = (datum) => { const current = scale.x(datum.x0); const next = scale.x(datum.x1); return Math.abs(next - current); }; const getBarWidth = (datum) => { if (binSpacing) { return getDistance(datum) - binSpacing; } return getDistance(datum); }; return data.reduce((childProps, datum, index) => { const eventKey = !Helpers.isNil(datum.eventKey) ? datum.eventKey : index; const { x, y, y0, x0 } = getBarPosition(props, datum); const barWidth = getBarWidth(datum); const dataProps = { alignment: "middle", barWidth, cornerRadius, data, datum, horizontal, index, scale, style: disableInlineStyles ? {} : style.data, width, height, x, y, y0, x0, getPath, disableInlineStyles, }; childProps[eventKey] = { data: dataProps, }; const text = LabelHelpers.getText(props, datum, index); if ( (text !== undefined && text !== null) || (labels && (events || sharedEvents)) ) { childProps[eventKey].labels = LabelHelpers.getProps(props, index); } return childProps; }, initialChildProps); }; ================================================ FILE: packages/victory-histogram/src/index.ts ================================================ export * from "./victory-histogram"; ================================================ FILE: packages/victory-histogram/src/victory-histogram.test.tsx ================================================ import React from "react"; import { render, screen, fireEvent } from "@testing-library/react"; import { Helpers } from "victory-core"; import { isBar, getBarHeight } from "../../../test/helpers"; import { VictoryHistogram } from "./victory-histogram"; describe("components/victory-histogram", () => { const DATA_COMPONENT_ID = "data-component-id"; const DataComponent = () =>
; describe("default component rendering", () => { it("accepts user props", () => { render( , ); const svgNode = screen.getByTestId("victory-histogram"); expect(svgNode.getAttribute("aria-label")).toEqual("Chart"); }); it("renders an svg with the correct width and height", () => { const { container } = render(); const svg = container.querySelector("svg"); expect(svg?.getAttribute("style")).toContain("width: 100%; height: 100%"); }); it("renders an svg with the correct viewBox", () => { const { container } = render(); const svg = container.querySelector("svg"); const viewBoxValue = `0 0 ${450} ${300}`; expect(svg?.getAttribute("viewBox")).toEqual(viewBoxValue); }); it("renders 0 bars", () => { const { container } = render(); const bars = container.querySelectorAll("path"); expect(bars).toHaveLength(0); }); it("renders 4 bars", () => { const { container } = render( , ); const bars = container.querySelectorAll("path"); expect(bars).toHaveLength(4); }); it("renders each bar as a rectangle", () => { const { container } = render(); const barCommandStrings = Array.from( container.querySelectorAll("path"), ).map((bar) => bar.getAttribute("d")); barCommandStrings.forEach((commandString) => { expect(isBar(commandString)).toBeTruthy(); }); }); }); describe("rendering data", () => { it("renders bars for {x} shaped data (default)", () => { const data = Helpers.range(5).map((i) => ({ x: i })); const { container } = render(); const bars = container.querySelectorAll("path"); expect(bars.length).toEqual(4); }); it("renders bars for deeply-nested data", () => { const data = Helpers.range(5).map((i) => ({ a: { b: [{ x: i }] } })); const { container } = render( , ); const bars = container.querySelectorAll("path"); expect(bars.length).toEqual(4); }); it("renders 2 bars of equal height", () => { const data = [{ x: 2 }, { x: 3 }]; const { container } = render( , ); const bars = Array.from(container.querySelectorAll("path")); const [height1, height2] = bars.map((bar) => getBarHeight(bar.getAttribute("d")), ); expect(bars).toHaveLength(2); expect(height1).toEqual(height2); expect(height1).toBeGreaterThan(0); }); it("renders bars values with null accessor", () => { const data = Helpers.range(10); render( } />, ); const bars = screen.getAllByTestId(DATA_COMPONENT_ID); expect(bars.length).toBeGreaterThan(0); }); it("renders bars with appropriate relative heights", () => { const { container } = render( , ); const bars = Array.from(container.querySelectorAll("path")); const [height1, height2, height3] = bars.map((bar) => getBarHeight(bar.getAttribute("d")), ); expect(height2 / 2).toBeCloseTo(height1); expect((height3 * 2) / 3).toBeCloseTo(height2); }); }); describe("event handling", () => { const clickHandler = jest.fn(); beforeEach(() => { clickHandler.mockReset(); }); it("attaches an event to the parent svg", () => { const { container } = render( , ); const svg = container.querySelector("svg"); fireEvent.click(svg!); expect(clickHandler).toHaveBeenCalled(); // the first argument is the standard event object const contextualArg = clickHandler.mock.calls[0][1]; expect(contextualArg.key).toEqual("histogram-parent-parent"); }); it("attaches an event to data", () => { const { container } = render( , ); const data = container.querySelectorAll("path"); expect(data).toHaveLength(2); data.forEach((node, index) => { clickHandler.mockReset(); fireEvent.click(node); expect(clickHandler).toHaveBeenCalled(); const contextualObject = clickHandler.mock.calls[0][1]; expect(contextualObject.key).toEqual(`histogram-data-${index}`); }); }); it("attaches an event to a label", () => { const data = [{ x: 0 }, { x: 1 }, { x: 2 }]; render( `Label: ${datum.x}`} />, ); data.forEach((datum) => { clickHandler.mockReset(); const label = screen.getByText(`Label: ${datum.x + 0.5}`); fireEvent.click(label); expect(clickHandler).toHaveBeenCalled(); }); }); }); describe("accessibility", () => { it("adds an aria role to each bar in the series", () => { const data = Helpers.range(5).map((x) => ({ x })); const { container } = render(); container.querySelectorAll("path").forEach((bar) => { expect(bar.getAttribute("role")).toEqual("presentation"); }); }); }); }); ================================================ FILE: packages/victory-histogram/src/victory-histogram.tsx ================================================ import React from "react"; import { Bar } from "victory-bar"; import { Helpers, VictoryLabel, VictoryContainer, VictoryTheme, addEvents, UserProps, EventPropTypeInterface, NumberOrCallback, StringOrNumberOrCallback, VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, VictoryStyleInterface, EventsMixinClass, } from "victory-core"; import { getBaseProps, getData, getDomain, getFormattedData, } from "./helper-methods"; export type VictoryHistogramTargetType = "data" | "labels" | "parent"; export interface VictoryHistogramProps extends Omit, Omit, VictoryMultiLabelableProps { binSpacing?: number; bins?: number | number[] | Date[]; cornerRadius?: | NumberOrCallback | { top?: NumberOrCallback; topLeft?: NumberOrCallback; topRight?: NumberOrCallback; bottom?: NumberOrCallback; bottomLeft?: NumberOrCallback; bottomRight?: NumberOrCallback; }; events?: EventPropTypeInterface< VictoryHistogramTargetType, number | string | number[] | string[] >[]; eventKey?: StringOrNumberOrCallback; style?: VictoryStyleInterface; } const fallbackProps: Partial = { width: 450, height: 300, padding: 50, }; const defaultData = []; // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryHistogramBase extends EventsMixinClass {} /** * Draw SVG histogram charts with React. VictoryHistogram is a composable component, so it doesn't include axes * Check out VictoryChart for complete histogram charts and more. */ class VictoryHistogramBase extends React.Component { static animationWhitelist = [ "data", "domain", "height", "padding", "style", "width", ]; static displayName = "VictoryHistogram"; static role = "histogram"; static defaultTransitions = { onLoad: { duration: 2000, before: () => ({ _y: 0, _y1: 0, _y0: 0 }), after: (datum) => ({ _y: datum._y, _y1: datum._y1, _y0: datum._y0 }), }, onExit: { duration: 500, before: () => ({ _y: 0, yOffset: 0 }), }, onEnter: { duration: 500, before: () => ({ _y: 0, _y1: 0, _y0: 0 }), after: (datum) => ({ _y: datum._y, _y1: datum._y1, _y0: datum._y0 }), }, }; static getFormattedData(...args: any) { return getFormattedData(...args); } static defaultProps: VictoryHistogramProps = { containerComponent: , data: defaultData, dataComponent: , groupComponent: , labelComponent: , samples: 50, sortOrder: "ascending", standalone: true, theme: VictoryTheme.grayscale, }; static getDomain(props, axis) { return getDomain(props, axis); } static getData(props) { return getData(props); } static getBaseProps(props: VictoryHistogramProps) { return getBaseProps(props, fallbackProps); } static expectedComponents: Partial[] = [ "dataComponent", "labelComponent", "groupComponent", "containerComponent", ]; // Overridden in native versions shouldAnimate() { return !!this.props.animate; } render(): React.ReactElement { const { animationWhitelist, role } = VictoryHistogramBase; const props = Helpers.modifyProps(this.props, fallbackProps, role); if (this.shouldAnimate()) { return this.animateComponent(props, animationWhitelist); } const children = this.renderData(props); const component = props.standalone ? this.renderContainer(props.containerComponent, children) : children; return UserProps.withSafeUserProps(component, props); } } export const VictoryHistogram = addEvents(VictoryHistogramBase); ================================================ FILE: packages/victory-histogram/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-histogram/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-legend/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-legend/CHANGELOG.md ================================================ # victory-legend ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) * Improve types in victory-core helpers ([#2999](https://github.com/FormidableLabs/victory/pull/2999)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ### Minor Changes - Minor updates for clean theme ([#2909](https://github.com/FormidableLabs/victory/pull/2909)) ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash keys with native code ([#2811](https://github.com/FormidableLabs/victory/pull/2811)) * Replace lodash sum with native code ([#2831](https://github.com/FormidableLabs/victory/pull/2831)) - Replace lodash isNil and isNan with native code ([#2800](https://github.com/FormidableLabs/victory/pull/2800)) * Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ### Patch Changes - Migrate victory-legend to TypeScript ([#2712](https://github.com/FormidableLabs/victory/pull/2712)) ## 36.8.1 ## 36.8.0 ### Patch Changes - Add missing size property to TS definitions ([#2523](https://github.com/FormidableLabs/victory/pull/2523)) ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-legend/README.md ================================================ # VictoryLegend `victory-legend@^30.0.0` exports `VictoryLegend` To view documentation for `VictoryLegend` please see https://commerce.nearform.com/open-source/victory/docs/victory-legend To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-legend.md ================================================ FILE: packages/victory-legend/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-legend/package.json ================================================ { "name": "victory-legend", "version": "37.3.6", "description": "Legend Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-legend/src/helper-methods.ts ================================================ import defaults from "lodash/defaults"; import groupBy from "lodash/groupBy"; import range from "lodash/range"; import { Helpers, Style, TextSize } from "victory-core"; import { VictoryLegendProps } from "./victory-legend"; const getColorScale = (props) => { const { colorScale, theme } = props; return typeof colorScale === "string" ? Style.getColorScale(colorScale, theme) : colorScale || []; }; const getLabelStyles = (props) => { const { data, style } = props; return data.map((datum, index) => { const baseLabelStyles = defaults({}, datum.labels, style.labels); return Helpers.evaluateStyle(baseLabelStyles, { datum, index, data }); }); }; const getStyles = (props, styleObject: VictoryLegendProps["style"] = {}) => { const style = props.style || {}; const parentStyleProps = { height: "100%", width: "100%" }; return { parent: defaults(style.parent, styleObject.parent, parentStyleProps), data: defaults({}, style.data, styleObject.data), labels: defaults({}, style.labels, styleObject.labels), border: defaults({}, style.border, styleObject.border), title: defaults({}, style.title, styleObject.title), }; }; const getCalculatedValues = (props) => { const { orientation, theme } = props; const defaultStyles = theme && theme.legend && theme.legend.style ? theme.legend.style : {}; const style = getStyles(props, defaultStyles); const colorScale = getColorScale(props); const isHorizontal = orientation === "horizontal"; const borderPadding = Helpers.getPadding(props.borderPadding); return Object.assign({}, props, { style, isHorizontal, colorScale, borderPadding, }); }; const getColumn = (props, index) => { const { itemsPerRow, isHorizontal } = props; if (!itemsPerRow) { return isHorizontal ? index : 0; } return isHorizontal ? index % itemsPerRow : Math.floor(index / itemsPerRow); }; const getRow = (props, index) => { const { itemsPerRow, isHorizontal } = props; if (!itemsPerRow) { return isHorizontal ? 0 : index; } return isHorizontal ? Math.floor(index / itemsPerRow) : index % itemsPerRow; }; const groupData = (props) => { const { data } = props; const style = (props.style && props.style.data) || {}; const labelStyles = getLabelStyles(props); return data.map((datum, index) => { const symbol = datum.symbol || {}; const { fontSize } = labelStyles[index]; // eslint-disable-next-line no-magic-numbers const size = symbol.size || style.size || fontSize / 2.5; const symbolSpacer = props.symbolSpacer || Math.max(size, fontSize); return { ...datum, size, symbolSpacer, fontSize, textSize: TextSize.approximateTextSize(datum.name, labelStyles[index]), column: getColumn(props, index), row: getRow(props, index), }; }); }; const getColumnWidths = (props, data) => { const gutter = props.gutter || {}; const gutterWidth = typeof gutter === "object" ? (gutter.left || 0) + (gutter.right || 0) : gutter || 0; const dataByColumn = groupBy(data, "column"); const columns = Object.keys(dataByColumn); return columns.reduce((memo, curr, index) => { const lengths = dataByColumn[curr].map((d) => { return d.textSize.width + d.size + d.symbolSpacer + gutterWidth; }); memo[index] = Math.max(...lengths); return memo; }, []); }; const getRowHeights = (props, data) => { const gutter = props.rowGutter || {}; const gutterHeight = typeof gutter === "object" ? (gutter.top || 0) + (gutter.bottom || 0) : gutter || 0; const dataByRow = groupBy(data, "row"); return Object.keys(dataByRow).reduce((memo, curr, index) => { const rows = dataByRow[curr]; const lengths = rows.map((d) => { return d.textSize.height + d.symbolSpacer + gutterHeight; }); memo[index] = Math.max(...lengths); return memo; }, []); }; const getTitleDimensions = (props) => { const style = (props.style && props.style.title) || {}; const textSize = TextSize.approximateTextSize(props.title, style); const padding = style.padding || 0; return { height: textSize.height + 2 * padding || 0, width: textSize.width + 2 * padding || 0, }; }; const getOffset = (datum, rowHeights, columnWidths) => { const { column, row } = datum; return { x: range(column).reduce((memo, curr) => memo + columnWidths[curr], 0), y: range(row).reduce((memo, curr) => memo + rowHeights[curr], 0), }; }; const getAnchors = (titleOrientation, centerTitle) => { const standardAnchors = { textAnchor: titleOrientation === "right" ? "end" : "start", verticalAnchor: titleOrientation === "bottom" ? "end" : "start", }; if (centerTitle) { const horizontal = titleOrientation === "top" || titleOrientation === "bottom"; return { textAnchor: horizontal ? "middle" : standardAnchors.textAnchor, verticalAnchor: horizontal ? standardAnchors.verticalAnchor : "middle", }; } return standardAnchors; }; const getTitleStyle = (props) => { const { titleOrientation, centerTitle, titleComponent } = props; const baseStyle = (props.style && props.style.title) || {}; const componentStyle = (titleComponent.props && titleComponent.props.style) || {}; const anchors = getAnchors(titleOrientation, centerTitle); return Array.isArray(componentStyle) ? componentStyle.map((obj) => defaults({}, obj, baseStyle, anchors)) : defaults({}, componentStyle, baseStyle, anchors); }; const getTitleProps = (props, borderProps) => { const { title, titleOrientation, centerTitle, borderPadding } = props; const { height, width } = borderProps; const style = getTitleStyle(props); const padding = Array.isArray(style) ? style[0].padding : style.padding; const horizontal = titleOrientation === "top" || titleOrientation === "bottom"; const xOrientation = titleOrientation === "bottom" ? "bottom" : "top"; const yOrientation = titleOrientation === "right" ? "right" : "left"; const standardPadding = { x: centerTitle ? width / 2 : borderPadding[xOrientation] + (padding || 0), y: centerTitle ? height / 2 : borderPadding[yOrientation] + (padding || 0), }; const getPadding = () => { return borderPadding[titleOrientation] + (padding || 0); }; const xOffset = horizontal ? standardPadding.x : getPadding(); const yOffset = horizontal ? getPadding() : standardPadding.y; return { x: titleOrientation === "right" ? props.x + width - xOffset : props.x + xOffset, y: titleOrientation === "bottom" ? props.y + height - yOffset : props.y + yOffset, style, text: title, }; }; const getBorderProps = (props, contentHeight, contentWidth) => { const { x, y, borderPadding, style } = props; const height = (contentHeight || 0) + borderPadding.top + borderPadding.bottom; const width = (contentWidth || 0) + borderPadding.left + borderPadding.right; return { x, y, height, width, style: Object.assign({ fill: "none" }, style.border), }; }; export const getDimensions = (initialProps, fallbackProps) => { const modifiedProps = Helpers.modifyProps( initialProps, fallbackProps, "legend", ); const props = Object.assign( {}, modifiedProps, getCalculatedValues(modifiedProps), ); const { title, titleOrientation } = props; const groupedData = groupData(props); const columnWidths = getColumnWidths(props, groupedData); const rowHeights = getRowHeights(props, groupedData); const titleDimensions = title ? getTitleDimensions(props) : { height: 0, width: 0 }; return { height: titleOrientation === "left" || titleOrientation === "right" ? Math.max(sum(rowHeights), titleDimensions.height) : sum(rowHeights) + titleDimensions.height, width: titleOrientation === "left" || titleOrientation === "right" ? sum(columnWidths) + titleDimensions.width : Math.max(sum(columnWidths), titleDimensions.width), }; }; export const getBaseProps = (initialProps, fallbackProps) => { const modifiedProps = Helpers.modifyProps( initialProps, fallbackProps, "legend", ); const props = Object.assign( {}, modifiedProps, getCalculatedValues(modifiedProps), ); const { data, standalone, theme, padding, style, colorScale, gutter, rowGutter, borderPadding, title, titleOrientation, name, x = 0, y = 0, } = props; const groupedData = groupData(props); const columnWidths = getColumnWidths(props, groupedData); const rowHeights = getRowHeights(props, groupedData); const labelStyles = getLabelStyles(props); const titleDimensions = title ? getTitleDimensions(props) : { height: 0, width: 0 }; const titleOffset = { x: titleOrientation === "left" ? titleDimensions.width : 0, y: titleOrientation === "top" ? titleDimensions.height : 0, }; const gutterOffset = { x: gutter && typeof gutter === "object" ? gutter.left || 0 : 0, y: rowGutter && typeof rowGutter === "object" ? rowGutter.top || 0 : 0, }; const { height, width } = getDimensions(props, fallbackProps); const borderProps = getBorderProps(props, height, width); const titleProps = getTitleProps(props, borderProps); const initialChildProps = { parent: { data, standalone, theme, padding, name, height: props.height, width: props.width, style: style.parent, }, all: { border: borderProps, title: titleProps }, }; return groupedData.reduce((childProps, datum, i) => { const color = colorScale[i % colorScale.length]; const dataStyle = defaults({}, datum.symbol, style.data, { fill: color }); const eventKey = !Helpers.isNil(datum.eventKey) ? datum.eventKey : i; const offset = getOffset(datum, rowHeights, columnWidths); const originY = y + borderPadding.top + datum.symbolSpacer; const originX = x + borderPadding.left + datum.symbolSpacer; const dataProps = { index: i, data, datum, symbol: dataStyle.type || dataStyle.symbol || "circle", size: datum.size, style: dataStyle, y: originY + offset.y + titleOffset.y + gutterOffset.y, x: originX + offset.x + titleOffset.x + gutterOffset.x, }; const labelProps = { datum, data, text: datum.name, style: labelStyles[i], y: dataProps.y, x: dataProps.x + datum.symbolSpacer + datum.size / 2, }; childProps[eventKey] = { data: dataProps, labels: labelProps }; return childProps; }, initialChildProps); }; /** * Computes the sum of the values in `array`. * @param {Array} array The array to iterate over. * @returns {number} Returns the sum. */ function sum(array: number[]) { if (array && array.length) { let value = 0; for (let i = 0; i < array.length; i++) { value += array[i]; } return value; } return 0; } ================================================ FILE: packages/victory-legend/src/index.ts ================================================ export * from "./victory-legend"; ================================================ FILE: packages/victory-legend/src/victory-legend.test.tsx ================================================ import React from "react"; import { render, screen } from "@testing-library/react"; import { isCircle, isTriangle } from "../../../test/helpers"; import { VictoryLegend } from "./victory-legend"; describe("components/victory-legend", () => { const initialData = [ { name: "Series 1", symbol: { type: "circle", }, }, { name: "Series 2", labels: { fill: "red", }, symbol: { type: "triangleUp", fill: "blue", }, }, ]; it("renders provided data correctly", () => { const { container } = render(); const legendLabels = container.querySelectorAll("text"); expect(legendLabels).toHaveLength(initialData.length); }); it("has expected horizontal label position", () => { const { container } = render( , ); const [label1, label2] = container.querySelectorAll("text"); expect(label1.getAttribute("y")).toEqual(label2.getAttribute("y")); }); it("has expected vertical symbol position", () => { const { container } = render( , ); const [label1, label2] = container.querySelectorAll("text"); expect(label1.getAttribute("x")).toEqual(label2.getAttribute("x")); }); describe("symbols", () => { const legendData = [ { name: "Series 1", labels: { fontSize: 10, }, symbol: { type: "circle", fill: "red", }, }, { name: "Long Series Name", labels: { fontSize: 12, }, symbol: { type: "triangleUp", fill: "blue", }, }, ]; it("has expected symbols length", () => { const { container } = render(); const symbols = container.querySelectorAll("path"); expect(symbols).toHaveLength(2); }); it("has expected symbol colors", () => { const { container } = render(); const symbols = container.querySelectorAll("path"); symbols.forEach((symbol, index) => { const style = symbol.getAttribute("style"); expect(style).toContain(legendData[index].symbol.fill); }); }); it("has expected symbol type", () => { const { container } = render(); const [circleSymbol, triangleSymbol] = Array.from( container.querySelectorAll("path"), ).map((symbol) => symbol.getAttribute("d")); expect(isCircle(circleSymbol)).toBeTruthy(); expect(isTriangle(triangleSymbol)).toBeTruthy(); }); }); describe("legend style prop", () => { const legendData = [ { name: "Thing 1", }, { name: "Thing 2", }, ]; const styleObject = { data: { type: "triangleUp", fill: "green", }, labels: { fontSize: 16, }, }; it("has expected symbol type", () => { const { container } = render( , ); container.querySelectorAll("path").forEach((symbol) => { const svgPath = symbol.getAttribute("d"); expect(isTriangle(svgPath)).toBeTruthy(); }); }); it("has expected symbol colors", () => { const { container } = render( , ); container.querySelectorAll("path").forEach((item) => { expect(item.getAttribute("style")).toContain(styleObject.data.fill); }); }); it("has expected label colors", () => { render(); legendData.forEach(({ name }) => { const label = screen.getByText(name); expect(label.getAttribute("style")).toContain("#252525"); }); }); }); describe("itemsPerRow", () => { const legendData = [ { name: "Thing 1", }, { name: "Thing 2", }, { name: "Thing 3", }, { name: "Thing 4", }, { name: "Thing 5", }, { name: "Thing 6", }, ]; const splitArrayAtIndex = (array, index) => { const half1 = array.slice(0, index); const half2 = array.slice(index, array.length); return [half1, half2]; }; it("aligns items in columns", () => { const { container } = render( , ); const labels = Array.from(container.querySelectorAll("text")); expect(labels).toHaveLength(6); const [column1, column2] = splitArrayAtIndex(labels, 3); // items line up between columns column1.forEach((item, index) => { const correspondingColumnItemY = column2[index].getAttribute("y"); expect(item.getAttribute("y")).toEqual(correspondingColumnItemY); }); }); it("aligns items in rows", () => { const { container } = render( , ); const labels = Array.from(container.querySelectorAll("text")); expect(labels).toHaveLength(6); const rows = splitArrayAtIndex(labels, 3); // each row is on the same y axis rows.forEach((row) => { const rowYValue = row[0].getAttribute("y"); const allInSameRow = row.every( (item) => item.getAttribute("y") === rowYValue, ); expect(allInSameRow).toBeTruthy(); }); }); }); }); ================================================ FILE: packages/victory-legend/src/victory-legend.tsx ================================================ import React from "react"; import { getBaseProps, getDimensions } from "./helper-methods"; import { addEvents, Helpers, VictoryLabel, VictoryContainer, VictoryTheme, Point, Border, BlockProps, ColorScalePropType, EventPropTypeInterface, OrientationTypes, PaddingProps, StringOrNumberOrCallback, VictoryCommonProps, VictoryDatableProps, VictorySingleLabelableProps, VictoryStyleInterface, VictoryLabelStyleObject, EventsMixinClass, } from "victory-core"; export type VictoryLegendTTargetType = "data" | "labels" | "parent"; export type VictoryLegendOrientationType = "horizontal" | "vertical"; export interface VictoryLegendProps extends VictoryCommonProps, VictoryDatableProps, VictorySingleLabelableProps { borderComponent?: React.ReactElement; borderPadding?: PaddingProps; centerTitle?: boolean; colorScale?: ColorScalePropType; dataComponent?: React.ReactElement; eventKey?: StringOrNumberOrCallback | string[]; events?: EventPropTypeInterface< VictoryLegendTTargetType, StringOrNumberOrCallback >[]; gutter?: number | { left: number; right: number }; itemsPerRow?: number; orientation?: VictoryLegendOrientationType; rowGutter?: number | Omit; style?: VictoryStyleInterface & { title?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; symbolSpacer?: number; title?: string | string[]; titleComponent?: React.ReactElement; titleOrientation?: OrientationTypes; } const fallbackProps = { orientation: "vertical", titleOrientation: "top", width: 450, height: 300, x: 0, y: 0, }; const defaultLegendData = [{ name: "Series 1" }, { name: "Series 2" }]; // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryLegendBase extends EventsMixinClass {} class VictoryLegendBase extends React.Component { static displayName = "VictoryLegend"; static role = "legend"; static defaultProps = { borderComponent: , data: defaultLegendData, containerComponent: , dataComponent: , groupComponent: , labelComponent: , standalone: true, theme: VictoryTheme.grayscale, titleComponent: , }; static getBaseProps(props: VictoryLegendProps) { return getBaseProps(props, fallbackProps); } static getDimensions(props: VictoryLegendProps) { return getDimensions(props, fallbackProps); } static expectedComponents = [ "borderComponent", "containerComponent", "dataComponent", "groupComponent", "labelComponent", "titleComponent", ]; renderChildren(props: VictoryLegendProps) { const { dataComponent, labelComponent, title } = props; const children: React.ReactElement[] = []; if (props.borderComponent) { const borderProps = this.getComponentProps( props.borderComponent, "border", "all", ); const borderComponent = React.cloneElement( props.borderComponent, borderProps, ); children.push(borderComponent); } if (dataComponent) { const dataComponents = this.dataKeys .map((_dataKey, index) => { if (_dataKey === "all") { return undefined; } const dataProps = this.getComponentProps( dataComponent, "data", index, ); return React.cloneElement(dataComponent, dataProps); }) .filter( (comp: React.ReactElement | undefined): comp is React.ReactElement => comp !== undefined, ); children.push(...dataComponents); } if (title && props.titleComponent) { const titleProps = this.getComponentProps(title, "title", "all"); const titleComponent = React.cloneElement( props.titleComponent, titleProps, ); children.push(titleComponent); } if (labelComponent) { const labelComponents = this.dataKeys .map((_dataKey, index) => { if (_dataKey === "all") { return undefined; } const labelProps = this.getComponentProps( labelComponent, "labels", index, ); if ( (labelProps as any).text !== undefined && (labelProps as any).text !== null ) { return React.cloneElement(labelComponent, labelProps); } return undefined; }) .filter( (comp: React.ReactElement | undefined): comp is React.ReactElement => comp !== undefined, ); children.push(...labelComponents); } return children; } render(): React.ReactElement { // @ts-expect-error Property 'role' does not exist on type 'Function'. // Ref: https://github.com/microsoft/TypeScript/issues/32452 const { role } = this.constructor; const props = Helpers.modifyProps(this.props, fallbackProps, role); const children = this.renderChildren(props); return props.standalone ? this.renderContainer(props.containerComponent, children) : React.cloneElement(props.groupComponent, {}, children); } } export const VictoryLegend = addEvents(VictoryLegendBase); ================================================ FILE: packages/victory-legend/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-legend/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-line/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-line/CHANGELOG.md ================================================ # victory-line ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ### Patch Changes - Remove duplicate types from interfaces ([#2940](https://github.com/FormidableLabs/victory/pull/2940)) ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash isNil and isNan with native code ([#2800](https://github.com/FormidableLabs/victory/pull/2800)) * Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ### Patch Changes - Fix victory-native component prop types ([#2785](https://github.com/FormidableLabs/victory/pull/2785)) ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) * Replace instances of lodash.range with equivalent native code ([#2760](https://github.com/FormidableLabs/victory/pull/2760)) ## 36.8.4 ## 36.8.3 ## 36.8.2 ## 36.8.1 ## 36.8.0 ### Minor Changes - Remove v37 experimental code ([#2697](https://github.com/FormidableLabs/victory/pull/2697)) ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) * Set VictoryLine to default to "pointer-events: stroke" so that events fire properly when lines overlap (fixes [#2468](https://github.com/FormidableLabs/victory/issues/2468)) ([#2530](https://github.com/FormidableLabs/victory/pull/2530)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 - victory-vendor@36.6.8 ## 36.6.7 ### Patch Changes - Type improvement: `VictoryLineProps.animate` can be an object (fixes [#2463](https://github.com/FormidableLabs/victory/issues/2463)) ([`3f476632f`](https://github.com/FormidableLabs/victory/commit/3f476632fcd41c31cb69fcfae74d1bbaaa78103d)) - Updated dependencies []: - victory-core@36.6.7 - victory-vendor@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 - victory-vendor@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416), [`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 - victory-vendor@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 - victory-vendor@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 - victory-vendor@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 - victory-vendor@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 - victory-vendor@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 - victory-vendor@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-line/README.md ================================================ # VictoryLine `victory-line@^30.0.0` exports `VictoryLine` and `Curve` components To view documentation for `VictoryLine` please see https://commerce.nearform.com/open-source/victory/docs/victory-line To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-line.md ================================================ FILE: packages/victory-line/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-line/package.json ================================================ { "name": "victory-line", "version": "37.3.6", "description": "Line Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6", "victory-vendor": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "devDependencies": { "victory-chart": "*" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs", "../victory-vendor:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-chart:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-chart:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-line/src/curve.test.tsx ================================================ import React from "react"; import * as d3Scale from "victory-vendor/d3-scale"; import { render } from "@testing-library/react"; import { SVGWrapper } from "../../../test/helpers"; import { Curve } from "./curve"; describe("victory-primitives/curve", () => { const baseProps = { data: [ { _x1: 1, x1: 1, _y1: 4, y1: 4, eventKey: 0 }, { _x1: 2, x1: 2, _y1: 5, y1: 5, eventKey: 1 }, { _x1: 3, x1: 3, _y1: 7, y1: 7, eventKey: 2 }, { _x1: 4, x1: 4, _y1: 10, y1: 10, eventKey: 3 }, { _x1: 5, x1: 5, _y1: 15, y1: 15, eventKey: 4 }, ], scale: { x: d3Scale.scaleLinear(), y: d3Scale.scaleLinear(), }, interpolation: "basis", }; it("should render a single curve for consecutive data", () => { const { container } = render(, { wrapper: SVGWrapper, }); expect(container.querySelector("path")).toMatchInlineSnapshot(` `); }); }); ================================================ FILE: packages/victory-line/src/curve.tsx ================================================ /* eslint no-magic-numbers: ["error", { "ignore": [-1, 0, 1, 2] }]*/ import React from "react"; import defaults from "lodash/defaults"; import { Helpers, Path, LineHelpers, UserProps, StringOrCallback, NumberOrCallback, VictoryCommonPrimitiveProps, } from "victory-core"; const evaluateProps = (props) => { /** * Potential evaluated props are: * `ariaLabel` * `id` * `style` * `tabIndex` */ const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const id = Helpers.evaluateProp(props.id, props); const style = Helpers.evaluateStyle( Object.assign( { fill: "none", stroke: "black", pointerEvents: "stroke" }, props.style, ), props, ); const tabIndex = Helpers.evaluateProp(props.tabIndex, props); return Object.assign({}, props, { ariaLabel, id, style, tabIndex }); }; const defaultProps = { pathComponent: , role: "presentation", shapeRendering: "auto", }; export const Curve: React.FC = (initialProps) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); const userProps = UserProps.getSafeUserProps(props); const { polar, origin } = props; const lineFunction = LineHelpers.getLineFunction(props); const defaultTransform = polar && origin ? `translate(${origin.x}, ${origin.y})` : undefined; const d = lineFunction(props.data); return React.cloneElement(props.pathComponent!, { ...props.events, ...userProps, "aria-label": props.ariaLabel, d, style: props.style, transform: props.transform || defaultTransform, className: props.className, role: props.role, shapeRendering: props.shapeRendering, clipPath: props.clipPath, tabIndex: props.tabIndex, }); }; export interface CurveProps extends VictoryCommonPrimitiveProps { ariaLabel?: StringOrCallback; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type interpolation?: string | Function; openCurve?: boolean; pathComponent?: React.ReactElement; tabIndex?: NumberOrCallback; } ================================================ FILE: packages/victory-line/src/helper-methods.ts ================================================ import { Helpers, LabelHelpers, Data, Domain, Scale } from "victory-core"; const getCalculatedValues = (props) => { let data = Data.getData(props); if (data.length < 2) { data = []; } const range = { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; const domain = { x: Domain.getDomain(props, "x"), y: Domain.getDomain(props, "y"), }; const scale = { x: Scale.getBaseScale(props, "x") .domain(domain.x) .range(props.horizontal ? range.y : range.x), y: Scale.getBaseScale(props, "y") .domain(domain.y) .range(props.horizontal ? range.x : range.y), }; const origin = props.polar ? props.origin || Helpers.getPolarOrigin(props) : undefined; const defaultStyles = Helpers.getDefaultStyles(props, "line"); const style = Helpers.getStyles(props.style, defaultStyles); return { domain, data, scale, style, origin }; }; export const getBaseProps = (initialProps, fallbackProps) => { const modifiedProps = Helpers.modifyProps( initialProps, fallbackProps, "line", ); const props = Object.assign( {}, modifiedProps, getCalculatedValues(modifiedProps), ); const { data, domain, events, groupComponent, height, horizontal, interpolation, origin, padding, polar, scale, sharedEvents, standalone, style, theme, width, labels, name, disableInlineStyles, } = props; const initialChildProps = { parent: { style: style.parent, scale, data, height, width, name, domain, standalone, polar, origin, padding, horizontal, }, all: { data: { horizontal, polar, origin, scale, data, interpolation, groupComponent, style: disableInlineStyles ? {} : style.data, theme, disableInlineStyles, }, }, }; return data.reduce((childProps, datum, index) => { const text = LabelHelpers.getText(props, datum, index); if ( (text !== undefined && text !== null) || (labels && (events || sharedEvents)) ) { const eventKey = !Helpers.isNil(datum.eventKey) ? datum.eventKey : index; childProps[eventKey] = { labels: LabelHelpers.getProps(props, index) }; } return childProps; }, initialChildProps); }; ================================================ FILE: packages/victory-line/src/index.ts ================================================ export * from "./victory-line"; export * from "./curve"; ================================================ FILE: packages/victory-line/src/victory-line.test.tsx ================================================ import { fireEvent, render, screen } from "@testing-library/react"; import random from "lodash/random"; import React from "react"; import { VictoryChart } from "victory-chart"; import { Helpers } from "victory-core"; import { curveCatmullRom } from "victory-vendor/d3-shape"; import { calculateD3Path } from "../../../test/helpers"; import { Curve } from "./curve"; import { VictoryLine, VictoryLineProps } from "./victory-line"; describe("components/victory-line", () => { describe("default component rendering", () => { it("attaches safe user props to the container component", () => { render( , ); const container = screen.getByTestId("victory-line"); expect(screen.getByLabelText("Chart")).toBeDefined(); expect(container).not.toHaveAttribute("unsafe-prop"); expect(container.nodeName).toEqual("svg"); }); it("attaches safe user props to the group component if the component is rendered inside a VictoryChart", () => { render( , { wrapper: VictoryChart }, ); const container = screen.getByTestId("victory-line"); expect(screen.getByLabelText("Chart")).toBeDefined(); expect(container).not.toHaveAttribute("unsafe-prop"); expect(container.tagName).toEqual("g"); }); it("renders an svg with the correct viewBox", () => { const { container } = render(); const viewBoxValue = `0 0 ${450} ${300}`; expect(container.querySelector("svg")!.getAttribute("viewBox")).toEqual( viewBoxValue, ); }); }); describe("rendering with data", () => { it("renders one dataComponent for the line", () => { const data = [ { x: 1, y: 1 }, { x: 2, y: 4 }, { x: 3, y: 5 }, { x: 4, y: 2 }, { x: 5, y: 3 }, { x: 6, y: 4 }, { x: 7, y: 6 }, ]; render( } />, ); expect(screen.getByTestId("line")).toBeDefined(); }); it("renders the correct d3Shape path", () => { const props: VictoryLineProps = { interpolation: "linear", scale: "linear", padding: 50, width: 400, height: 300, data: [ { x: 0, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 2 }, ], }; const { container } = render(); expect(container.querySelector("path")!.getAttribute("d")).toEqual( calculateD3Path(props, "line", 0), ); }); it("renders the correct d3Shape path with custom interpolation string property", () => { const props: VictoryLineProps = { interpolation: "catmullRom", scale: "linear", padding: 50, width: 400, height: 300, data: [ { x: 0, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 2 }, ], }; const { container } = render(); expect(container.querySelector("path")!.getAttribute("d")).toEqual( calculateD3Path(props, "line", 0), ); }); it("renders the correct d3Shape path with custom interpolation function", () => { const props: VictoryLineProps = { interpolation: curveCatmullRom, scale: "linear", padding: 50, width: 400, height: 300, data: [ { x: 0, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 2 }, ], }; const { container } = render(); expect(container.querySelector("path")!.getAttribute("d")).toEqual( calculateD3Path(props, "line", 0), ); }); }); describe("rendering with accessors", () => { it("renders array-type data", () => { const data = [ [1, 2], [3, 4], ]; const { container } = render(); const lines = container.querySelectorAll("path"); expect(lines).toHaveLength(1); }); it("renders deeply nested data", () => { const data = [ { a: { b: [{ x: 1, y: 2 }] } }, { a: { b: [{ x: 3, y: 4 }] } }, ]; const { container } = render( , ); const lines = container.querySelectorAll("path"); expect(lines).toHaveLength(1); }); it("renders data ordered by x-value, by default", () => { const data = [{ t: 0 /* x: 10, y: 1*/ }, { t: 1 /* x: 9, y: 1*/ }]; const { container } = render( 10 - t} y={() => 1} dataComponent={ JSON.stringify(props.data)} /> } />, ); const line = container.querySelector("path"); const renderedData = JSON.parse(line!.getAttribute("data-json") || ""); expect(renderedData[0].t).toEqual(1); expect(renderedData[1].t).toEqual(0); }); it("renders data ordered by value of sortKey, if given", () => { const data = [{ t: 0 /* x: 10, y: 1*/ }, { t: 1 /* x: 9, y: 1*/ }]; const { container } = render( 10 - t} y={() => 1} dataComponent={ JSON.stringify(props.data)} /> } />, ); const line = container.querySelector("path"); const renderedData = JSON.parse(line!.getAttribute("data-json")!).map( ({ t }) => t, ); expect(renderedData).toEqual([0, 1]); }); }); describe("event handling", () => { it("attaches an event to the parent svg", () => { const clickHandler = jest.fn(); render( , ); const svg = screen.getByTestId("container"); fireEvent.click(svg); expect(clickHandler).toHaveBeenCalled(); }); it("attaches an event to data", () => { const clickHandler = jest.fn(); render( } events={[ { target: "data", eventHandlers: { onClick: clickHandler }, }, ]} />, ); const line = screen.getByTestId("line"); fireEvent.click(line); expect(clickHandler).toHaveBeenCalled(); }); it("attaches an event to a label", () => { const clickHandler = jest.fn(); render( datum.x} events={[ { target: "labels", eventHandlers: { onClick: clickHandler }, }, ]} />, ); const label = screen.getByText("1"); fireEvent.click(label); expect(clickHandler).toHaveBeenCalled(); }); }); describe("accessibility", () => { it("adds an aria role to a line segment", () => { const { container } = render(); expect(container.querySelector("path")!.getAttribute("role")).toEqual( "presentation", ); }); it("adds an aria role to each line segment", () => { const data = [ { x: 1, y: 1 }, { x: 2, y: 3 }, { x: 3, y: 5 }, { x: 4, y: 2 }, { x: 5, y: null }, { x: 6, y: null }, { x: 7, y: 6 }, { x: 8, y: 7 }, { x: 9, y: 8 }, { x: 10, y: 12 }, ]; const { container } = render(); container.querySelectorAll("path").forEach((p) => { expect(p.getAttribute("role")).toEqual("presentation"); }); }); it("adds aria-label and tabIndex to Curve primitive", () => { const ariaTestData = Helpers.range(4).map((x) => ({ x, y: random(1, 7), })); const { container } = render( `data point ${data![2].x + 1}'s x value is ${data![2].x}` } tabIndex={3} /> } />, ); const path = container.querySelector("path"); expect(path?.getAttribute("aria-label")).toEqual( `data point 3's x value is 2`, ); expect(parseInt(path!.getAttribute("tabindex")!)).toEqual(3); }); }); }); ================================================ FILE: packages/victory-line/src/victory-line.tsx ================================================ import React from "react"; import { getBaseProps } from "./helper-methods"; import { Curve } from "./curve"; import { Helpers, VictoryLabel, addEvents, VictoryContainer, VictoryTheme, DefaultTransitions, VictoryClipContainer, Data, Domain, UserProps, EventPropTypeInterface, InterpolationPropType, StringOrNumberOrCallback, VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, VictoryStyleInterface, EventsMixinClass, } from "victory-core"; const fallbackProps = { width: 450, height: 300, padding: 50, interpolation: "linear", }; const options = { components: [ { name: "parent", index: "parent" }, { name: "data", index: "all" }, { name: "labels" }, ], }; // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryLineBase extends EventsMixinClass {} class VictoryLineBase extends React.Component { constructor(props) { super(props); } static animationWhitelist = [ "data", "domain", "height", "padding", "samples", "style", "width", ]; static displayName = "VictoryLine"; static role = "line"; static defaultTransitions = DefaultTransitions.continuousTransitions(); static defaultPolarTransitions = DefaultTransitions.continuousPolarTransitions(); static continuous = true; static defaultProps: VictoryLineProps = { containerComponent: , dataComponent: , labelComponent: , groupComponent: , samples: 50, sortKey: "x", sortOrder: "ascending", standalone: true, theme: VictoryTheme.grayscale, }; static getDomain = Domain.getDomain; static getData = Data.getData; static getBaseProps(props) { return getBaseProps(props, fallbackProps); } static expectedComponents = [ "dataComponent", "labelComponent", "groupComponent", "containerComponent", ]; // Overridden in native versions shouldAnimate() { return !!this.props.animate; } render() { const { animationWhitelist, role } = VictoryLineBase; const props = Helpers.modifyProps(this.props, fallbackProps, role); if (this.shouldAnimate()) { return this.animateComponent(props, animationWhitelist); } const children = this.renderContinuousData(props); const component = props.standalone ? this.renderContainer(props.containerComponent, children) : children; return UserProps.withSafeUserProps(component, props); } } export const VictoryLine = addEvents(VictoryLineBase, options); export type VictoryLineTTargetType = "data" | "labels" | "parent"; export interface VictoryLineProps extends VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps { events?: EventPropTypeInterface[]; eventKey?: StringOrNumberOrCallback | string[]; // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type interpolation?: InterpolationPropType | Function; style?: VictoryStyleInterface; } ================================================ FILE: packages/victory-line/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-line/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-native/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-native/CHANGELOG.md ================================================ # victory-native ## 37.3.6 ### Patch Changes - Fixes clipping/loss of interactivity bug in victory-zoom-container ([#3026](https://github.com/FormidableLabs/victory/pull/3026)) ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Refactor containers and portal to function components ([#2799](https://github.com/FormidableLabs/victory/pull/2799)) * Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ### Patch Changes - Fix victory-native component prop types ([#2785](https://github.com/FormidableLabs/victory/pull/2785)) ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ### Patch Changes - Migrate victory-native to TypeScript ([#2739](https://github.com/FormidableLabs/victory/pull/2739)) ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) * Use legacy npm dist-tag to avoid conflicts with victory-native-xl ([#2764](https://github.com/FormidableLabs/victory/pull/2764)) ## 36.8.4 ## 36.8.3 ## 36.8.2 ### Patch Changes - Assign merged props to a const instead of modifying initialProps ([#2718](https://github.com/FormidableLabs/victory/pull/2718)) ## 36.8.1 ## 36.8.0 ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Added `react` as a `peerDependency` (fixes [#2478](https://github.com/FormidableLabs/victory/issues/2478)) ([#2479](https://github.com/FormidableLabs/victory/pull/2479)) - Updated dependencies [[`c5b4f660c`](https://github.com/FormidableLabs/victory/commit/c5b4f660cb91d8d1979d216b846a44f0c5030ec1), [`1da86d186`](https://github.com/FormidableLabs/victory/commit/1da86d18615bf75db35cfc55cc8e3d5c0b5772b6)]: - victory-zoom-container@36.6.8 - victory-stack@36.6.8 - victory@36.6.8 - victory-area@36.6.8 - victory-axis@36.6.8 - victory-bar@36.6.8 - victory-box-plot@36.6.8 - victory-brush-container@36.6.8 - victory-brush-line@36.6.8 - victory-candlestick@36.6.8 - victory-chart@36.6.8 - victory-core@36.6.8 - victory-create-container@36.6.8 - victory-cursor-container@36.6.8 - victory-errorbar@36.6.8 - victory-group@36.6.8 - victory-histogram@36.6.8 - victory-legend@36.6.8 - victory-line@36.6.8 - victory-pie@36.6.8 - victory-polar-axis@36.6.8 - victory-scatter@36.6.8 - victory-selection-container@36.6.8 - victory-shared-events@36.6.8 - victory-tooltip@36.6.8 - victory-voronoi@36.6.8 - victory-voronoi-container@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies [[`3f476632f`](https://github.com/FormidableLabs/victory/commit/3f476632fcd41c31cb69fcfae74d1bbaaa78103d)]: - victory-line@36.6.7 - victory@36.6.7 - victory-area@36.6.7 - victory-axis@36.6.7 - victory-bar@36.6.7 - victory-box-plot@36.6.7 - victory-brush-container@36.6.7 - victory-brush-line@36.6.7 - victory-candlestick@36.6.7 - victory-chart@36.6.7 - victory-core@36.6.7 - victory-create-container@36.6.7 - victory-cursor-container@36.6.7 - victory-errorbar@36.6.7 - victory-group@36.6.7 - victory-histogram@36.6.7 - victory-legend@36.6.7 - victory-pie@36.6.7 - victory-polar-axis@36.6.7 - victory-scatter@36.6.7 - victory-selection-container@36.6.7 - victory-shared-events@36.6.7 - victory-stack@36.6.7 - victory-tooltip@36.6.7 - victory-voronoi@36.6.7 - victory-voronoi-container@36.6.7 - victory-zoom-container@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies [[`6c6764f73`](https://github.com/FormidableLabs/victory/commit/6c6764f734b9655170c75798b7fe1c6d9e63f2a5)]: - victory-brush-container@36.6.6 - victory-cursor-container@36.6.6 - victory-selection-container@36.6.6 - victory-voronoi-container@36.6.6 - victory-zoom-container@36.6.6 - victory@36.6.6 - victory-area@36.6.6 - victory-axis@36.6.6 - victory-bar@36.6.6 - victory-box-plot@36.6.6 - victory-brush-line@36.6.6 - victory-candlestick@36.6.6 - victory-chart@36.6.6 - victory-core@36.6.6 - victory-create-container@36.6.6 - victory-errorbar@36.6.6 - victory-group@36.6.6 - victory-histogram@36.6.6 - victory-legend@36.6.6 - victory-line@36.6.6 - victory-pie@36.6.6 - victory-polar-axis@36.6.6 - victory-scatter@36.6.6 - victory-shared-events@36.6.6 - victory-stack@36.6.6 - victory-tooltip@36.6.6 - victory-voronoi@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416), [`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-zoom-container@36.6.5 - victory-core@36.6.5 - victory@36.6.5 - victory-area@36.6.5 - victory-axis@36.6.5 - victory-bar@36.6.5 - victory-box-plot@36.6.5 - victory-brush-container@36.6.5 - victory-brush-line@36.6.5 - victory-candlestick@36.6.5 - victory-chart@36.6.5 - victory-create-container@36.6.5 - victory-cursor-container@36.6.5 - victory-errorbar@36.6.5 - victory-group@36.6.5 - victory-histogram@36.6.5 - victory-legend@36.6.5 - victory-line@36.6.5 - victory-pie@36.6.5 - victory-polar-axis@36.6.5 - victory-scatter@36.6.5 - victory-selection-container@36.6.5 - victory-shared-events@36.6.5 - victory-stack@36.6.5 - victory-tooltip@36.6.5 - victory-voronoi@36.6.5 - victory-voronoi-container@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`57ed0fe30`](https://github.com/FormidableLabs/victory/commit/57ed0fe304dbc8753da1126a02d44de8004e96aa), [`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory@36.6.4 - victory-create-container@36.6.4 - victory-selection-container@36.6.4 - victory-voronoi-container@36.6.4 - victory-zoom-container@36.6.4 - victory-core@36.6.4 - victory-area@36.6.4 - victory-axis@36.6.4 - victory-bar@36.6.4 - victory-box-plot@36.6.4 - victory-brush-container@36.6.4 - victory-brush-line@36.6.4 - victory-candlestick@36.6.4 - victory-chart@36.6.4 - victory-cursor-container@36.6.4 - victory-errorbar@36.6.4 - victory-group@36.6.4 - victory-histogram@36.6.4 - victory-legend@36.6.4 - victory-line@36.6.4 - victory-pie@36.6.4 - victory-polar-axis@36.6.4 - victory-scatter@36.6.4 - victory-shared-events@36.6.4 - victory-stack@36.6.4 - victory-tooltip@36.6.4 - victory-voronoi@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory@36.6.3 - victory-area@36.6.3 - victory-axis@36.6.3 - victory-bar@36.6.3 - victory-box-plot@36.6.3 - victory-brush-container@36.6.3 - victory-brush-line@36.6.3 - victory-candlestick@36.6.3 - victory-chart@36.6.3 - victory-core@36.6.3 - victory-create-container@36.6.3 - victory-cursor-container@36.6.3 - victory-errorbar@36.6.3 - victory-group@36.6.3 - victory-histogram@36.6.3 - victory-legend@36.6.3 - victory-line@36.6.3 - victory-pie@36.6.3 - victory-polar-axis@36.6.3 - victory-scatter@36.6.3 - victory-selection-container@36.6.3 - victory-shared-events@36.6.3 - victory-stack@36.6.3 - victory-tooltip@36.6.3 - victory-voronoi@36.6.3 - victory-voronoi-container@36.6.3 - victory-zoom-container@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies [[`877c16923`](https://github.com/FormidableLabs/victory/commit/877c169230c24ab02f570a21dca10a2dce0dcf4e)]: - victory-cursor-container@36.6.2 - victory@36.6.2 - victory-area@36.6.2 - victory-axis@36.6.2 - victory-bar@36.6.2 - victory-box-plot@36.6.2 - victory-brush-container@36.6.2 - victory-brush-line@36.6.2 - victory-candlestick@36.6.2 - victory-chart@36.6.2 - victory-core@36.6.2 - victory-create-container@36.6.2 - victory-errorbar@36.6.2 - victory-group@36.6.2 - victory-histogram@36.6.2 - victory-legend@36.6.2 - victory-line@36.6.2 - victory-pie@36.6.2 - victory-polar-axis@36.6.2 - victory-scatter@36.6.2 - victory-selection-container@36.6.2 - victory-shared-events@36.6.2 - victory-stack@36.6.2 - victory-tooltip@36.6.2 - victory-voronoi@36.6.2 - victory-voronoi-container@36.6.2 - victory-zoom-container@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`f7d50fa38`](https://github.com/FormidableLabs/victory/commit/f7d50fa381998c068a8b91af9818a6bc726b0dcd), [`f7d50fa38`](https://github.com/FormidableLabs/victory/commit/f7d50fa381998c068a8b91af9818a6bc726b0dcd), [`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-group@36.6.1 - victory-stack@36.6.1 - victory-core@36.6.1 - victory-axis@36.6.1 - victory-errorbar@36.6.1 - victory-scatter@36.6.1 - victory-cursor-container@36.6.1 - victory@36.6.1 - victory-area@36.6.1 - victory-bar@36.6.1 - victory-box-plot@36.6.1 - victory-brush-container@36.6.1 - victory-brush-line@36.6.1 - victory-candlestick@36.6.1 - victory-chart@36.6.1 - victory-create-container@36.6.1 - victory-histogram@36.6.1 - victory-legend@36.6.1 - victory-line@36.6.1 - victory-pie@36.6.1 - victory-polar-axis@36.6.1 - victory-selection-container@36.6.1 - victory-shared-events@36.6.1 - victory-tooltip@36.6.1 - victory-voronoi@36.6.1 - victory-voronoi-container@36.6.1 - victory-zoom-container@36.6.1 ## 36.6.0 ### Patch Changes - Updated dependencies [[`0c827edb4`](https://github.com/FormidableLabs/victory/commit/0c827edb4daf6fa61ee2a561df27ddf89ccc649f), [`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`f7bf134e5`](https://github.com/FormidableLabs/victory/commit/f7bf134e58bc1660c28f83f0eede3b19048d2656), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-box-plot@36.6.0 - victory-core@36.6.0 - victory-errorbar@36.6.0 - victory-brush-container@36.6.0 - victory-brush-line@36.6.0 - victory-area@36.6.0 - victory-axis@36.6.0 - victory-bar@36.6.0 - victory-candlestick@36.6.0 - victory-create-container@36.6.0 - victory-cursor-container@36.6.0 - victory-legend@36.6.0 - victory-line@36.6.0 - victory-pie@36.6.0 - victory-polar-axis@36.6.0 - victory-scatter@36.6.0 - victory-selection-container@36.6.0 - victory-shared-events@36.6.0 - victory-stack@36.6.0 - victory-tooltip@36.6.0 - victory-voronoi@36.6.0 - victory-voronoi-container@36.6.0 - victory-zoom-container@36.6.0 - victory@36.6.0 - victory-chart@36.6.0 - victory-group@36.6.0 - victory-histogram@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-native/README.md ================================================ # Victory Native [![Maintenance Status][maintenance-image]](#maintenance-status) ### Usage Install victory-native: ```sh $ npm install victory-native@legacy --save ``` Install react-native-svg: ```sh $ npm install react-native-svg --save ``` Link react-native: ```sh $ react-native link react-native-svg ``` **`victory-native@^33.0.0` requires `react-native-svg@^9.0.0` and `react-native@~0.60.0`** **Please see [Peer Dependencies and Version Requirements](#peer-dependencies-and-version-requirements) for requirements for previous versions of `victory-native`** Import charts from `victory-native`. For example, ```jsx import React, { Component } from "react"; import { VictoryBar } from "victory-native"; class App extends Component { render() { return ( ); } } export default App; ``` ### Peer Dependencies and Version Requirements **Note:** `victory-native` requires the following peer dependencies: - `react-native-svg` - `react` - `react-native` **Note:** `react-native-svg` has strict version requirements for both `react` and `react-native`. Please match versions to those required by `react-native-svg`. See the up-to-date requirements on the [react-native-svg Readme][react-native-svg-readme]. We encourage you to use the latest version of `react-native-svg` possible for your project, as `victory-native` issues are frequently solved by `react-native-svg` bugfixes. - `victory-native@^33.0.0` requires `react-native-svg@^9.0.0` and `react-native@~0.60.0` * `victory-native@^30.0.0` requires `react-native-svg@6.1.x` or `react-native-svg@^6.5.0`and above * `victory-native@^0.16.2` requires `react-native-svg@6.1.x` or `react-native-svg@^6.5.0` * ~~`victory-native@~0.16.0` requires `react-native-svg@6.0.0`~~ No longer supported * ~~`victory-native@~0.15.0` requires `react-native-svg@^5.0.0`~~ No longer supported ### Local Development and Demo If you'd like to contribute to `victory-native`, you can use the local demo app to test your changes on the iOS simulator. The demo app uses [Expo](https://expo.dev/) to streamline this dev experience. To open the demo app, just fire up the expo app. ```sh # Install victory and its dependencies $ git clone https://github.com/FormidableLabs/victory $ cd victory $ pnpm install $ pnpm run build --watch # In a new terminal, open up the React Native demo app $ cd victory/demo/rn $ pnpm install $ pnpm start ``` Once Expo has fired up, it should open a web browser window where you can find instructions to open the demo application (either on a simulator or a physical device using the Expo Go app). Changes to the Victory and Victory Native source code will be reflected in the demo app. ### Documentation See the docs and examples on [the Victory website](https://commerce.nearform.com/open-source/victory/docs/native/). ## Contributor Covenant Code of Conduct Please review our [Code of Conduct][code] before contributing. ### Maintenance Status **Active:** Formidable is actively working on this project, and we expect to continue for work for the foreseeable future. Bug reports, feature requests and pull requests are welcome. [code]: https://github.com/FormidableLabs/.github/blob/master/CODE_OF_CONDUCT.md [react-native-svg-readme]: https://github.com/react-native-community/react-native-svg#notice [maintenance-image]: https://img.shields.io/badge/maintenance-active-green.svg ================================================ FILE: packages/victory-native/package.json ================================================ { "name": "victory-native", "version": "37.3.6", "description": "Native Port for Victory", "keywords": [ "data visualization", "React", "React Native", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "source": "src/index.ts", "sideEffects": false, "main": "lib/index.js", "files": [ "src", "lib", "index.js" ], "author": "Formidable", "license": "MIT", "dependencies": { "hoist-non-react-statics": "^3.3.2", "lodash": "^4.17.21", "react-fast-compare": "^3.2.0", "victory": "^37.3.6", "victory-area": "^37.3.6", "victory-axis": "^37.3.6", "victory-bar": "^37.3.6", "victory-box-plot": "^37.3.6", "victory-brush-container": "^37.3.6", "victory-brush-line": "^37.3.6", "victory-candlestick": "^37.3.6", "victory-chart": "^37.3.6", "victory-core": "^37.3.6", "victory-create-container": "^37.3.6", "victory-cursor-container": "^37.3.6", "victory-errorbar": "^37.3.6", "victory-group": "^37.3.6", "victory-histogram": "^37.3.6", "victory-legend": "^37.3.6", "victory-line": "^37.3.6", "victory-pie": "^37.3.6", "victory-polar-axis": "^37.3.6", "victory-scatter": "^37.3.6", "victory-selection-container": "^37.3.6", "victory-shared-events": "^37.3.6", "victory-stack": "^37.3.6", "victory-tooltip": "^37.3.6", "victory-voronoi": "^37.3.6", "victory-voronoi-container": "^37.3.6", "victory-zoom-container": "^37.3.6" }, "devDependencies": { "@babel/core": ">=7.18.9", "react-native": ">=0.65.1", "react-native-gesture-handler": ">=1.10.3", "react-native-svg": ">=12.4.3" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true, "tag": "legacy" }, "scripts": { "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "format": "wireit", "format:fix": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "build": { "dependencies": [ "build:lib", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:cjs" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory:build:lib:cjs", "../victory-area:build:lib:cjs", "../victory-axis:build:lib:cjs", "../victory-bar:build:lib:cjs", "../victory-box-plot:build:lib:cjs", "../victory-brush-container:build:lib:cjs", "../victory-brush-line:build:lib:cjs", "../victory-candlestick:build:lib:cjs", "../victory-chart:build:lib:cjs", "../victory-core:build:lib:cjs", "../victory-create-container:build:lib:cjs", "../victory-cursor-container:build:lib:cjs", "../victory-errorbar:build:lib:cjs", "../victory-group:build:lib:cjs", "../victory-histogram:build:lib:cjs", "../victory-legend:build:lib:cjs", "../victory-line:build:lib:cjs", "../victory-pie:build:lib:cjs", "../victory-polar-axis:build:lib:cjs", "../victory-scatter:build:lib:cjs", "../victory-selection-container:build:lib:cjs", "../victory-shared-events:build:lib:cjs", "../victory-stack:build:lib:cjs", "../victory-tooltip:build:lib:cjs", "../victory-voronoi:build:lib:cjs", "../victory-voronoi-container:build:lib:cjs", "../victory-zoom-container:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:esm": { "command": "echo victory-native has no esm build", "files": [], "output": [] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory:types:create", "../victory-area:types:create", "../victory-axis:types:create", "../victory-bar:types:create", "../victory-box-plot:types:create", "../victory-brush-container:types:create", "../victory-brush-line:types:create", "../victory-candlestick:types:create", "../victory-chart:types:create", "../victory-core:types:create", "../victory-create-container:types:create", "../victory-cursor-container:types:create", "../victory-errorbar:types:create", "../victory-group:types:create", "../victory-histogram:types:create", "../victory-legend:types:create", "../victory-line:types:create", "../victory-pie:types:create", "../victory-polar-axis:types:create", "../victory-scatter:types:create", "../victory-selection-container:types:create", "../victory-shared-events:types:create", "../victory-stack:types:create", "../victory-tooltip:types:create", "../victory-voronoi:types:create", "../victory-voronoi-container:types:create", "../victory-zoom-container:types:create", "../victory-vendor:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory:types:create", "../victory-area:types:create", "../victory-axis:types:create", "../victory-bar:types:create", "../victory-box-plot:types:create", "../victory-brush-container:types:create", "../victory-brush-line:types:create", "../victory-candlestick:types:create", "../victory-chart:types:create", "../victory-core:types:create", "../victory-create-container:types:create", "../victory-cursor-container:types:create", "../victory-errorbar:types:create", "../victory-group:types:create", "../victory-histogram:types:create", "../victory-legend:types:create", "../victory-line:types:create", "../victory-pie:types:create", "../victory-polar-axis:types:create", "../victory-scatter:types:create", "../victory-selection-container:types:create", "../victory-shared-events:types:create", "../victory-stack:types:create", "../victory-tooltip:types:create", "../victory-voronoi:types:create", "../victory-voronoi-container:types:create", "../victory-zoom-container:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory:types:create", "../victory-area:types:create", "../victory-axis:types:create", "../victory-bar:types:create", "../victory-box-plot:types:create", "../victory-brush-container:types:create", "../victory-brush-line:types:create", "../victory-candlestick:types:create", "../victory-chart:types:create", "../victory-core:types:create", "../victory-create-container:types:create", "../victory-cursor-container:types:create", "../victory-errorbar:types:create", "../victory-group:types:create", "../victory-histogram:types:create", "../victory-legend:types:create", "../victory-line:types:create", "../victory-pie:types:create", "../victory-polar-axis:types:create", "../victory-scatter:types:create", "../victory-selection-container:types:create", "../victory-shared-events:types:create", "../victory-stack:types:create", "../victory-tooltip:types:create", "../victory-voronoi:types:create", "../victory-voronoi-container:types:create", "../victory-zoom-container:types:create", "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory:types:create", "../victory-area:types:create", "../victory-axis:types:create", "../victory-bar:types:create", "../victory-box-plot:types:create", "../victory-brush-container:types:create", "../victory-brush-line:types:create", "../victory-candlestick:types:create", "../victory-chart:types:create", "../victory-core:types:create", "../victory-create-container:types:create", "../victory-cursor-container:types:create", "../victory-errorbar:types:create", "../victory-group:types:create", "../victory-histogram:types:create", "../victory-legend:types:create", "../victory-line:types:create", "../victory-pie:types:create", "../victory-polar-axis:types:create", "../victory-scatter:types:create", "../victory-selection-container:types:create", "../victory-shared-events:types:create", "../victory-stack:types:create", "../victory-tooltip:types:create", "../victory-voronoi:types:create", "../victory-voronoi-container:types:create", "../victory-zoom-container:types:create", "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "echo \"victory-native (legacy) has no tests\"", "files": [], "output": [] } } } ================================================ FILE: packages/victory-native/src/components/victory-area.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { VictoryArea as VictoryAreaBase, VictoryAreaProps, } from "victory-area/es"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { VictoryClipContainer } from "./victory-clip-container"; import { Area } from "./victory-primitives/area"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryArea = wrapCoreComponent({ Component: VictoryAreaBase, defaultProps: { ...VictoryAreaBase.defaultProps, dataComponent: , labelComponent: , containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-axis.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryAxis as VictoryAxisBase, VictoryAxisProps, } from "victory-axis/es"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { LineSegment } from "./victory-primitives/line-segment"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryAxis = wrapCoreComponent({ Component: VictoryAxisBase, defaultProps: { ...VictoryAxisBase.defaultProps, axisComponent: , axisLabelComponent: , tickLabelComponent: , tickComponent: , gridComponent: , containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-bar.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { Bar } from "./victory-primitives/bar"; import { VictoryBar as VictoryBarBase, VictoryBarProps } from "victory-bar/es"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryBar = wrapCoreComponent({ Component: VictoryBarBase, defaultProps: { ...VictoryBarBase.defaultProps, dataComponent: , labelComponent: , containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-boxplot.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryBoxPlot as VictoryBoxPlotBase, VictoryBoxPlotProps, } from "victory-box-plot/es"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { Border } from "./victory-primitives/border"; import { Whisker } from "./victory-primitives/whisker"; import { LineSegment } from "./victory-primitives/line-segment"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryBoxPlot = wrapCoreComponent({ Component: VictoryBoxPlotBase, defaultProps: { ...VictoryBoxPlotBase.defaultProps, maxComponent: , maxLabelComponent: , medianComponent: , medianLabelComponent: , minComponent: , minLabelComponent: , q1Component: , q1LabelComponent: , q3Component: , q3LabelComponent: , containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-brush-container.tsx ================================================ /* eslint-disable react/no-multi-comp */ import React from "react"; import { Rect } from "react-native-svg"; import { VictoryEventHandler } from "victory-core"; import { BrushHelpers, VictoryBrushContainerProps, useVictoryBrushContainer, VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS, } from "victory-brush-container"; import { VictoryContainer } from "./victory-container"; import NativeHelpers from "../helpers/native-helpers"; export interface VictoryBrushContainerNativeProps extends VictoryBrushContainerProps { disableContainerEvents?: boolean; onTouchStart?: VictoryEventHandler; onTouchEnd?: VictoryEventHandler; } // ensure the selection component get native styles const RectWithStyle = ({ style = {}, ...otherProps }: { style?: Record; }) => ; export const VictoryBrushContainer = ( initialProps: VictoryBrushContainerNativeProps, ) => { const { props, children } = useVictoryBrushContainer({ ...initialProps, brushComponent: initialProps.brushComponent ?? , handleComponent: initialProps.handleComponent ?? , }); return {children}; }; VictoryBrushContainer.role = "container"; VictoryBrushContainer.defaultEvents = ( initialProps: VictoryBrushContainerNativeProps, ) => { const props = { ...VICTORY_BRUSH_CONTAINER_DEFAULT_PROPS, ...initialProps }; const createEventHandler = (handler: VictoryEventHandler, cancel: boolean): VictoryEventHandler => // eslint-disable-next-line max-params (event, targetProps, eventKey, context) => { if (props.disable) { return {}; } if (cancel) { BrushHelpers.onGlobalMouseMove.cancel(); } return handler(event, { ...props, ...targetProps }, eventKey, context); }; return [ { target: "parent", eventHandlers: { onTouchStart: createEventHandler(BrushHelpers.onMouseDown, true), onTouchMove: createEventHandler(BrushHelpers.onGlobalMouseMove, false), onTouchEnd: createEventHandler(BrushHelpers.onGlobalMouseUp, true), }, }, ]; }; ================================================ FILE: packages/victory-native/src/components/victory-brush-line.tsx ================================================ import React from "react"; import { PanResponder } from "react-native"; import { G, Rect } from "react-native-svg"; import get from "lodash/get"; import { VictoryEventHandler } from "victory-core"; import { VictoryBrushLine as VictoryBrushLineBase, VictoryBrushLineProps, } from "victory-brush-line/es"; import { LineSegment } from "./victory-primitives/line-segment"; import NativeHelpers from "../helpers/native-helpers"; // ensure the selection component get native styles import { wrapCoreComponent } from "../helpers/wrap-core-component"; export interface VictoryNativeBrushLineProps extends VictoryBrushLineProps { onTouchStart?: VictoryEventHandler; onTouchEnd?: VictoryEventHandler; } const RectWithStyle = ({ style = {}, ...otherProps }: { style?: Record; }) => ; const yes = () => true; const no = () => false; const vblDefaultEvents = VictoryBrushLineBase.defaultEvents; class VictoryNativeBrushLine< TProps extends VictoryNativeBrushLineProps, > extends VictoryBrushLineBase { static displayName = "VictoryNativeBrushLine"; defaultEvents = function (props) { if (props.disable) return undefined; // refer to web victory brush line to see the events we're aliasing here // we're just remapping the existing default events to their mobile equivalent handlers const existingEventHandlers = vblDefaultEvents(props)?.[0].eventHandlers; return [ { target: props.type, eventHandlers: { onTouchStart: existingEventHandlers?.onMouseDown, onTouchMove: existingEventHandlers?.onMouseMove, onTouchEnd: existingEventHandlers?.onMouseUp, }, }, ]; }; panResponder: any; constructor(props: TProps) { super(props); this.panResponder = this.getResponder(); } // mimics victory-container responders, except for comment below getResponder() { let shouldBlockNativeResponder = no; if ( this.props && (this.props.allowDrag || this.props.allowDraw || this.props.allowResize) ) { shouldBlockNativeResponder = yes; } return PanResponder.create({ onStartShouldSetPanResponder: yes, onStartShouldSetPanResponderCapture: no, onMoveShouldSetPanResponder: yes, onMoveShouldSetPanResponderCapture: yes, onShouldBlockNativeResponder: shouldBlockNativeResponder, // prevent parent responder (the VictoryContainer) from stealing touches onPanResponderTerminationRequest: no, onPanResponderGrant: this.handleResponderGrant.bind(this), onPanResponderMove: this.handleResponderMove.bind(this), onPanResponderRelease: this.handleResponderEnd.bind(this), onPanResponderTerminate: this.handleResponderEnd.bind(this), }); } callOptionalEventCallback(eventName, evt) { const callback = get(this.props.events, eventName); if (callback) { evt.persist(); // RN nativeEvent is reused. see https://fb.me/react-event-pooling (callback as any)(evt, this.props, "__unknownEventKey__", eventName); } } handleResponderGrant(evt) { if (this.props.onTouchStart) { this.props.onTouchStart(evt); } this.callOptionalEventCallback("onTouchStart", evt); } handleResponderMove(evt) { const { touches } = evt.nativeEvent; if (touches && touches.length === 2) { this.callOptionalEventCallback("onTouchPinch", evt); } else { this.callOptionalEventCallback("onTouchMove", evt); } } handleResponderEnd(evt) { if (this.props.onTouchEnd) { this.props.onTouchEnd(evt); } this.callOptionalEventCallback("onTouchEnd", evt); } render(): React.ReactElement { return ( {this.renderLine(this.props)} {this.renderBrushArea(this.props)} {this.renderBrush(this.props)} {this.renderHandles(this.props)} ); } } export const VictoryBrushLine = wrapCoreComponent({ Component: VictoryNativeBrushLine, defaultProps: { ...VictoryNativeBrushLine.defaultProps, brushComponent: , brushAreaComponent: , handleComponent: , groupComponent: , lineComponent: , }, }); ================================================ FILE: packages/victory-native/src/components/victory-candlestick.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { Candle } from "./victory-primitives/candle"; import { VictoryCandlestick as VictoryCandlestickBase, VictoryCandlestickProps, } from "victory-candlestick/es"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryCandlestick = wrapCoreComponent({ Component: VictoryCandlestickBase, defaultProps: { ...VictoryCandlestickBase.defaultProps, dataComponent: , labelComponent: , containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-chart.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryChart as VictoryChartBase, VictoryChartProps, } from "victory-chart/es"; import { Background } from "./victory-primitives/background"; import { VictoryAxis } from "./victory-axis"; import { VictoryPolarAxis } from "./victory-polar-axis"; import { VictoryContainer } from "./victory-container"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryChart = wrapCoreComponent({ Component: VictoryChartBase, defaultProps: { backgroundComponent: , containerComponent: , groupComponent: , defaultAxes: { independent: , dependent: , }, defaultPolarAxes: { independent: , dependent: , }, prependDefaultAxes: true, width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-clip-container.tsx ================================================ import React from "react"; import { G } from "react-native-svg"; import { Circle } from "./victory-primitives/circle"; import { Rect } from "./victory-primitives/rect"; import { ClipPath } from "./victory-primitives/clip-path"; import { VictoryClipContainer as VictoryClipContainerBase } from "victory-core/es"; import uniqueId from "lodash/uniqueId"; export class VictoryClipContainer extends VictoryClipContainerBase { static defaultProps = { ...VictoryClipContainerBase.defaultProps, groupComponent: , rectComponent: , clipPathComponent: , circleComponent: , }; // There seems to be a caching issue with clip paths. // This is required to make clip paths update when animating componentDidUpdate() { this.clipId = uniqueId("victory-clip-"); } } ================================================ FILE: packages/victory-native/src/components/victory-container.tsx ================================================ import React, { RefObject } from "react"; import Svg, { Rect } from "react-native-svg"; import get from "lodash/get"; import { View, PanResponder } from "react-native"; import { VictoryContainerProps, VictoryEventHandler, mergeRefs, useVictoryContainer, PortalProvider, PortalOutlet, } from "victory-core/es"; import NativeHelpers from "../helpers/native-helpers"; import { Portal } from "./victory-portal/portal"; const yes = () => true; const no = () => false; export interface VictoryContainerNativeProps extends VictoryContainerProps { disableContainerEvents?: boolean; onTouchStart?: VictoryEventHandler; onTouchEnd?: VictoryEventHandler; } export const VictoryContainer = (initialProps: VictoryContainerNativeProps) => { const props = useVictoryContainer(initialProps); const { title, desc, width, height, dimensions, children, style, ouiaId, ouiaSafe, ouiaType, ariaLabelledBy, ariaDescribedBy, portalZIndex, viewBox, preserveAspectRatio, userProps, containerRef, events, onTouchStart, onTouchEnd, localContainerRef, disableContainerEvents, } = props; const callOptionalEventCallback = (eventName, event) => { const callback = get(events, eventName); if (callback) { event.persist(); // RN nativeEvent is reused. see https://fb.me/react-event-pooling callback(event, props, "__unknownEventKey__", eventName); } }; const handleResponderGrant = (event) => { if (onTouchStart) { onTouchStart(event); } callOptionalEventCallback("onTouchStart", event); }; const handleResponderMove = (event) => { const { touches } = event.nativeEvent; if (touches && touches.length === 2) { callOptionalEventCallback("onTouchPinch", event); } else { callOptionalEventCallback("onTouchMove", event); } }; const handleResponderEnd = (event) => { if (onTouchEnd) { onTouchEnd(event); } callOptionalEventCallback("onTouchEnd", event); }; const getResponder = () => { let shouldBlockNativeResponder = no; const { allowDrag, allowDraw, allowResize, allowSelection, allowPan, allowZoom, } = props as any; if ( allowDrag || allowDraw || allowResize || allowSelection || allowPan || allowZoom ) { shouldBlockNativeResponder = yes; } return PanResponder.create({ onStartShouldSetPanResponder: yes, onStartShouldSetPanResponderCapture: no, onMoveShouldSetPanResponder: yes, onMoveShouldSetPanResponderCapture: yes, onShouldBlockNativeResponder: shouldBlockNativeResponder, onPanResponderTerminationRequest: yes, onPanResponderGrant: handleResponderGrant, // User has started a touch move onPanResponderMove: handleResponderMove, // Active touch or touches have moved onPanResponderRelease: handleResponderEnd, // The user has released all touches onPanResponderTerminate: handleResponderEnd, // Another component has become the responder }); }; const panResponder = getResponder(); const handlers = disableContainerEvents ? {} : panResponder.panHandlers; const baseStyle = NativeHelpers.getStyle(style, ["width", "height"]); return ( ([ localContainerRef as unknown as RefObject, containerRef as unknown as RefObject, ])} > {/* The following Rect is a temporary solution until the following RNSVG issue is resolved https://github.com/react-native-svg/react-native-svg/issues/1488 */} {title ? {title} : null} {desc ? {desc} : null} {children} } width={width} height={height} viewBox={viewBox} style={{ ...dimensions, overflow: "visible" }} /> ); }; VictoryContainer.role = "container"; ================================================ FILE: packages/victory-native/src/components/victory-cursor-container.tsx ================================================ import React from "react"; import { VictoryEventHandler } from "victory-core"; import { useVictoryCursorContainer, CursorHelpers, VICTORY_CURSOR_CONTAINER_DEFAULT_PROPS, VictoryCursorContainerProps, } from "victory-cursor-container"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { LineSegment } from "./victory-primitives/line-segment"; export interface VictoryCursorContainerNativeProps extends VictoryCursorContainerProps { disableContainerEvents?: boolean; onTouchStart?: VictoryEventHandler; onTouchEnd?: VictoryEventHandler; } export const VictoryCursorContainer = ( initialProps: VictoryCursorContainerNativeProps, ) => { const { props, children } = useVictoryCursorContainer({ ...initialProps, cursorLabelComponent: initialProps.cursorLabelComponent ?? , cursorComponent: initialProps.cursorComponent ?? , }); return {children}; }; VictoryCursorContainer.role = "container"; VictoryCursorContainer.defaultEvents = ( initialProps: VictoryCursorContainerNativeProps, ) => { const props = { ...VICTORY_CURSOR_CONTAINER_DEFAULT_PROPS, ...initialProps }; const createEventHandler = (handler: VictoryEventHandler, disabled?: boolean): VictoryEventHandler => // eslint-disable-next-line max-params (event, targetProps, eventKey, context) => disabled || props.disable ? {} : handler(event, { ...props, ...targetProps }, eventKey, context); return [ { target: "parent", eventHandlers: { onTouchStart: createEventHandler(CursorHelpers.onMouseMove), onTouchMove: createEventHandler(CursorHelpers.onMouseMove), onTouchEnd: createEventHandler(CursorHelpers.onTouchEnd), }, }, ]; }; ================================================ FILE: packages/victory-native/src/components/victory-errorbar.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryErrorBar as VictoryErrorBarBase, VictoryErrorBarProps, } from "victory-errorbar/es"; import { VictoryContainer } from "./victory-container"; import { ErrorBar } from "./victory-primitives/error-bar"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryErrorBar = wrapCoreComponent({ Component: VictoryErrorBarBase, defaultProps: { ...VictoryErrorBarBase.defaultProps, dataComponent: , containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-group.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryGroup as VictoryGroupBase, VictoryGroupProps, } from "victory-group/es"; import { VictoryContainer } from "./victory-container"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryGroup = wrapCoreComponent({ Component: VictoryGroupBase, defaultProps: { containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-histogram.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { Bar } from "./victory-primitives/bar"; import { VictoryHistogram as VictoryHistogramBase, VictoryHistogramProps, } from "victory-histogram/es"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryHistogram = wrapCoreComponent({ Component: VictoryHistogramBase, defaultProps: { ...VictoryHistogramBase.defaultProps, dataComponent: , labelComponent: , containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-label.tsx ================================================ import React from "react"; import { VictoryLabel as VictoryLabelBase, VictoryLabelProps, } from "victory-core/es"; import { G } from "react-native-svg"; import { Text } from "./victory-primitives/text"; import { TSpan } from "./victory-primitives/tspan"; import { Rect } from "./victory-primitives/rect"; export const VictoryLabel = (props: VictoryLabelProps) => ( } tspanComponent={} backgroundComponent={} groupComponent={} {...props} /> ); VictoryLabel.role = VictoryLabelBase.role; ================================================ FILE: packages/victory-native/src/components/victory-legend.tsx ================================================ import React from "react"; import { G } from "react-native-svg"; import { VictoryLegend as VictoryLegendBase, VictoryLegendProps, } from "victory-legend/es"; import { Dimensions } from "react-native"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { Point } from "./victory-primitives/point"; import { Border } from "./victory-primitives/border"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryLegend = wrapCoreComponent({ Component: VictoryLegendBase, defaultProps: { ...VictoryLegendBase.defaultProps, borderComponent: , containerComponent: , dataComponent: , groupComponent: , labelComponent: , titleComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-line.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { VictoryLine as VictoryLineBase, VictoryLineProps, } from "victory-line/es"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { VictoryClipContainer } from "./victory-clip-container"; import { Curve } from "./victory-primitives/curve"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryLine = wrapCoreComponent({ Component: VictoryLineBase, defaultProps: { ...VictoryLineBase.defaultProps, dataComponent: , labelComponent: , containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-pie.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryPie as VictoryPieBase, VictoryPieProps } from "victory-pie/es"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { Slice } from "./victory-primitives/slice"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryPie = wrapCoreComponent({ Component: VictoryPieBase, defaultProps: { ...VictoryPieBase.defaultProps, dataComponent: , labelComponent: , containerComponent: , groupComponent: , height: Dimensions.get("window").width, width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-polar-axis.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryPolarAxis as VictoryPolarAxisBase, VictoryPolarAxisProps, } from "victory-polar-axis/es"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { Arc } from "./victory-primitives/arc"; import { LineSegment } from "./victory-primitives/line-segment"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryPolarAxis = wrapCoreComponent({ Component: VictoryPolarAxisBase, defaultProps: { ...VictoryPolarAxisBase.defaultProps, axisComponent: , axisLabelComponent: , circularAxisComponent: , circularGridComponent: , tickLabelComponent: , tickComponent: , gridComponent: , containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-portal/portal.tsx ================================================ import React, { LegacyRef } from "react"; import { ViewStyle } from "react-native"; import Svg from "react-native-svg"; import { PortalProps } from "victory-core/es"; export const Portal = React.forwardRef( (props, ref) => { const { style, ...rest } = props; return ( } {...rest} /> ); }, ); ================================================ FILE: packages/victory-native/src/components/victory-portal/victory-portal.tsx ================================================ import React from "react"; import { G } from "react-native-svg"; import { VictoryPortal as VictoryPortalBase, VictoryPortalProps, } from "victory-core/es"; export const VictoryPortal = (initialProps: VictoryPortalProps) => { return ( } /> ); }; ================================================ FILE: packages/victory-native/src/components/victory-primitives/arc.tsx ================================================ import React from "react"; import { Path } from "./path"; import { Arc as ArcBase, ArcProps } from "victory-core"; export const Arc = (props: ArcProps) => ( } {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-primitives/area.tsx ================================================ import React from "react"; import { Path } from "./path"; import { G } from "react-native-svg"; import { Area as AreaBase, AreaProps } from "victory-area/es"; export const Area = (props: AreaProps) => ( } groupComponent={} {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-primitives/background.tsx ================================================ import React from "react"; import { Rect } from "./rect"; import { Circle } from "./circle"; import { Background as BackgroundBase, BackgroundProps } from "victory-core/es"; export const Background = (props: BackgroundProps) => ( } rectComponent={} {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-primitives/bar.tsx ================================================ import React from "react"; import { Path } from "./path"; import { Bar as BarBase, BarProps } from "victory-bar/es"; export const Bar = (props: BarProps) => ( } {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-primitives/border.tsx ================================================ import React from "react"; import { Rect } from "./rect"; import { Border as BorderBase, BorderProps } from "victory-core/es"; export const Border = (props: BorderProps) => ( } {...props} /> ); export const Box = Border; ================================================ FILE: packages/victory-native/src/components/victory-primitives/candle.tsx ================================================ import React from "react"; import { Line } from "./line"; import { Rect } from "./rect"; import { G } from "react-native-svg"; import { Candle as CandleBase, CandleProps } from "victory-candlestick/es"; export const Candle = (props: CandleProps) => ( } rectComponent={} groupComponent={} {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-primitives/circle.tsx ================================================ import React from "react"; import { Circle as CircleBase, CircleProps } from "react-native-svg"; import { useGetNativeStyle } from "../../helpers/native-helpers"; import { VictoryNativePrimitiveShapeProps } from "./types"; export type VictoryNativeCircleProps = CircleProps & VictoryNativePrimitiveShapeProps; export const Circle = (props: VictoryNativeCircleProps) => { const { "aria-label": accessibilityLabel, desc, style, ...rest } = props; const nativeStyle = useGetNativeStyle(style); return ( ); }; ================================================ FILE: packages/victory-native/src/components/victory-primitives/clip-path.tsx ================================================ import React from "react"; import { Defs, ClipPath as ClipPathBase, G, ClipPathProps, } from "react-native-svg"; export interface VictoryNativeClipPathProps extends ClipPathProps { clipId?: string; } export const ClipPath = (props: VictoryNativeClipPathProps) => { const { children, clipId } = props; // Wrap in G not to cause exceptions in old react-native-svg // https://github.com/FormidableLabs/victory-native/issues/432#issuecomment-475927581 return ( {children} ); }; ================================================ FILE: packages/victory-native/src/components/victory-primitives/curve.tsx ================================================ import React from "react"; import { Path } from "./path"; import { Curve as CurveBase, CurveProps } from "victory-line/es"; export const Curve = (props: CurveProps) => ( } {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-primitives/error-bar.tsx ================================================ import React from "react"; import { Line } from "./line"; import { G } from "react-native-svg"; import { ErrorBar as ErrorBarBase, ErrorBarProps } from "victory-errorbar/es"; export const ErrorBar = (props: ErrorBarProps) => ( } groupComponent={} {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-primitives/flyout.tsx ================================================ import React from "react"; import { Path } from "./path"; import { Flyout as FlyoutBase, FlyoutProps } from "victory-tooltip/es"; export const Flyout = (props: FlyoutProps) => ( } {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-primitives/line-segment.tsx ================================================ import React from "react"; import { Line } from "./line"; import { LineSegment as LineSegmentBase, LineSegmentProps, } from "victory-core/es"; export const LineSegment = (props: LineSegmentProps) => ( } {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-primitives/line.tsx ================================================ import React from "react"; import { Line as LineBase, LineProps } from "react-native-svg"; import { useGetNativeStyle } from "../../helpers/native-helpers"; import { VictoryNativePrimitiveShapeProps } from "./types"; export type VictoryNativeLineProps = LineProps & VictoryNativePrimitiveShapeProps; export const Line = (props: VictoryNativeLineProps) => { const { "aria-label": accessibilityLabel, desc, style, ...rest } = props; const nativeStyle = useGetNativeStyle(style); return ( ); }; ================================================ FILE: packages/victory-native/src/components/victory-primitives/path.tsx ================================================ import React from "react"; import { Path as PathBase, PathProps } from "react-native-svg"; import { useGetNativeStyle } from "../../helpers/native-helpers"; import { VictoryNativePrimitiveShapeProps } from "./types"; export type VictoryNativePathProps = PathProps & VictoryNativePrimitiveShapeProps; export const Path = (props: VictoryNativePathProps) => { const { "aria-label": accessibilityLabel, desc, style, ...rest } = props; const nativeStyle = useGetNativeStyle(style); return ( ); }; ================================================ FILE: packages/victory-native/src/components/victory-primitives/point.tsx ================================================ import React from "react"; import { Path } from "./path"; import { Point as PointBase, PointProps } from "victory-core/es"; export const Point = (props: PointProps) => ( } {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-primitives/rect.tsx ================================================ import React from "react"; import { Rect as RectBase, RectProps } from "react-native-svg"; import { useGetNativeStyle } from "../../helpers/native-helpers"; import { VictoryNativePrimitiveShapeProps } from "./types"; export type VictoryNativeRectProps = RectProps & VictoryNativePrimitiveShapeProps; export const Rect = (props: VictoryNativeRectProps) => { const { "aria-label": accessibilityLabel, desc, style, ...rest } = props; const nativeStyle = useGetNativeStyle(style); return ( ); }; ================================================ FILE: packages/victory-native/src/components/victory-primitives/slice.tsx ================================================ import React from "react"; import { Path } from "./path"; import { Slice as SliceBase, SliceProps } from "victory-pie/es"; export const Slice = (props: SliceProps) => ( } {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-primitives/text.tsx ================================================ import React from "react"; import { Text as TextBase, TextProps } from "react-native-svg"; import { useGetNativeStyle } from "../../helpers/native-helpers"; import { VictoryNativePrimitiveShapeProps } from "./types"; export interface VictoryNativeTextProps extends TextProps, VictoryNativePrimitiveShapeProps { direction?: "inherit" | "rtl" | "ltr"; } export const Text = (props: VictoryNativeTextProps) => { const { "aria-label": accessibilityLabel, children, desc, style, ...rest } = props; const nativeStyle = useGetNativeStyle(style); return ( {children} ); }; ================================================ FILE: packages/victory-native/src/components/victory-primitives/tspan.tsx ================================================ import React from "react"; import { TSpan as TSpanBase, TSpanProps } from "react-native-svg"; import { useGetNativeStyle } from "../../helpers/native-helpers"; import { VictoryNativePrimitiveShapeProps } from "./types"; export type VictoryNativeTSpanProps = TSpanProps & VictoryNativePrimitiveShapeProps; export const TSpan = (props: VictoryNativeTSpanProps) => { const { style, ...rest } = props; const nativeStyle = useGetNativeStyle(style); return ; }; ================================================ FILE: packages/victory-native/src/components/victory-primitives/types.ts ================================================ import { VictoryPrimitiveShapeProps } from "victory-core/es"; export interface VictoryNativePrimitiveShapeProps extends Pick< VictoryPrimitiveShapeProps, "className" | "events" | "role" | "shapeRendering" | "desc" | "style" > { "aria-label"?: string; } ================================================ FILE: packages/victory-native/src/components/victory-primitives/voronoi.tsx ================================================ import React from "react"; import { Path } from "./path"; import { ClipPath } from "./clip-path"; import { Circle } from "./circle"; import { G } from "react-native-svg"; import { Voronoi as VoronoiBase, VoronoiProps } from "victory-voronoi/es"; export const Voronoi = (props: VoronoiProps) => ( } groupComponent={} clipPathComponent={} circleComponent={} {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-primitives/whisker.tsx ================================================ import React from "react"; import { Line } from "./line"; import { G } from "react-native-svg"; import { Whisker as WhiskerBase, WhiskerProps } from "victory-core/es"; export const Whisker = (props: WhiskerProps) => ( } groupComponent={} {...props} /> ); ================================================ FILE: packages/victory-native/src/components/victory-scatter.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryScatter as VictoryScatterBase, VictoryScatterProps, } from "victory-scatter/es"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { Point } from "./victory-primitives/point"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryScatter = wrapCoreComponent({ Component: VictoryScatterBase, defaultProps: { ...VictoryScatterBase.defaultProps, dataComponent: , labelComponent: , containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-selection-container.tsx ================================================ /* eslint-disable react/no-multi-comp */ import React from "react"; import { Rect } from "react-native-svg"; import { VictoryEventHandler } from "victory-core"; import { SelectionHelpers, VictorySelectionContainerProps, VICTORY_SELECTION_CONTAINER_DEFAULT_PROPS, useVictorySelectionContainer, } from "victory-selection-container"; import { VictoryContainer } from "./victory-container"; import NativeHelpers from "../helpers/native-helpers"; export interface VictorySelectionContainerNativeProps extends VictorySelectionContainerProps { disableContainerEvents?: boolean; onTouchStart?: VictoryEventHandler; onTouchEnd?: VictoryEventHandler; } // ensure the selection component get native styles const DefaultSelectionComponent = ({ style = {}, ...otherProps }: { style?: Record; }) => ; export const VictorySelectionContainer = ( initialProps: VictorySelectionContainerNativeProps, ) => { const { props, children } = useVictorySelectionContainer({ ...initialProps, // @ts-expect-error TODO: standalone is not a valid prop for VictoryContainer, figure out why this is here standalone: initialProps.standalone ?? true, selectionComponent: initialProps.selectionComponent ?? ( ), }); return {children}; }; VictorySelectionContainer.role = "container"; VictorySelectionContainer.defaultEvents = ( initialProps: VictorySelectionContainerNativeProps, ) => { const props = { ...VICTORY_SELECTION_CONTAINER_DEFAULT_PROPS, ...initialProps, }; const createEventHandler = (handler: VictoryEventHandler, cancel: boolean): VictoryEventHandler => // eslint-disable-next-line max-params (event, targetProps, eventKey, context) => { if (props.disable) { return {}; } if (cancel) { SelectionHelpers.onMouseMove.cancel(); } return handler(event, { ...props, ...targetProps }, eventKey, context); }; return [ { target: "parent", eventHandlers: { onTouchStart: createEventHandler(SelectionHelpers.onMouseMove, true), onTouchMove: createEventHandler(SelectionHelpers.onMouseMove, false), onTouchEnd: createEventHandler(SelectionHelpers.onMouseUp, true), }, }, ]; }; ================================================ FILE: packages/victory-native/src/components/victory-stack.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryStack as VictoryStackBase, VictoryStackProps, } from "victory-stack/es"; import { VictoryContainer } from "./victory-container"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryStack = wrapCoreComponent({ Component: VictoryStackBase, defaultProps: { containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-tooltip.tsx ================================================ import React from "react"; import { G } from "react-native-svg"; import { VictoryTooltip as VictoryTooltipBase } from "victory-tooltip/es"; import { VictoryLabel } from "./victory-label"; import { VictoryPortal } from "./victory-portal/victory-portal"; import { Flyout } from "./victory-primitives/flyout"; export class VictoryTooltip extends VictoryTooltipBase { static defaultProps = { ...VictoryTooltipBase.defaultProps, labelComponent: , flyoutComponent: , groupComponent: , }; static defaultEvents() { return [ { target: "data", eventHandlers: { onPressIn: (targetProps) => { return [ { target: "labels", mutation: () => ({ active: true }), }, { target: "data", mutation: () => targetProps.activateData ? { active: true } : { active: undefined }, }, ]; }, onPressOut: () => { return [ { target: "labels", mutation: () => ({ active: undefined }), }, { target: "data", mutation: () => ({ active: undefined }), }, ]; }, }, }, ]; } renderTooltip(props) { const evaluatedProps = this.getEvaluatedProps(props); const { flyoutComponent, labelComponent, groupComponent, active, renderInPortal, } = evaluatedProps; if (!active) { return renderInPortal ? ( ) : ( ); } const calculatedValues = this.getCalculatedValues(evaluatedProps); const children = [ React.cloneElement( flyoutComponent!, this.getFlyoutProps(evaluatedProps, calculatedValues), ), React.cloneElement( labelComponent!, this.getLabelProps(evaluatedProps, calculatedValues), ), ]; const tooltip = React.cloneElement( groupComponent!, { role: "presentation" }, children, ); return renderInPortal ? {tooltip} : tooltip; } } ================================================ FILE: packages/victory-native/src/components/victory-voronoi-container.tsx ================================================ import React from "react"; import { VictoryEventHandler } from "victory-core"; import { VictoryVoronoiContainerProps, VoronoiHelpers, useVictoryVoronoiContainer, VICTORY_VORONOI_CONTAINER_DEFAULT_PROPS, } from "victory-voronoi-container"; import { VictoryContainer } from "./victory-container"; import { VictoryTooltip } from "./victory-tooltip"; export interface VictoryVoronoiContainerNativeProps extends VictoryVoronoiContainerProps { disableContainerEvents?: boolean; onTouchStart?: VictoryEventHandler; onTouchEnd?: VictoryEventHandler; } const DEFAULT_VORONOI_PADDING = 5; export const VictoryVoronoiContainer = ( initialProps: VictoryVoronoiContainerNativeProps, ) => { const { props, children } = useVictoryVoronoiContainer({ ...initialProps, activateData: initialProps.activateData ?? true, activateLabels: initialProps.activateLabels ?? true, labelComponent: initialProps.labelComponent ?? , voronoiPadding: initialProps.voronoiPadding ?? DEFAULT_VORONOI_PADDING, }); return {children}; }; VictoryVoronoiContainer.role = "container"; VictoryVoronoiContainer.defaultEvents = ( initialProps: VictoryVoronoiContainerNativeProps, ) => { const props = { ...VICTORY_VORONOI_CONTAINER_DEFAULT_PROPS, ...initialProps }; const createEventHandler = (handler: VictoryEventHandler, disabled?: boolean): VictoryEventHandler => // eslint-disable-next-line max-params (event, targetProps, eventKey, context) => disabled || props.disable ? {} : handler(event, { ...props, ...targetProps }, eventKey, context); return [ { target: "parent", eventHandlers: { onTouchStart: createEventHandler(VoronoiHelpers.onMouseMove), onTouchMove: createEventHandler(VoronoiHelpers.onMouseMove), onTouchEnd: createEventHandler(VoronoiHelpers.onMouseLeave), }, }, { target: "data", eventHandlers: props.disable ? {} : { onTouchStart: () => null, onTouchMove: () => null, onTouchEnd: () => null, }, }, ]; }; ================================================ FILE: packages/victory-native/src/components/victory-voronoi.tsx ================================================ import React from "react"; import { Dimensions } from "react-native"; import { G } from "react-native-svg"; import { VictoryVoronoi as VictoryVoronoiBase, VictoryVoronoiProps, } from "victory-voronoi/es"; import { VictoryLabel } from "./victory-label"; import { VictoryContainer } from "./victory-container"; import { Voronoi } from "./victory-primitives/voronoi"; import { wrapCoreComponent } from "../helpers/wrap-core-component"; export const VictoryVoronoi = wrapCoreComponent({ Component: VictoryVoronoiBase, defaultProps: { ...VictoryVoronoiBase.defaultProps, dataComponent: , labelComponent: , containerComponent: , groupComponent: , width: Dimensions.get("window").width, }, }); ================================================ FILE: packages/victory-native/src/components/victory-zoom-container.tsx ================================================ import React from "react"; import { VictoryContainer } from "./victory-container"; import { VictoryClipContainer } from "./victory-clip-container"; import { VictoryEventHandler } from "victory-core"; import { VictoryZoomContainerProps, useVictoryZoomContainer, VICTORY_ZOOM_CONTAINER_DEFAULT_PROPS, } from "victory-zoom-container"; import NativeZoomHelpers from "../helpers/native-zoom-helpers"; export interface VictoryZoomContainerNativeProps extends VictoryZoomContainerProps { disableContainerEvents?: boolean; onTouchStart?: VictoryEventHandler; onTouchEnd?: VictoryEventHandler; } export const VictoryZoomContainer = ( initialProps: VictoryZoomContainerNativeProps, ) => { const { props, children } = useVictoryZoomContainer({ ...initialProps, clipContainerComponent: initialProps.clipContainerComponent ?? ( ), }); return {children}; }; VictoryZoomContainer.role = "container"; VictoryZoomContainer.defaultEvents = ( initialProps: VictoryZoomContainerNativeProps, ) => { const props = { ...VICTORY_ZOOM_CONTAINER_DEFAULT_PROPS, ...initialProps }; const createEventHandler = (handler: VictoryEventHandler, disabled?: boolean): VictoryEventHandler => // eslint-disable-next-line max-params (event, targetProps, eventKey, context) => disabled || props.disable ? {} : handler(event, { ...props, ...targetProps }, eventKey, context); return [ { target: "parent", eventHandlers: { onTouchStart: createEventHandler(NativeZoomHelpers.onTouchStart), onTouchMove: createEventHandler(NativeZoomHelpers.onTouchMove), onTouchEnd: createEventHandler(NativeZoomHelpers.onTouchEnd), onTouchPinch: createEventHandler(NativeZoomHelpers.onTouchPinch), }, }, ]; }; ================================================ FILE: packages/victory-native/src/helpers/create-container.ts ================================================ import { makeCreateContainerFunction } from "victory-create-container"; import { VictoryContainer } from "../components/victory-container"; import { VictoryZoomContainer } from "../components/victory-zoom-container"; import { VictoryVoronoiContainer } from "../components/victory-voronoi-container"; import { VictorySelectionContainer } from "../components/victory-selection-container"; import { VictoryBrushContainer } from "../components/victory-brush-container"; import { VictoryCursorContainer } from "../components/victory-cursor-container"; export const createContainer = makeCreateContainerFunction( { zoom: VictoryZoomContainer, voronoi: VictoryVoronoiContainer, selection: VictorySelectionContainer, brush: VictoryBrushContainer, cursor: VictoryCursorContainer, }, VictoryContainer, ); export default createContainer; ================================================ FILE: packages/victory-native/src/helpers/native-helpers.test.ts ================================================ import NativeHelpers from "./native-helpers"; describe("getStyle", () => { it("should return undefined if not called with any arguments", () => { expect(NativeHelpers.getStyle()).toEqual(undefined); }); it("removes all unsupported props and leaves others, including stroke props", () => { expect( NativeHelpers.getStyle({ fill: "black", stroke: "grey", pointerEvents: "auto", x: 0, y: 0, _x: 0, _y: 0, userSelect: "none", strokeWidth: 1, strokeOpacity: 1, strokeDasharray: 1, strokeDashoffset: 1, strokeLinecap: 1, strokeLinejoin: 1, }), ).toEqual({ fill: "black", stroke: "grey", strokeWidth: 1, strokeOpacity: 1, strokeDasharray: 1, strokeDashoffset: 1, strokeLinecap: 1, strokeLinejoin: 1, }); }); it("removes all unsupported and stroke props when stroke is transparent", () => { expect( NativeHelpers.getStyle({ stroke: "transparent", fill: "black", pointerEvents: "auto", x: 0, y: 0, _x: 0, _y: 0, userSelect: "none", strokeWidth: 1, strokeOpacity: 1, strokeDasharray: 1, strokeDashoffset: 1, strokeLinecap: 1, strokeLinejoin: 1, }), ).toEqual({ fill: "black" }); }); it("removes all unsupported and stroke props when stroke is 'none'", () => { expect( NativeHelpers.getStyle({ stroke: "none", fill: "black", pointerEvents: "auto", x: 0, y: 0, _x: 0, _y: 0, userSelect: "none", strokeWidth: 1, strokeOpacity: 1, strokeDasharray: 1, strokeDashoffset: 1, strokeLinecap: 1, strokeLinejoin: 1, }), ).toEqual({ fill: "black" }); }); it("removes extra properties if given", () => { expect( NativeHelpers.getStyle({ width: 100, height: 100, depth: 100 }, [ "width", "depth", ]), ).toEqual({ height: 100 }); }); }); ================================================ FILE: packages/victory-native/src/helpers/native-helpers.ts ================================================ import * as React from "react"; /** * creates an object with some keys excluded * replacement for lodash.omit for performance. does not mimick the entire lodash.omit api * @param {Object} originalObject: created object will be based on this object * @param {Array} keys: an array of keys to omit from the new object * @returns {Object} new object with same properties as originalObject */ export const omit = (originalObject, keys: string[] = []) => { // code based on babel's _objectWithoutProperties const newObject = {}; for (const key in originalObject) { if (keys.indexOf(key) >= 0) { continue; } if (!Object.prototype.hasOwnProperty.call(originalObject, key)) { continue; } newObject[key] = originalObject[key]; } return newObject; }; const unsupportedProps = ["pointerEvents", "x", "y", "_x", "_y", "userSelect"]; const unsupportedAndStrokeProps = [ "stroke", "strokeWidth", "strokeOpacity", "strokeDasharray", "strokeDashoffset", "strokeLinecap", "strokeLinejoin", ...unsupportedProps, ]; const getStyle = ( style?: Record, extraOmitProperties?: string[], ) => { if (!style) { return undefined; } // TODO: more style fixes for Native? const omitProperties = style.stroke === "none" || style.stroke === "transparent" ? unsupportedAndStrokeProps : unsupportedProps; return extraOmitProperties ? omit(style, [...omitProperties, ...extraOmitProperties]) : omit(style, omitProperties); }; export default { getStyle, }; export const useGetNativeStyle = ( style: Record, extraOmitProperties?: string[], ) => { return React.useMemo( () => getStyle(style, extraOmitProperties), [style, extraOmitProperties], ); }; ================================================ FILE: packages/victory-native/src/helpers/native-zoom-helpers.ts ================================================ import defaults from "lodash/defaults"; import throttle from "lodash/throttle"; import { Dimensions } from "react-native"; import isEqual from "react-fast-compare"; import { Collection, Helpers as CoreHelpers } from "victory-core"; import { RawZoomHelpers } from "victory-zoom-container"; const hypotenuse = (x, y) => Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); const screenSize = hypotenuse( Dimensions.get("window").width, Dimensions.get("window").height, ); const Helpers = { ...RawZoomHelpers, onTouchEnd() { return [ { target: "parent", mutation: () => { return { panning: false, originalPinchDistance: null }; }, }, ]; }, // eslint-disable-next-line max-params onTouchPinch(evt, targetProps, eventKey, ctx) { const { onZoomDomainChange, zoomDimension, domain, zoomDomain } = targetProps; const { touches } = evt.nativeEvent; if (!targetProps.allowZoom) { return {}; } const originalDomain = this.getDomain(targetProps); const lastDomain = defaults( {}, targetProps.currentDomain || zoomDomain || originalDomain, domain, ); const { x, y } = lastDomain; const currentDomain = { x: zoomDimension === "y" ? lastDomain.x : this.scaleNative(x, evt, targetProps, "x"), y: zoomDimension === "x" ? lastDomain.y : this.scaleNative(y, evt, targetProps, "y"), }; const resumeAnimation = this.handleAnimation(ctx); const pinchDistance = this.getPinchDistance(touches); const originalPinchDistance = targetProps.originalPinchDistance || pinchDistance; const zoomActive = pinchDistance !== originalPinchDistance || // if zoomActive is already set AND user hasn't zoommed out all the way (targetProps.zoomActive && !isEqual(originalDomain, lastDomain)); if (CoreHelpers.isFunction(onZoomDomainChange)) { onZoomDomainChange(currentDomain); } return [ { target: "parent", callback: resumeAnimation, mutation: () => { return { domain: currentDomain, currentDomain, originalDomain, cachedZoomDomain: zoomDomain, parentControlledProps: ["domain"], panning: false, originalPinchDistance, zoomActive, }; }, }, ]; }, getPinchDistance([a, b]) { return hypotenuse(b.locationX - a.locationX, b.locationY - a.locationY); }, getScaleFactorNative(evt, props) { const { touches } = evt.nativeEvent; const originalPinchDistance = props.originalPinchDistance || 0; const currentPinchDistance = this.getPinchDistance(touches); const scaledPinchChange = (currentPinchDistance - originalPinchDistance) / screenSize; return 1 - scaledPinchChange; }, // eslint-disable-next-line max-params scaleNative(currentDomain, evt, props, axis) { const [from, to] = currentDomain; const range = Math.abs(to - from); const minimumZoom = props.minimumZoom && props.minimumZoom[axis]; const factor = this.getScaleFactorNative(evt, props); if (minimumZoom && range <= minimumZoom && factor < 1) { return currentDomain; } const [fromBound, toBound] = this.getDomain(props)[axis]; const percent = this.getScalePercent(evt, props, axis); const point = factor * from + percent * (factor * range); const minDomain = this.getMinimumDomain(point, props, axis); const [newMin, newMax] = this.getScaledDomain( currentDomain, factor, percent, ); const newDomain = [ newMin > fromBound && newMin < toBound ? newMin : fromBound, newMax < toBound && newMax > fromBound ? newMax : toBound, ]; const domain = Math.abs(minDomain[1] - minDomain[0]) > Math.abs(newDomain[1] - newDomain[0]) ? minDomain : newDomain; return Collection.containsDates([fromBound, toBound]) ? [new Date(domain[0]), new Date(domain[1])] : domain; }, }; const makeThrottledHandler = (handler) => { // eslint-disable-next-line no-magic-numbers const throttledHandler = throttle(handler, 16, { leading: true }); return (evt, ...otherParams) => { evt.persist(); // ensure that the react native event is persisted! return throttledHandler(evt, ...otherParams); }; }; export { Helpers, makeThrottledHandler }; export default { onTouchStart: Helpers.onMouseDown.bind(Helpers), onTouchEnd: Helpers.onTouchEnd.bind(Helpers), onTouchMove: makeThrottledHandler(Helpers.onMouseMove.bind(Helpers)), onTouchPinch: makeThrottledHandler(Helpers.onTouchPinch.bind(Helpers)), }; ================================================ FILE: packages/victory-native/src/helpers/wrap-core-component.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import hoistNonReactStatics from "hoist-non-react-statics"; /** * Wrap a core component, pass props through. */ export function wrapCoreComponent({ Component, defaultProps, }: { Component: React.ComponentType; defaultProps: TProps; }) { const WrappedComponent = (props: TProps) => { const propsWithDefaults = defaults({}, props, defaultProps); return ; }; hoistNonReactStatics(WrappedComponent, Component); return WrappedComponent; } ================================================ FILE: packages/victory-native/src/index.ts ================================================ export { VictoryAnimation, VictoryTheme, VictoryTransition, addEvents, Collection, Data, DefaultTransitions, Domain, Events, Helpers, Log, Scale, Style, TextSize, Transitions, Selection, LabelHelpers, Axis, Wrapper, } from "victory-core"; export { VictorySharedEvents } from "victory-shared-events"; export * from "./components/victory-primitives/circle"; export * from "./components/victory-primitives/line"; export * from "./components/victory-primitives/path"; export * from "./components/victory-primitives/rect"; export * from "./components/victory-primitives/text"; export * from "./components/victory-primitives/tspan"; export * from "./components/victory-primitives/arc"; export * from "./components/victory-primitives/area"; export * from "./components/victory-primitives/background"; export * from "./components/victory-primitives/bar"; export * from "./components/victory-primitives/border"; export * from "./components/victory-primitives/candle"; export * from "./components/victory-primitives/clip-path"; export * from "./components/victory-primitives/curve"; export * from "./components/victory-primitives/error-bar"; export * from "./components/victory-primitives/line-segment"; export * from "./components/victory-primitives/point"; export * from "./components/victory-primitives/slice"; export * from "./components/victory-primitives/voronoi"; export * from "./components/victory-primitives/flyout"; export * from "./components/victory-primitives/whisker"; export * from "./components/victory-tooltip"; export * from "./components/victory-portal/victory-portal"; export * from "./components/victory-portal/portal"; export * from "./components/victory-area"; export * from "./components/victory-axis"; export * from "./components/victory-polar-axis"; export * from "./components/victory-bar"; export * from "./components/victory-brush-line"; export * from "./components/victory-boxplot"; export * from "./components/victory-group"; export * from "./components/victory-line"; export * from "./components/victory-histogram"; export * from "./components/victory-scatter"; export * from "./components/victory-stack"; export * from "./components/victory-chart"; export * from "./components/victory-errorbar"; export * from "./components/victory-candlestick"; export * from "./components/victory-voronoi"; export * from "./components/victory-pie"; export * from "./components/victory-container"; export * from "./components/victory-clip-container"; export * from "./components/victory-label"; export * from "./components/victory-legend"; export * from "./components/victory-zoom-container"; export * from "./components/victory-voronoi-container"; export * from "./components/victory-selection-container"; export * from "./components/victory-cursor-container"; export * from "./components/victory-brush-container"; export { default as NativeHelpers } from "./helpers/native-helpers"; export { default as NativeZoomHelpers } from "./helpers/native-zoom-helpers"; export { createContainer } from "./helpers/create-container"; ================================================ FILE: packages/victory-native/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts", "jest-native-setup.tsx"] } ================================================ FILE: packages/victory-native/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts", "jest-native-setup.tsx"] } ================================================ FILE: packages/victory-pie/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-pie/CHANGELOG.md ================================================ # victory-pie ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) * Improve types in victory-core helpers ([#2999](https://github.com/FormidableLabs/victory/pull/2999)) ## 37.3.2 ## 37.3.1 ### Patch Changes - Added logic to sort pie chart slices by categories prop ([#2934](https://github.com/FormidableLabs/victory/pull/2934)) * Remove duplicate types from interfaces ([#2940](https://github.com/FormidableLabs/victory/pull/2940)) ## 37.3.0 ## 37.2.0 ### Minor Changes - Minor updates for clean theme ([#2909](https://github.com/FormidableLabs/victory/pull/2909)) ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash isNil and isNan with native code ([#2800](https://github.com/FormidableLabs/victory/pull/2800)) * Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) * Replace instances of lodash.range with equivalent native code ([#2760](https://github.com/FormidableLabs/victory/pull/2760)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Migrate victory-pie to TypeScript ([#2740](https://github.com/FormidableLabs/victory/pull/2740)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 - victory-vendor@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 - victory-vendor@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 - victory-vendor@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416), [`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 - victory-vendor@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 - victory-vendor@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 - victory-vendor@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 - victory-vendor@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 - victory-vendor@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 - victory-vendor@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-pie/README.md ================================================ # VictoryPie `victory-pie@^30.0.0` exports `VictoryPie` and `Slice` components To view documentation for `VictoryPie` please see https://commerce.nearform.com/open-source/victory/docs/victory-pie To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-pie.md ================================================ FILE: packages/victory-pie/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-pie/package.json ================================================ { "name": "victory-pie", "version": "37.3.6", "description": "Pie Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6", "victory-vendor": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs", "../victory-vendor:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-vendor:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-vendor:build", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-pie/src/helper-methods.ts ================================================ /* eslint no-magic-numbers: ["error", { "ignore": [-1, 0, 1, 2, 45, 90, 135, 180, 225, 270, 315, 360] }]*/ import defaults from "lodash/defaults"; import isPlainObject from "lodash/isPlainObject"; import * as d3Shape from "victory-vendor/d3-shape"; import { Helpers, Data, Style } from "victory-core"; import { VictoryPieProps } from "./victory-pie"; const checkForValidText = (text) => { if (text === undefined || text === null || Helpers.isFunction(text)) { return text; } return `${text}`; }; const getColor = (style, colors, index) => { if (style && style.data && style.data.fill) { return style.data.fill; } return colors && colors[index % colors.length]; }; const getRadius = (props, padding) => { if (typeof props.radius === "number") { return props.radius; } return ( Math.min( props.width - padding.left - padding.right, props.height - padding.top - padding.bottom, ) / 2 ); }; const getOrigin = (props, padding) => { const { width, height } = props; const origin = isPlainObject(props.origin) ? props.origin : {}; return { x: origin.x !== undefined ? origin.x : (padding.left - padding.right + width) / 2, y: origin.y !== undefined ? origin.y : (padding.top - padding.bottom + height) / 2, }; }; const getSlices = (props, data) => { const padAngle = Helpers.isFunction(props.padAngle) ? 0 : props.padAngle; const layoutFunction = d3Shape .pie() .sort(null) .startAngle(Helpers.degreesToRadians(props.startAngle)) .endAngle(Helpers.degreesToRadians(props.endAngle)) .padAngle(Helpers.degreesToRadians(padAngle)) .value((datum: any) => { return datum._y; }); return layoutFunction(data); }; const getCategoriesFromProps = (props: VictoryPieProps) => Array.isArray(props.categories) ? props.categories : ((props?.categories as { x: string[] })?.x ?? []); /** * Sorts data by props.categories or props.categories.x. If all of the data keys aren't * included in categories, any remaining data will be appended to the data array. * If extraneous categories are included in the categories prop, the will be ignored and * have no effect on the rendered component. */ const getDataSortedByCategories = (props: VictoryPieProps, data) => { const sorted: string[] = []; getCategoriesFromProps(props).forEach((category) => { const idx = data.findIndex(({ x }) => x === category); if (idx >= 0) { const datum = data.splice(idx, 1)[0]; sorted.push(datum); } }); return [...sorted, ...data]; }; const getCalculatedValues = (props) => { const { colorScale, theme } = props; const styleObject = Helpers.getDefaultStyles(props, "pie"); const style = Helpers.getStyles(props.style, styleObject); const colors = Array.isArray(colorScale) ? colorScale : Style.getColorScale(colorScale, theme); const padding = Helpers.getPadding(props.padding); const defaultRadius = getRadius(props, padding); const origin = getOrigin(props, padding); const data = getDataSortedByCategories(props, Data.getData(props)); const slices = getSlices(props, data); return Object.assign({}, props, { style, colors, padding, defaultRadius, data, slices, origin, }); }; const getSliceStyle = (index, calculatedValues) => { const { style, colors } = calculatedValues; const fill = getColor(style, colors, index); return Object.assign({ fill }, style.data); }; const getLabelText = (props, datum, index) => { let text; if (datum.label) { text = datum.label; } else if (Array.isArray(props.labels)) { text = props.labels[index]; } else { text = Helpers.isFunction(props.labels) ? props.labels : datum.xName || datum._x; } return checkForValidText(text); }; const getLabelArc = (labelRadius) => { return d3Shape.arc().outerRadius(labelRadius).innerRadius(labelRadius); }; const getCalculatedLabelRadius = (radius, labelRadius, style) => { const padding = (style && style.padding) || 0; return labelRadius || radius + padding; }; const getLabelPosition = (arc, slice, position) => { const construct = { startAngle: position === "startAngle" ? slice.startAngle : slice.endAngle, endAngle: position === "endAngle" ? slice.endAngle : slice.startAngle, }; const clonedArc = Object.assign({}, slice, construct); return arc.centroid(clonedArc); }; const getLabelOrientation = (degree, labelPlacement) => { if (labelPlacement === "perpendicular") { return degree > 90 && degree < 270 ? "bottom" : "top"; } else if (labelPlacement === "parallel") { return degree >= 0 && degree <= 180 ? "right" : "left"; } if (degree < 45 || degree > 315) { return "top"; } else if (degree >= 45 && degree < 135) { return "right"; } else if (degree >= 135 && degree < 225) { return "bottom"; } return "left"; }; const getTextAnchor = (orientation) => { if (orientation === "top" || orientation === "bottom") { return "middle"; } return orientation === "right" ? "start" : "end"; }; const getVerticalAnchor = (orientation) => { if (orientation === "left" || orientation === "right") { return "middle"; } return orientation === "bottom" ? "start" : "end"; }; const getBaseLabelAngle = (slice, labelPosition, labelStyle) => { let baseAngle = 0; if (labelPosition.angle !== undefined) { baseAngle = labelStyle.angle; } else if (labelPosition === "centroid") { baseAngle = Helpers.radiansToDegrees( (slice.startAngle + slice.endAngle) / 2, ); } else { baseAngle = labelPosition === "startAngle" ? Helpers.radiansToDegrees(slice.startAngle) : Helpers.radiansToDegrees(slice.endAngle); } const positiveAngle = baseAngle < 0 ? 360 - baseAngle : baseAngle; return positiveAngle % 360; }; const getLabelAngle = (baseAngle, labelPlacement) => { if (labelPlacement === "vertical") { return 0; } if (labelPlacement === "parallel") { return baseAngle > 180 && baseAngle < 360 ? baseAngle + 90 : baseAngle - 90; } return baseAngle > 90 && baseAngle < 270 ? baseAngle - 180 : baseAngle; }; const getLabelProps = (text, dataProps, calculatedValues) => { const { index, datum, data, slice, labelComponent, theme } = dataProps; const { style, defaultRadius, origin, width, height } = calculatedValues; const labelRadius = Helpers.evaluateProp( calculatedValues.labelRadius, Object.assign({ text }, dataProps), ); const labelPosition = Helpers.evaluateProp( calculatedValues.labelPosition, Object.assign({ text }, dataProps), ) || "centroid"; const labelPlacement = Helpers.evaluateProp( calculatedValues.labelPlacement, Object.assign({ text }, dataProps), ) || "vertical"; const labelStyle = Object.assign({ padding: 0 }, style.labels); const evaluatedStyle = Helpers.evaluateStyle( labelStyle, Object.assign({ labelRadius, text }, dataProps), ); const calculatedLabelRadius = getCalculatedLabelRadius( defaultRadius, labelRadius, evaluatedStyle, ); const labelArc = getLabelArc(calculatedLabelRadius); const position = getLabelPosition(labelArc, slice, labelPosition); const baseAngle = getBaseLabelAngle(slice, labelPosition, labelStyle); const labelAngle = getLabelAngle(baseAngle, labelPlacement); const orientation = getLabelOrientation(baseAngle, labelPlacement); const textAnchor = labelStyle.textAnchor || getTextAnchor(orientation); const verticalAnchor = labelStyle.verticalAnchor || getVerticalAnchor(orientation); const labelProps = { width, height, index, datum, data, slice, orientation, text, style: labelStyle, x: Math.round(position[0]) + origin.x, y: Math.round(position[1]) + origin.y, textAnchor, verticalAnchor, angle: labelAngle, calculatedLabelRadius, }; if (!Helpers.isTooltip(labelComponent)) { return labelProps; } const tooltipTheme = (theme && theme.tooltip) || {}; return defaults({}, labelProps, Helpers.omit(tooltipTheme, ["style"])); }; export const getXOffsetMultiplayerByAngle = (angle) => Math.cos(angle - Helpers.degreesToRadians(90)); export const getYOffsetMultiplayerByAngle = (angle) => Math.sin(angle - Helpers.degreesToRadians(90)); export const getXOffset = (offset, angle) => offset * getXOffsetMultiplayerByAngle(angle); export const getYOffset = (offset, angle) => offset * getYOffsetMultiplayerByAngle(angle); export const getAverage = (array) => array.reduce((acc, cur) => acc + cur, 0) / array.length; export const getLabelIndicatorPropsForLineSegment = ( props, calculatedValues, labelProps, ) => { const { innerRadius, radius, slice: { startAngle, endAngle }, labelIndicatorInnerOffset, labelIndicatorOuterOffset, index, } = props; const { height, width } = calculatedValues; const { calculatedLabelRadius } = labelProps; // calculation const middleRadius = getAverage([innerRadius, radius]); const midAngle = getAverage([endAngle, startAngle]); const centerX = width / 2; const centerY = height / 2; const innerOffset = middleRadius + labelIndicatorInnerOffset; const outerOffset = calculatedLabelRadius - labelIndicatorOuterOffset; const x1 = centerX + getXOffset(innerOffset, midAngle); const y1 = centerY + getYOffset(innerOffset, midAngle); const x2 = centerX + getXOffset(outerOffset, midAngle); const y2 = centerY + getYOffset(outerOffset, midAngle); const labelIndicatorProps = { x1, y1, x2, y2, index, }; return defaults({}, labelIndicatorProps); }; export const getBaseProps = (initialProps, fallbackProps) => { const props = Helpers.modifyProps(initialProps, fallbackProps, "pie"); const calculatedValues = getCalculatedValues(props); const { slices, style, data, origin, defaultRadius, labels, events, sharedEvents, height, width, standalone, name, innerRadius, cornerRadius, padAngle, disableInlineStyles, labelIndicator, } = calculatedValues; const radius = props.radius || defaultRadius; const initialChildProps = { parent: { standalone, height, width, slices, name, style: style.parent }, }; return slices.reduce((childProps, slice, index) => { const datum = defaults({}, data[index], { startAngle: Helpers.radiansToDegrees(slice.startAngle), endAngle: Helpers.radiansToDegrees(slice.endAngle), padAngle: Helpers.radiansToDegrees(slice.padAngle), }); const eventKey = !Helpers.isNil(datum.eventKey) ? datum.eventKey : index; const dataProps = { index, slice, datum, data, origin, innerRadius, radius, cornerRadius, padAngle, style: disableInlineStyles ? {} : getSliceStyle(index, calculatedValues), disableInlineStyles, }; childProps[eventKey] = { data: dataProps, }; const text = getLabelText(props, datum, index); if ( (text !== undefined && text !== null) || (labels && (events || sharedEvents)) ) { const evaluatedText = Helpers.evaluateProp(text, dataProps); childProps[eventKey].labels = getLabelProps( evaluatedText, Object.assign({}, props, dataProps), calculatedValues, ); if (labelIndicator) { const labelProps = childProps[eventKey].labels; if (labelProps.calculatedLabelRadius > radius) { childProps[eventKey].labelIndicators = getLabelIndicatorPropsForLineSegment( Object.assign({}, props, dataProps), calculatedValues, labelProps, ); } } } return childProps; }, initialChildProps); }; ================================================ FILE: packages/victory-pie/src/index.ts ================================================ export * from "./victory-pie"; export * from "./slice"; ================================================ FILE: packages/victory-pie/src/slice.test.tsx ================================================ import { render } from "@testing-library/react"; import React from "react"; import { SVGWrapper } from "../../../test/helpers"; import { Slice } from "."; describe("victory-primitives/slice", () => { describe("rendering", () => { it("renders a path with attribute `d` equal to the result of `props.pathFunction` called with `props.slice`", () => { const EXPECTED_D_ATTR = "M1,1"; const slice = { x: 1, y: 1 }; const pathFunction = (sli) => { // The path function is called with `props.slice` expect(sli).toEqual(slice); return EXPECTED_D_ATTR; }; const { container } = render( // @ts-expect-error there is a prop mismatch here between the slice definition and the prop , { wrapper: SVGWrapper }, ); expect(container.querySelector("path")).toMatchInlineSnapshot(` `); }); }); }); ================================================ FILE: packages/victory-pie/src/slice.tsx ================================================ import React from "react"; import { Helpers, Path, NumberOrCallback, SliceNumberOrCallback, StringOrCallback, VictoryCommonProps, VictoryStyleInterface, } from "victory-core"; import defaults from "lodash/defaults"; import * as d3Shape from "victory-vendor/d3-shape"; export type VictorySliceLabelPositionType = | "startAngle" | "centroid" | "endAngle"; export type VictorySliceLabelPlacementType = | "vertical" | "parallel" | "perpendicular"; export type VictorySliceTTargetType = "data" | "labels" | "parent"; export interface SliceProps extends VictoryCommonProps { ariaLabel?: StringOrCallback; cornerRadius?: SliceNumberOrCallback; datum?: any; innerRadius?: NumberOrCallback; padAngle?: SliceNumberOrCallback; pathComponent?: React.ReactElement; pathFunction?: (props: SliceProps) => string; radius?: SliceNumberOrCallback; slice?: { startAngle?: number; endAngle?: number; padAngle?: number; data?: any[]; }; sliceEndAngle?: SliceNumberOrCallback; sliceStartAngle?: SliceNumberOrCallback; style?: VictoryStyleInterface; tabIndex?: NumberOrCallback; role?: string; shapeRendering?: string; } const getPath = (props) => { const { slice, radius, innerRadius, cornerRadius } = props; if (Helpers.isFunction(props.pathFunction)) { return props.pathFunction(slice); } const padAngle = Helpers.degreesToRadians(props.padAngle); const startAngle = Helpers.degreesToRadians(props.sliceStartAngle); const endAngle = Helpers.degreesToRadians(props.sliceEndAngle); const pathFunction = d3Shape .arc() .cornerRadius(cornerRadius) .outerRadius(radius) .innerRadius(innerRadius || 0); return pathFunction(defaults({ startAngle, endAngle, padAngle }, slice)); }; const evaluateProps = (props) => { /** * * Potential evaluated props of following must be evaluated in this order: * 1) `style` * 2) `radius` * 3) `innerRadius` * * Everything else does not have to be evaluated in a particular order: * `ariaLabel` * `id` * `cornerRadius` * `padAngle` * `sliceStartAngle` * `sliceEndAngle` * `tabIndex` */ const style = Helpers.evaluateStyle(props.style, props); const radius = Helpers.evaluateProp( props.radius, Object.assign({}, props, { style }), ); const innerRadius = Helpers.evaluateProp( props.innerRadius, Object.assign({}, props, { style, radius }), ); const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const id = Helpers.evaluateProp(props.id, props); const cornerRadius = Helpers.evaluateProp(props.cornerRadius, props); const padAngle = Helpers.evaluateProp(props.padAngle, props); const sliceStartAngle = Helpers.evaluateProp(props.sliceStartAngle, props); const sliceEndAngle = Helpers.evaluateProp(props.sliceEndAngle, props); const tabIndex = Helpers.evaluateProp(props.tabIndex, props); return Object.assign({}, props, { ariaLabel, style, radius, innerRadius, id, cornerRadius, padAngle, sliceStartAngle, sliceEndAngle, tabIndex, }); }; const defaultProps: SliceProps = { pathComponent: , role: "presentation", shapeRendering: "auto", }; export const Slice = (initialProps: SliceProps) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); const defaultTransform = props.origin ? `translate(${props.origin.x}, ${props.origin.y})` : undefined; return React.cloneElement(props.pathComponent, { ...props.events, "aria-label": props.ariaLabel, d: getPath(props), style: props.style, transform: props.transform || defaultTransform, className: props.className, role: props.role, shapeRendering: props.shapeRendering, clipPath: props.clipPath, tabIndex: props.tabIndex, }); }; ================================================ FILE: packages/victory-pie/src/victory-pie.test.tsx ================================================ import React from "react"; import { render, screen, fireEvent } from "@testing-library/react"; import { Helpers, Style } from "victory-core"; import { isCircularSector, getSvgCoordinatesAngleFromCartesianYAxis, getSliceArcStart, parseSvgPathCommands, getDistanceFromOrigin, getSliceArcEnd, } from "../../../test/helpers"; import { VictoryPie } from "./victory-pie"; import { Slice } from "./slice"; const pizzaSliceInnerText = "Pizza Slice"; const PizzaSlice = ({ datum }: { datum?: { x: number } }) => (

{pizzaSliceInnerText}

); describe("components/victory-pie", () => { const labeledData = [ { x: "Cats", y: 35 }, { x: "Dogs", y: 40 }, { x: "Birds", y: 55 }, ]; describe("default component rendering", () => { it("accepts user props", () => { render(); const svgNode = screen.getByTestId("victory-pie"); expect(svgNode).toHaveAttribute("aria-label", "Chart"); }); it("renders an svg with the correct width and height", () => { const { container } = render(); const svg = container.querySelector("svg"); expect(svg?.getAttribute("style")).toContain("width: 100%; height: 100%"); }); it("renders an svg with the correct viewBox", () => { const { container } = render(); const svg = container.querySelector("svg"); expect(svg?.getAttribute("viewBox")).toEqual("0 0 400 400"); }); it("renders 5 slices", () => { const { container } = render(); const slices = container.querySelectorAll("path"); expect(slices).toHaveLength(5); }); it("renders each slice as a circular sector", () => { const { container } = render(); const slices = container.querySelectorAll("path"); slices.forEach((slice) => { const sliceCommandString = slice.getAttribute("d"); expect(isCircularSector(sliceCommandString)).toBeTruthy(); }); }); it("renders 5 slice labels", () => { const { container } = render(); const labels = container.querySelectorAll("text"); expect(labels).toHaveLength(5); }); it("renders 0 slice labels for empty label array", () => { const { container } = render(); const labels = container.querySelectorAll("text"); expect(labels).toHaveLength(0); }); it("renders 0 slice labels for label function returning undefined", () => { // @ts-expect-error "undefined" is not assignable to "string" const { container } = render( undefined} />); const labels = container.querySelectorAll("text"); expect(labels).toHaveLength(0); }); }); describe("rendering data", () => { it("renders dataComponents for {x, y} shaped data (default)", () => { const data = Helpers.range(5).map((i) => ({ x: i, y: i })); render(} />); const pizzaSlices = screen.getAllByText(pizzaSliceInnerText); expect(pizzaSlices).toHaveLength(5); }); it("renders points for {x, y} shaped data (default)", () => { const data = Helpers.range(5).map((i) => ({ x: i, y: i })); const { container } = render(); const slices = container.querySelectorAll("path"); expect(slices).toHaveLength(5); }); it("renders points for array-shaped data", () => { const data = Helpers.range(6).map((i) => [i, i]); const { container } = render(); const slices = container.querySelectorAll("path"); expect(slices).toHaveLength(6); }); it("renders points for deeply-nested data", () => { const data = Helpers.range(7).map((i) => ({ a: { b: [{ x: i, y: i }] }, })); const { container } = render( , ); const slices = container.querySelectorAll("path"); expect(slices).toHaveLength(7); }); it("renders data values with null accessor", () => { const data = Helpers.range(8); const { container } = render( // @ts-expect-error "'null' is not assignable to 'x'" , ); const slices = container.querySelectorAll("path"); expect(slices).toHaveLength(8); }); it("renders data values in their given order", () => { const data = Helpers.range(9).map((i) => ({ x: i, y: i })); render(} />); const xValues = Array.from(screen.getAllByText(pizzaSliceInnerText)).map( (slice) => { return parseInt(slice.getAttribute("data-xvalue") || ""); }, ); const xValuesFromGivenData = data.map(({ x }) => x); expect(xValues).toEqual(xValuesFromGivenData); }); it("renders data values sorted by categories prop", () => { const { container } = render( , ); const xValues = Array.from( container.querySelectorAll("text[id^=pie-labels] > tspan"), ).map((slice) => slice.textContent); expect(xValues).toEqual(["E", "A", "D", "C", "B"]); }); it("renders data values sorted by categories prop, appending any data keys missing from categories and ignoring any categories values that are not valid data keys", () => { const { container } = render( , ); const xValues = Array.from( container.querySelectorAll("text[id^=pie-labels] > tspan"), ).map((slice) => slice.textContent); expect(xValues).toEqual(["E", "C", "B", "A", "D"]); }); it("renders data values sorted by sortKey prop", () => { const data = Helpers.range(9) .map((i) => ({ x: i, y: i })) .reverse(); render( } />, ); const xValues = Array.from(screen.getAllByText(pizzaSliceInnerText)).map( (slice) => { return parseInt(slice.getAttribute("data-xvalue") || ""); }, ); const xValuesFromDataAscending = data .map(({ x }) => x) .sort((a, b) => a - b); expect(xValues).toEqual(xValuesFromDataAscending); }); it("renders data values sorted by sortKey prop and sortOrder", () => { const data = Helpers.range(9).map((i) => ({ x: i, y: i })); render( } />, ); const xValues = Array.from(screen.getAllByText(pizzaSliceInnerText)).map( (slice) => { return parseInt(slice.getAttribute("data-xvalue") || ""); }, ); const xValuesFromDataDescending = data .map(({ x }) => x) .sort((a, b) => b - a); expect(xValues).toEqual(xValuesFromDataDescending); }); it("does not render data with null x or y values", () => { const data = [ { x: 1, y: 2 }, { x: null, y: 4 }, { x: 5, y: null }, ]; const { container } = render(); const slices = container.querySelectorAll("path"); expect(slices).toHaveLength(1); }); }); describe("the `startAngle` prop", () => { it("determines the counter clockwise angle relative to a cartesian Y axis of a vector extending from the origin to the _first drawn coordinate_ of the first slice ", () => { [0, 90, 180, 270].map((angle) => { const { container } = render(); const [firstSlice] = container.querySelectorAll("path"); const sliceCommandString = firstSlice.getAttribute("d"); const coordinates = getSliceArcStart(sliceCommandString); const renderedAngle = getSvgCoordinatesAngleFromCartesianYAxis(coordinates); // There is a small degree of inprecision due to how D3 renders the paths expect(renderedAngle).toBeCloseTo(angle); }); }); }); describe("the `innerRadius` prop", () => { it("renders the slices as annular sections", () => { const { container } = render(); const slices = container.querySelectorAll("path"); slices.forEach((slice) => { const commands = parseSvgPathCommands(slice.getAttribute("d")); const startOfInnerArc = { x: commands[2].args[0], y: commands[2].args[1], }; const endOfInnerArc = { x: commands[3].args[5], y: commands[3].args[6], }; expect(getDistanceFromOrigin(startOfInnerArc)).toBeCloseTo( getDistanceFromOrigin(endOfInnerArc), ); }); }); it("determines the distance in pixels between the origin & the inner edge of the sections", () => { const INNER_RADIUS = 70; const { container } = render(); const slices = container.querySelectorAll("path"); slices.forEach((slice) => { const commands = parseSvgPathCommands(slice.getAttribute("d")); const startOfInnerArc = { x: commands[2].args[0], y: commands[2].args[1], }; const innerRadius = getDistanceFromOrigin(startOfInnerArc); expect(innerRadius).toBeCloseTo(INNER_RADIUS); }); }); }); describe("`startAngle` in conjunction with `endAngle`", () => { it("renders a portion of a chart from `startAngle` to `endAngle`", () => { const { container } = render( , ); const slices = container.querySelectorAll("path"); const firstSlice = slices[0]; const lastSlice = slices[slices.length - 1]; const arcStart = getSliceArcStart(firstSlice.getAttribute("d")); const arcEnd = getSliceArcEnd(lastSlice.getAttribute("d")); expect(getSvgCoordinatesAngleFromCartesianYAxis(arcStart)).toBeCloseTo( 270, ); expect(getSvgCoordinatesAngleFromCartesianYAxis(arcEnd)).toBeCloseTo(90); }); }); describe("the `width` prop", () => { it("determines the width of the containing viewBox", () => { const width = 200; const { container } = render(); const svg = container.querySelector("svg"); expect(svg?.getAttribute("viewBox")).toEqual(`0 0 ${width} 400`); }); }); describe("the `height` prop", () => { it("determines the height of the containing viewBox", () => { const height = 200; const { container } = render(); const svg = container.querySelector("svg"); expect(svg?.getAttribute("viewBox")).toEqual(`0 0 400 ${height}`); }); }); describe("the `colorScale` prop", () => { describe("if provided an array of CSS colors", () => { it("renders each slice with the next color in the array, reiterating through colors as necessary", () => { const data = Helpers.range(5); const colorScale = ["#fffff", "#eeeee", "#ddddd"]; const { container } = render( , ); const slices = container.querySelectorAll("path"); expect(slices).toHaveLength(5); slices.forEach((slice, index) => { expect(slice.getAttribute("style")).toContain( colorScale[index % colorScale.length], ); }); }); }); describe("if provided a string", () => { describe("and the string is a valid victory color scale", () => { it("renders the chart using the given color scale", () => { const VALID_VICTORY_COLOR_SCALE_NAMES = [ "grayscale", "qualitative", "heatmap", "warm", "cool", "red", "green", "blue", ]; VALID_VICTORY_COLOR_SCALE_NAMES.map((colorScaleName) => { const colorScale = Style.getColorScale(colorScaleName); const data = Helpers.range(colorScale.length + 1); const { container } = render( , ); Array.from(container.querySelectorAll("path")).map( (slice, index) => { const expectedColorScheme = colorScale[index % colorScale.length]; expect(slice.getAttribute("style")).toContain( expectedColorScheme, ); }, ); }); }); }); }); }); describe("event handling", () => { const clickHandler = jest.fn(); beforeEach(() => { clickHandler.mockReset(); }); it("attaches an event to the parent svg", () => { const { container } = render( , ); const svg = container.querySelector("svg"); fireEvent.click(svg!); expect(clickHandler).toBeCalled(); const contextualArg = clickHandler.mock.calls[0][1]; // the first argument is the standard event object expect(contextualArg.key).toEqual("pie-parent-parent"); }); it("attaches an event to data", () => { const { container } = render( , ); const slices = container.querySelectorAll("path"); slices.forEach((slice, index) => { fireEvent.click(slice); const contextualArg = clickHandler.mock.calls[index][1]; expect(contextualArg.key).toEqual(`pie-data-${index}`); }); }); it("attaches an event to label", () => { render( , ); labeledData.forEach((dataPoint, index) => { const label = screen.getByText(dataPoint.x); fireEvent.click(label); const contextualArg = clickHandler.mock.calls[index][1]; expect(contextualArg.slice.data._y).toEqual(dataPoint.y); }); }); }); describe("accessbility", () => { it("adds aria-label and tabIndex to Slice primitive", () => { const { container } = render( `${datum.x}`} tabIndex={({ index }) => Number(index) + 5} /> } />, ); const slices = container.querySelectorAll("path"); expect(slices).toHaveLength(labeledData.length); slices.forEach((slice, index) => { expect(slice.getAttribute("aria-label")).toEqual(labeledData[index].x); expect(slice.getAttribute("tabindex")).toEqual(`${index + 5}`); }); }); }); }); ================================================ FILE: packages/victory-pie/src/victory-pie.tsx ================================================ import React from "react"; import { addEvents, Helpers, Data, LineSegment, VictoryContainer, VictoryLabel, VictoryTheme, UserProps, EventPropTypeInterface, NumberOrCallback, SliceNumberOrCallback, StringOrNumberOrCallback, VictoryCommonProps, VictoryMultiLabelableProps, VictoryStyleInterface, EventsMixinClass, VictoryDatableProps, } from "victory-core"; import { getBaseProps } from "./helper-methods"; import { Slice, SliceProps, VictorySliceTTargetType, VictorySliceLabelPlacementType, VictorySliceLabelPositionType, } from "./slice"; export interface VictoryPieProps extends Omit, VictoryDatableProps, VictoryMultiLabelableProps { cornerRadius?: SliceNumberOrCallback; endAngle?: number; events?: EventPropTypeInterface< VictorySliceTTargetType, StringOrNumberOrCallback | string[] | number[] >[]; eventKey?: StringOrNumberOrCallback; innerRadius?: NumberOrCallback; labelIndicator?: boolean | React.ReactElement; labelIndicatorInnerOffset?: number; labelIndicatorOuterOffset?: number; labelPlacement?: | VictorySliceLabelPlacementType | ((props: SliceProps) => VictorySliceLabelPlacementType); labelPosition?: | VictorySliceLabelPositionType | ((props: SliceProps) => VictorySliceLabelPositionType); labelIndicatorComponent?: React.ReactElement; labelRadius?: number | ((props: SliceProps) => number); padAngle?: NumberOrCallback; radius?: NumberOrCallback; startAngle?: number; style?: VictoryStyleInterface; } const fallbackProps = { endAngle: 360, height: 400, innerRadius: 0, cornerRadius: 0, padAngle: 0, padding: 30, width: 400, startAngle: 0, colorScale: [ "#ffffff", "#f0f0f0", "#d9d9d9", "#bdbdbd", "#969696", "#737373", "#525252", "#252525", "#000000", ], labelPosition: "centroid", labelIndicatorInnerOffset: 15, labelIndicatorOuterOffset: 5, }; const datumHasXandY = (datum) => { return !Helpers.isNil(datum._x) && !Helpers.isNil(datum._y); }; // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryPieBase extends EventsMixinClass {} class VictoryPieBase extends React.Component { static animationWhitelist: (keyof VictoryPieProps)[] = [ "data", "endAngle", "height", "innerRadius", "cornerRadius", "padAngle", "padding", "colorScale", "startAngle", "style", "width", ]; static displayName = "VictoryPie"; static role = "pie"; static defaultTransitions = { onExit: { duration: 500, before: () => ({ _y: 0, label: " " }), }, onEnter: { duration: 500, before: () => ({ _y: 0, label: " " }), after: (datum) => ({ y_: datum._y, label: datum.label, }), }, }; static defaultProps: VictoryPieProps = { data: [ { x: "A", y: 1 }, { x: "B", y: 2 }, { x: "C", y: 3 }, { x: "D", y: 1 }, { x: "E", y: 2 }, ], standalone: true, dataComponent: , labelComponent: , containerComponent: , groupComponent: , sortOrder: "ascending", theme: VictoryTheme.grayscale, }; static getBaseProps(props: VictoryPieProps) { return getBaseProps(props, fallbackProps); } static getData = Data.getData; static expectedComponents: (keyof VictoryPieProps)[] = [ "dataComponent", "labelComponent", "groupComponent", "containerComponent", "labelIndicatorComponent", ]; // Overridden in victory-native shouldAnimate() { return Boolean(this.props.animate); } renderComponents(props: VictoryPieProps, shouldRenderDatum = datumHasXandY) { const { dataComponent, labelComponent, groupComponent, labelIndicator, labelPosition, } = props; if (!groupComponent) { throw new Error("VictoryPie expects a groupComponent prop"); } const showIndicator = labelIndicator && labelPosition === "centroid"; const children: React.ReactElement[] = []; if (dataComponent) { const dataComponents = this.dataKeys.reduce( (validDataComponents, _dataKey, index) => { const dataProps = this.getComponentProps( dataComponent, "data", index, ); if (shouldRenderDatum((dataProps as any).datum)) { validDataComponents.push( React.cloneElement(dataComponent, dataProps), ); } return validDataComponents; }, [], ); children.push(...dataComponents); } if (labelComponent) { const labelComponents = this.dataKeys .map((_dataKey, index) => { const labelProps = this.getComponentProps( labelComponent, "labels", index, ); if ( (labelProps as any).text !== undefined && (labelProps as any).text !== null ) { return React.cloneElement(labelComponent, labelProps); } return undefined; }) .filter( (comp: React.ReactElement | undefined): comp is React.ReactElement => comp !== undefined, ); children.push(...labelComponents); } if (showIndicator && labelIndicator) { let labelIndicatorComponent: React.ReactElement = ; if (typeof labelIndicator === "object") { // pass user provided react component labelIndicatorComponent = labelIndicator; } const labelIndicatorComponents = this.dataKeys.map((_dataKey, index) => { const labelIndicatorProps = this.getComponentProps( labelIndicatorComponent, "labelIndicators", index, ); return React.cloneElement(labelIndicatorComponent, labelIndicatorProps); }); children.push(...labelIndicatorComponents); } return this.renderContainer(groupComponent, children); } render(): React.ReactElement { const { animationWhitelist, role } = VictoryPie; const props = Helpers.modifyProps(this.props, fallbackProps, role); if (this.shouldAnimate()) { return this.animateComponent(props, animationWhitelist); } const children = this.renderComponents(props); const component = props.standalone ? this.renderContainer(props.containerComponent, children) : children; return UserProps.withSafeUserProps(component, props); } } export const VictoryPie = addEvents(VictoryPieBase); ================================================ FILE: packages/victory-pie/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-pie/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-polar-axis/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-polar-axis/CHANGELOG.md ================================================ # victory-polar-axis ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) * Improve types in victory-core helpers ([#2999](https://github.com/FormidableLabs/victory/pull/2999)) ## 37.3.2 ## 37.3.1 ### Patch Changes - Remove duplicate types from interfaces ([#2940](https://github.com/FormidableLabs/victory/pull/2940)) ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ### Patch Changes - Migrate victory-native to TypeScript ([#2739](https://github.com/FormidableLabs/victory/pull/2739)) ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ### Patch Changes - Migrate polar axis to typescript ([#2713](https://github.com/FormidableLabs/victory/pull/2713)) ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-polar-axis/README.md ================================================ # VictoryPolarAxis `victory-polar-axis@^30.0.0` exports `VictoryPolarAxis` To view documentation for `VictoryPolarAxis` please see https://commerce.nearform.com/open-source/victory/docs/victory-polar-axis To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-polar-axis.md ================================================ FILE: packages/victory-polar-axis/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-polar-axis/package.json ================================================ { "name": "victory-polar-axis", "version": "37.3.6", "description": "Polar Axis Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-polar-axis/src/helper-methods.ts ================================================ import defaults from "lodash/defaults"; import uniqBy from "lodash/uniqBy"; import { Helpers, LabelHelpers, Scale, Axis } from "victory-core"; import { VictoryPolarAxisProps } from "./types"; const getPosition = (r: number, angle: number, axis?: string) => { return axis === "x" ? r * Math.cos(angle) : -r * Math.sin(angle); }; const getAxisType = (props: VictoryPolarAxisProps) => { const typicalType = props.dependentAxis ? "radial" : "angular"; const invertedType = typicalType === "angular" ? "radial" : "angular"; return props.horizontal ? invertedType : typicalType; }; const filterTicks = (ticks, scale) => { const compareTicks = (t) => scale(t) % (2 * Math.PI); return uniqBy(ticks, compareTicks); }; const getEvaluatedStyles = (style, props) => { return { tickStyle: Helpers.evaluateStyle(style.ticks, props), labelStyle: Helpers.evaluateStyle(style.tickLabels, props), gridStyle: Helpers.evaluateStyle(style.grid, props), }; }; const getStyleObject = (props: VictoryPolarAxisProps) => { const { theme = {}, dependentAxis } = props; const generalAxisStyle = (theme.polarAxis && theme.polarAxis.style) || (theme.axis && theme.axis.style); const polarAxisType = dependentAxis ? "polarDependentAxis" : "polarIndependentAxis"; const standardAxisType = dependentAxis ? "dependentAxis" : "independentAxis"; const specificAxisStyle = theme?.[polarAxisType]?.style || theme?.[standardAxisType]?.style; const mergeStyles = () => { const styleNamespaces = [ "axis", "axisLabel", "grid", "parent", "tickLabels", "ticks", ]; return styleNamespaces.reduce((memo, curr) => { memo[curr] = defaults( {}, specificAxisStyle?.[curr], generalAxisStyle?.[curr], ); return memo; }, {}); }; return generalAxisStyle && specificAxisStyle ? mergeStyles() : specificAxisStyle || generalAxisStyle; }; const getRadius = (props: VictoryPolarAxisProps) => { const { left, right, top, bottom } = Helpers.getPadding(props.padding); const { width, height } = props; if (width === undefined || height === undefined) { throw new Error( "VictoryPolarAxis: width and height properties are required for standalone axes.", ); } return Math.min(width - left - right, height - top - bottom) / 2; }; const getRange = (props: VictoryPolarAxisProps, axis) => { // Return the range from props if one is given. if (props.range && props.range[axis]) { return props.range[axis]; } else if (props.range && Array.isArray(props.range)) { return props.range; } const axisType = getAxisType(props); if (axisType === "angular") { const startAngle = Helpers.degreesToRadians(props.startAngle); const endAngle = Helpers.degreesToRadians(props.endAngle); return [startAngle, endAngle]; } const radius = getRadius(props); return [props.innerRadius || 0, radius]; }; export const getScale = (props: VictoryPolarAxisProps) => { const axis = Axis.getAxis(props); const scale = Scale.getBaseScale(props, axis); const domain = Axis.getDomain(props, axis) || scale.domain(); const range = getRange(props, axis); scale.range(range); scale.domain(domain); return scale; }; export const getStyles = ( props: VictoryPolarAxisProps, styleObject: VictoryPolarAxisProps["style"] = {}, ) => { if (props.disableInlineStyles) { return {}; } const style = props.style || {}; const parentStyleProps = { height: "auto", width: "100%" }; return { parent: defaults(parentStyleProps, style.parent, styleObject.parent), axis: defaults({}, style.axis, styleObject.axis), axisLabel: defaults({}, style.axisLabel, styleObject.axisLabel), grid: defaults({}, style.grid, styleObject.grid), ticks: defaults({}, style.ticks, styleObject.ticks), tickLabels: defaults({}, style.tickLabels, styleObject.tickLabels), }; }; const getAxisAngle = (props: { axisAngle?: number; dependentAxis?: boolean; startAngle?: number; }) => { const { axisAngle, startAngle, dependentAxis } = props; const axis = Axis.getAxis(props); const axisValue = Axis.getAxisValue(props, axis); if (axisValue === undefined || !dependentAxis) { return axisAngle === undefined ? startAngle : axisAngle; } return Helpers.radiansToDegrees(axisValue); }; const getTickProps = ( props: VictoryPolarAxisProps, calculatedValues: { axisType: string; radius: number; scale: any; style: any; stringTicks: any; ticks: any; tickFormat: any; origin: { x: number; y: number }; }, tickValue, index, // eslint-disable-next-line max-params ) => { const { axisType, radius, scale, style, stringTicks, ticks, tickFormat, origin, } = calculatedValues; const text = tickFormat(tickValue, index, ticks); const tick = stringTicks ? stringTicks[index] : tickValue; const { tickStyle } = getEvaluatedStyles(style, { tick, tickValue, index, ticks, stringTicks, radius, scale, axisType, text, }); const axisAngle = axisType === "radial" ? getAxisAngle(props) : undefined; const tickPadding = tickStyle.padding || tickStyle.size || 0; const padAngle = Helpers.degreesToRadians(90 - axisAngle); const tickAngle = axisType === "angular" ? scale(tickValue) : Helpers.degreesToRadians(-1 * axisAngle); const tickRadius = axisType === "angular" ? radius : scale(tickValue); return axisType === "angular" ? { index, datum: tick, style: tickStyle, x1: getPosition(tickRadius, tickAngle, "x") + origin.x, y1: getPosition(tickRadius, tickAngle, "y") + origin.y, x2: getPosition(tickRadius + tickPadding, tickAngle, "x") + origin.x, y2: getPosition(tickRadius + tickPadding, tickAngle, "y") + origin.y, } : { index, datum: tick, style: tickStyle, x1: tickRadius * Math.cos(tickAngle) + Math.cos(padAngle) * tickPadding + origin.x, x2: tickRadius * Math.cos(tickAngle) - Math.cos(padAngle) * tickPadding + origin.x, y1: tickRadius * Math.sin(tickAngle) + Math.sin(padAngle) * tickPadding + origin.y, y2: tickRadius * Math.sin(tickAngle) - Math.sin(padAngle) * tickPadding + origin.y, }; }; const getTickLabelProps = ( props: VictoryPolarAxisProps, calculatedValues: { axisType: string; radius: number; tickFormat: any; style: any; scale: any; ticks: any; stringTicks: any; origin: { x: number; y: number }; }, tickValue, index, // eslint-disable-next-line max-params ) => { const { axisType, radius, tickFormat, style, scale, ticks, stringTicks, origin, } = calculatedValues; const text = tickFormat(tickValue, index, ticks); const tick = stringTicks ? stringTicks[index] : tickValue; const { labelStyle } = getEvaluatedStyles(style, { text, tick, tickValue, index, ticks, stringTicks, radius, scale, axisType, }); const { tickLabelComponent } = props; const labelPlacement = tickLabelComponent?.props.labelPlacement ? tickLabelComponent.props.labelPlacement : props.labelPlacement; const tickPadding = labelStyle.padding || 0; const angularPadding = 0; // TODO: do some geometry const axisAngle = axisType === "radial" ? getAxisAngle(props) : undefined; const labelAngle = axisType === "angular" ? Helpers.radiansToDegrees(scale(tickValue)) : axisAngle + angularPadding; const textAngle = labelStyle.angle === undefined ? LabelHelpers.getPolarAngle( Object.assign({}, props, { labelPlacement }), labelAngle, ) : labelStyle.angle; const labelRadius = axisType === "angular" ? radius + tickPadding : scale(tickValue); const textAnchor = labelStyle.textAnchor || LabelHelpers.getPolarTextAnchor( Object.assign({}, props, { labelPlacement }), labelAngle, ); return { index, datum: tick, style: labelStyle, angle: textAngle, textAnchor, text, x: labelRadius * Math.cos(Helpers.degreesToRadians(labelAngle)) + origin.x, y: -labelRadius * Math.sin(Helpers.degreesToRadians(labelAngle)) + origin.y, }; }; const getGridProps = ( props: VictoryPolarAxisProps, calculatedValues: { axisType: string; radius: number; style: any; scale: any; stringTicks: any; ticks: any; tickFormat: any; origin: { x: number; y: number }; }, tickValue, index, // eslint-disable-next-line max-params ) => { const { axisType, radius, style, scale, stringTicks, ticks, tickFormat, origin, } = calculatedValues; const text = tickFormat(tickValue, index, ticks); const { startAngle, endAngle, innerRadius = 0 } = props; const tick = stringTicks ? stringTicks[index] : tickValue; const { gridStyle } = getEvaluatedStyles(style, { tick, tickValue, index, ticks, stringTicks, radius, scale, axisType, text, }); const angle = scale(tickValue); return axisType === "angular" ? { index, datum: tick, style: gridStyle, x1: getPosition(radius, angle, "x") + origin.x, y1: getPosition(radius, angle, "y") + origin.y, x2: getPosition(innerRadius, angle, "x") + origin.x, y2: getPosition(innerRadius, angle, "y") + origin.y, } : { style: gridStyle, index, datum: tick, cx: origin.x, cy: origin.y, r: scale(tickValue), startAngle, endAngle, }; }; const getAxisLabelProps = ( props: VictoryPolarAxisProps, calculatedValues: { axisType: string; radius: number; style: any; origin: { x: number; y: number }; }, ) => { const { axisType, radius, style, origin } = calculatedValues; const { axisLabelComponent } = props; if (axisType !== "radial") { return {}; } const labelPlacement = axisLabelComponent?.props.labelPlacement ? axisLabelComponent.props.labelPlacement : props.labelPlacement; const labelStyle = (style && style.axisLabel) || {}; const axisAngle = axisType === "radial" ? getAxisAngle(props) : undefined; const textAngle = labelStyle.angle === undefined ? LabelHelpers.getPolarAngle( Object.assign({}, props, { labelPlacement }), axisAngle, ) : labelStyle.angle; const labelRadius = radius + (labelStyle.padding || 0); const textAnchor = labelStyle.textAnchor || LabelHelpers.getPolarTextAnchor( Object.assign({}, props, { labelPlacement }), axisAngle, ); const verticalAnchor = labelStyle.verticalAnchor || LabelHelpers.getPolarVerticalAnchor( Object.assign({}, props, { labelPlacement }), axisAngle, ); return { style: labelStyle, angle: textAngle, textAnchor, verticalAnchor, text: props.label, x: getPosition(labelRadius, Helpers.degreesToRadians(axisAngle), "x") + origin.x, y: getPosition(labelRadius, Helpers.degreesToRadians(axisAngle), "y") + origin.y, }; }; const getAxisProps = (modifiedProps, calculatedValues) => { const { style, axisType, radius, origin } = calculatedValues; const { startAngle, endAngle, innerRadius = 0 } = modifiedProps; const axisAngle = axisType === "radial" ? Helpers.degreesToRadians(getAxisAngle(modifiedProps)) : undefined; return axisType === "radial" ? { style: style.axis, x1: getPosition(innerRadius, axisAngle, "x") + origin.x, x2: getPosition(radius, axisAngle, "x") + origin.x, y1: getPosition(innerRadius, axisAngle, "y") + origin.y, y2: getPosition(radius, axisAngle, "y") + origin.y, } : { style: style.axis, cx: origin.x, cy: origin.y, r: radius, startAngle, endAngle, }; }; const getCalculatedValues = (initialProps: VictoryPolarAxisProps) => { const props = Object.assign({ polar: true }, initialProps); const defaultStyles = getStyleObject(props); const style = getStyles(props, defaultStyles); const padding = Helpers.getPadding(props.padding); const axis = Axis.getAxis(props); const axisType = getAxisType(props); const stringTicks = Axis.stringTicks(props) ? props.tickValues : undefined; const domain = Axis.getDomain(props, axis); const range = getRange(props, axis); const scale = getScale(props); const initialTicks = Axis.getTicks(props, scale); const ticks = axisType === "angular" ? filterTicks(initialTicks, scale) : initialTicks; const tickFormat = Axis.getTickFormat(props, scale); const radius = getRadius(props); // @ts-expect-error props is not typed but contains width and height const origin = Helpers.getPolarOrigin(props); return { axis, style, padding, stringTicks, axisType, scale, ticks, tickFormat, domain, range, radius, origin, }; }; export const getBaseProps = ( initialProps: VictoryPolarAxisProps, fallbackProps, ) => { const props = Axis.modifyProps(initialProps, fallbackProps); const calculatedValues = getCalculatedValues(props); const { style, scale, ticks, domain } = calculatedValues; const { width, height, standalone, theme, name } = props; const axisProps = getAxisProps(props, calculatedValues); const axisLabelProps = getAxisLabelProps(props, calculatedValues); const initialChildProps = { parent: { style: style.parent, ticks, scale, width, height, domain, standalone, theme, name, }, }; return ticks.reduce((childProps, tick, index) => { childProps[index] = { axis: axisProps, axisLabel: axisLabelProps, ticks: getTickProps(props, calculatedValues, tick, index), tickLabels: getTickLabelProps(props, calculatedValues, tick, index), grid: getGridProps(props, calculatedValues, tick, index), }; return childProps; }, initialChildProps); }; ================================================ FILE: packages/victory-polar-axis/src/index.ts ================================================ export * from "./types"; export * from "./victory-polar-axis"; ================================================ FILE: packages/victory-polar-axis/src/types.ts ================================================ import React from "react"; import { EventPropTypeInterface, LabelOrientationType, VictoryAxisCommonProps, VictoryCommonProps, VictorySingleLabelableProps, } from "victory-core"; export type VictoryPolarAxisTTargetType = | "axis" | "axisLabel" | "grid" | "ticks" | "tickLabels"; export interface VictoryPolarAxisProps extends VictoryAxisCommonProps, VictoryCommonProps, VictorySingleLabelableProps { axisAngle?: number; circularAxisComponent?: React.ReactElement; circularGridComponent?: React.ReactElement; endAngle?: number; events?: EventPropTypeInterface< VictoryPolarAxisTTargetType, string | number >[]; innerRadius?: number; labelOrientation?: LabelOrientationType; labelPlacement?: LabelOrientationType; startAngle?: number; } ================================================ FILE: packages/victory-polar-axis/src/victory-polar-axis.tsx ================================================ import React from "react"; import isEmpty from "lodash/isEmpty"; import { VictoryLabel, VictoryContainer, VictoryTheme, LineSegment, addEvents, Arc, Axis, EventsMixinClass, } from "victory-core"; import { getScale, getStyles, getBaseProps } from "./helper-methods"; import { VictoryPolarAxisProps } from "./types"; const fallbackProps: Partial = { width: 450, height: 300, padding: 50, }; // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryPolarAxisBase extends EventsMixinClass {} class VictoryPolarAxisBase extends React.Component { static animationWhitelist = [ "style", "domain", "range", "tickCount", "tickValues", "padding", "width", "height", ]; static displayName = "VictoryAxis"; static role = "axis"; static defaultTransitions = { onExit: { duration: 500, }, onEnter: { duration: 500, }, }; static defaultProps: VictoryPolarAxisProps = { axisComponent: , axisLabelComponent: , circularAxisComponent: , circularGridComponent: , containerComponent: , endAngle: 360, gridComponent: , groupComponent: , labelPlacement: "parallel", startAngle: 0, standalone: true, theme: VictoryTheme.grayscale, tickComponent: , tickLabelComponent: , }; static getDomain = Axis.getDomain; static getAxis = Axis.getAxis; static getScale(props) { return getScale(props); } static getStyles(props) { return getStyles(props, fallbackProps.style); } static getBaseProps(props) { return getBaseProps(props, fallbackProps); } static expectedComponents = [ "axisComponent", "circularAxisComponent", "groupComponent", "containerComponent", "tickComponent", "tickLabelComponent", "gridComponent", "circularGridComponent", ]; renderAxisLine(props: VictoryPolarAxisProps) { const { dependentAxis } = props; const axisComponent = dependentAxis ? props.axisComponent : props.circularAxisComponent; const axisProps = this.getComponentProps(axisComponent, "axis", 0); return React.cloneElement(axisComponent!, axisProps); } renderLabel(props: VictoryPolarAxisProps) { const { axisLabelComponent, dependentAxis, label } = props; if (!label || !dependentAxis) { return null; } const axisLabelProps = this.getComponentProps( axisLabelComponent, "axisLabel", 0, ); return React.cloneElement(axisLabelComponent!, axisLabelProps); } renderAxis(props: VictoryPolarAxisProps) { const { tickComponent, tickLabelComponent, name } = props; const shouldRender = (componentProps) => { const { style = {}, events = {} } = componentProps; const visible = style.stroke !== "transparent" && style.stroke !== "none" && style.strokeWidth !== 0; return visible || !isEmpty(events); }; const axisType = props.dependentAxis ? "radial" : "angular"; const gridComponent = axisType === "radial" ? props.circularGridComponent : props.gridComponent; const tickComponents = this.dataKeys .map((key, index) => { const tickProps = Object.assign( { key: `${name}-tick-${key}` }, this.getComponentProps(tickComponent, "ticks", index), ); const TickComponent = React.cloneElement(tickComponent!, tickProps); return shouldRender(TickComponent.props) ? TickComponent : undefined; }) .filter(Boolean); const gridComponents = this.dataKeys .map((key, index) => { const gridProps = Object.assign( { key: `${name}-grid-${key}` }, this.getComponentProps(gridComponent, "grid", index), ); const GridComponent = React.cloneElement(gridComponent!, gridProps); return shouldRender(GridComponent.props) ? GridComponent : undefined; }) .filter(Boolean); const tickLabelComponents = this.dataKeys.map((key, index) => { const tickLabelProps = Object.assign( { key: `${name}-tick-${key}` }, this.getComponentProps(tickLabelComponent, "tickLabels", index), ); return React.cloneElement(tickLabelComponent!, tickLabelProps); }); const axis = this.renderAxisLine(props); const axisLabel = this.renderLabel(props); const children = [ axis, axisLabel, ...tickComponents, ...gridComponents, ...tickLabelComponents, ]; return this.renderGroup(props, children); } // Overridden in victory-native renderGroup(props: VictoryPolarAxisProps, children: React.ReactNode) { const { groupComponent } = props; return React.cloneElement(groupComponent!, {}, children); } shouldAnimate() { return !!this.props.animate; } render(): React.ReactElement { const { animationWhitelist } = VictoryPolarAxis; const props = Axis.modifyProps(this.props, fallbackProps); if (this.shouldAnimate()) { return this.animateComponent(props, animationWhitelist); } const children = this.renderAxis(props); return props.standalone ? this.renderContainer(props.containerComponent, children) : children; } } const options = { components: [ { name: "axis", index: 0 }, { name: "axisLabel", index: 0 }, { name: "grid" }, { name: "parent", index: "parent" }, { name: "ticks" }, { name: "tickLabels" }, ], }; export const VictoryPolarAxis = addEvents(VictoryPolarAxisBase, options); ================================================ FILE: packages/victory-polar-axis/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-polar-axis/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-scatter/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-scatter/CHANGELOG.md ================================================ # victory-scatter ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Improve types in victory-core helpers ([#2999](https://github.com/FormidableLabs/victory/pull/2999)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash array utils with native code ([#2810](https://github.com/FormidableLabs/victory/pull/2810)) * Replace lodash values and mapValues with native code ([#2808](https://github.com/FormidableLabs/victory/pull/2808)) - Replace lodash isNil and isNan with native code ([#2800](https://github.com/FormidableLabs/victory/pull/2800)) * Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ### Patch Changes - Migrate victory-native to TypeScript ([#2739](https://github.com/FormidableLabs/victory/pull/2739)) ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) * Replace instances of lodash.range with equivalent native code ([#2760](https://github.com/FormidableLabs/victory/pull/2760)) ## 36.8.4 ## 36.8.3 ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - - Removed Template Literal Types to increase TS compatibility (fixes [#2418](https://github.com/FormidableLabs/victory/issues/2418)) ([#2420](https://github.com/FormidableLabs/victory/pull/2420)) - Improved type for `VictoryLabelProps["textAnchor"]` (fixes [#2361](https://github.com/FormidableLabs/victory/issues/2361)) - Fixed exported types for `VictoryAxis`, `VictoryBoxPlot`, `VictoryErrorBar`, and `VictoryScatter` (fixes [#2411](https://github.com/FormidableLabs/victory/issues/2411)) - Migrate `victory-cursor-container` to TS (fixes [#2402](https://github.com/FormidableLabs/victory/issues/2402)) - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-scatter/README.md ================================================ # VictoryScatter `victory-scatter@^30.0.0` exports `VictoryScatter` To view documentation for `VictoryScatter` please see https://commerce.nearform.com/open-source/victory/docs/victory-scatter To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-scatter.md ================================================ FILE: packages/victory-scatter/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-scatter/package.json ================================================ { "name": "victory-scatter", "version": "37.3.6", "description": "Scatter Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-scatter/src/helper-methods.test.tsx ================================================ import * as Helpers from "./helper-methods"; describe("victory-scatter/helper-methods", () => { describe("getSize", () => { beforeEach(() => { jest.spyOn(Helpers, "getBubbleSize"); }); afterEach(() => { jest.clearAllMocks(); }); const data = [ { x: 1, y: 2, z: 1, t: 4 }, { x: 2, y: 3, z: 1, t: 2 }, ]; const datum = data[0]; it("returns a size attribute from data", () => { const point = { size: 3, ...datum }; const sizeResult = Helpers.getSize(point, {}); expect(Helpers.getBubbleSize).not.toHaveBeenCalled(); expect(sizeResult).toEqual(3); }); it("returns 1 if the size attribute is less than one", () => { const point = { size: -2, ...datum }; const sizeResult = Helpers.getSize(point, {}); expect(Helpers.getBubbleSize).not.toHaveBeenCalled(); expect(sizeResult).toEqual(1); }); it("returns size from props, if no size is set on data", () => { const sizeResult = Helpers.getSize(datum, { data, size: 2 }); expect(Helpers.getBubbleSize).not.toHaveBeenCalled(); expect(sizeResult).toEqual(2); }); it("calculates a bubbleSize if a bubbleProperty is specified, and size is not set", () => { const sizeResult = Helpers.getSize(datum, { data, z: "t" }); expect(sizeResult).toEqual(5); }); }); describe("getBubbleSize", () => { it("determines the size of a point", () => { const data = [ { x: 1, y: 2, z: 5 }, { x: 2, y: 3, z: 1 }, ]; const props = { data, z: "z" }; const sizeResult = Helpers.getBubbleSize(data[0], props); expect(sizeResult).toEqual(5); }); }); describe("getSymbol", () => { const data = { symbol: "star" }; const props = { symbol: "plus" }; it("returns 'circle' for bubble plots", () => { const symbolResult = Helpers.getSymbol( {}, { ...props, bubbleProperty: "z" }, ); expect(symbolResult).toEqual("circle"); }); it("returns a symbol from data if one is given", () => { const symbolResult = Helpers.getSymbol(data, props); expect(symbolResult).toEqual("star"); }); it("returns a symbol from props if no symbol is found on data", () => { const symbolResult = Helpers.getSymbol({}, props); expect(symbolResult).toEqual("plus"); }); }); }); ================================================ FILE: packages/victory-scatter/src/helper-methods.tsx ================================================ import { Helpers, LabelHelpers, Data, Domain, Scale } from "victory-core"; export const getSymbol = (data, props) => { if (props.bubbleProperty) { return "circle"; } return data.symbol || props.symbol; }; export const getBubbleSize = (datum, props) => { const { data, z, maxBubbleSize, minBubbleSize } = props; const zData = data.map((point) => point[z]); const zMin = Math.min(...zData); const zMax = Math.max(...zData); const getMaxRadius = () => { const minPadding = Math.min( ...Object.values(Helpers.getPadding(props.padding)), ); return Math.max(minPadding, 5); // eslint-disable-line no-magic-numbers }; const maxRadius = maxBubbleSize || getMaxRadius(); const minRadius = minBubbleSize || maxRadius * 0.1; // eslint-disable-line no-magic-numbers if (zMax === zMin) { return Math.max(minRadius, 1); } const maxArea = Math.PI * Math.pow(maxRadius, 2); const minArea = Math.PI * Math.pow(minRadius, 2); const pointArea = ((datum[z] - zMin) / (zMax - zMin)) * maxArea; const area = Math.max(pointArea, minArea); const radius = Math.sqrt(area / Math.PI); return Math.max(radius, 1); }; export const getSize = (datum, props) => { const { size, z } = props; if (datum.size) { return typeof datum.size === "function" ? datum.size : Math.max(datum.size, 1); } else if (typeof props.size === "function") { return size; } else if (datum[z]) { return getBubbleSize(datum, props); } return Math.max(size || 0, 1); }; const getCalculatedValues = (props) => { const defaultStyles = Helpers.getDefaultStyles(props, "scatter"); const style = Helpers.getStyles(props.style, defaultStyles); const range = { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; const domain = { x: Domain.getDomain(props, "x"), y: Domain.getDomain(props, "y"), }; const scale = { x: Scale.getBaseScale(props, "x") .domain(domain.x) .range(props.horizontal ? range.y : range.x), y: Scale.getBaseScale(props, "y") .domain(domain.y) .range(props.horizontal ? range.x : range.y), }; const origin = props.polar ? props.origin || Helpers.getPolarOrigin(props) : undefined; const z = props.bubbleProperty || "z"; let data = Data.getData(props); data = Data.formatDataFromDomain(data, domain); return { domain, data, scale, style, origin, z }; }; export const getBaseProps = (initialProps, fallbackProps) => { const modifiedProps = Helpers.modifyProps( initialProps, fallbackProps, "scatter", ); const props = Object.assign( {}, modifiedProps, getCalculatedValues(modifiedProps), ); const { data, domain, events, height, origin, padding, polar, scale, name, sharedEvents, standalone, style, theme, width, labels, horizontal, disableInlineStyles, } = props; const initialChildProps = { parent: { style: style.parent, scale, domain, data, height, width, standalone, theme, origin, polar, padding, name, horizontal, }, }; return data.reduce((childProps, datum, index) => { const eventKey = !Helpers.isNil(datum.eventKey) ? datum.eventKey : index; const { x, y } = Helpers.scalePoint(props, datum); const dataProps = { x, y, datum, data, index, scale, polar, origin, horizontal, size: getSize(datum, props), symbol: getSymbol(datum, props), style: disableInlineStyles ? {} : style.data, disableInlineStyles, }; childProps[eventKey] = { data: dataProps }; const text = LabelHelpers.getText(props, datum, index); if ( (text !== undefined && text !== null) || (labels && (events || sharedEvents)) ) { childProps[eventKey].labels = LabelHelpers.getProps(props, index); } return childProps; }, initialChildProps); }; ================================================ FILE: packages/victory-scatter/src/index.ts ================================================ export * from "./victory-scatter"; ================================================ FILE: packages/victory-scatter/src/victory-scatter.test.tsx ================================================ import { fireEvent, render, screen } from "@testing-library/react"; import React from "react"; import { Helpers, Point, VictoryLabel } from "victory-core"; import { convertSvgCoordinatesToCartesian, getSvgPointCoordinates, isCircle, } from "../../../test/helpers"; import { VictoryScatter } from "./victory-scatter"; describe("components/victory-scatter", () => { describe("default component rendering", () => { it("accepts safe user props", () => { render( , ); const container = screen.getByTestId("victory-scatter"); expect(screen.getByLabelText("Chart")).toBeDefined(); expect(container).not.toHaveAttribute("unsafe-prop"); expect(container.nodeName).toBe("svg"); }); it("renders an svg with the correct width and height", () => { const { container } = render(); const svg = container.querySelector("svg"); expect(svg?.style.width).toEqual("100%"); expect(svg?.style.height).toEqual("100%"); }); it("renders an svg with the correct viewBox", () => { const { container } = render(); const svg = container.querySelector("svg"); const viewBoxValue = `0 0 ${450} ${300}`; expect(svg).toHaveAttribute("viewBox", viewBoxValue); }); it("renders 51 points", () => { const { container } = render(); const points = container.querySelectorAll("path"); expect(points).toHaveLength(51); }); it("renders each point as a circle", () => { const { container } = render(); const points = container.querySelectorAll("path"); expect(points).not.toHaveLength(0); points.forEach((point) => { expect(isCircle(point.getAttribute("d"))).toBeTruthy(); }); }); }); describe("rendering data", () => { it("renders injected points for {x, y} shaped data (default)", () => { const data = Helpers.range(10).map((i) => ({ x: i, y: i })); render( } />, ); const points = screen.getAllByTestId("point"); expect(points).toHaveLength(10); }); it("renders points for {x, y} shaped data (default)", () => { const data = Helpers.range(10).map((i) => ({ x: i, y: i })); const { container } = render(); const points = container.querySelectorAll("path"); expect(points).toHaveLength(10); }); it("renders points for array-shaped data", () => { const data = Helpers.range(4).map((i) => [i, i]); const { container } = render(); const points = container.querySelectorAll("path"); expect(points).toHaveLength(4); }); it("renders points for deeply-nested data", () => { const data = Helpers.range(4).map((i) => ({ a: { b: [{ x: i, y: i }] }, })); const { container } = render( , ); const points = container.querySelectorAll("path"); expect(points).toHaveLength(4); }); it("renders data values with null accessor", () => { const data = Helpers.range(30); const { container } = render( // @ts-expect-error "'null' is not assignable to 'x'" , ); const points = container.querySelectorAll("path"); expect(points).toHaveLength(30); }); it("renders points in the correct positions", () => { const svgDimensions = { width: 350, height: 200, padding: 75 }; const { container } = render( , ); const domain = { x: [0, 5], y: [0, 5] }; const points = container.querySelectorAll("path"); const svgCoordinates = Array.from(points).map(getSvgPointCoordinates); const coordinates = svgCoordinates.map((coord) => { return convertSvgCoordinatesToCartesian(coord, svgDimensions, domain); }); expect(coordinates).toEqual([ [0, 0], [2, 3], [5, 5], ]); }); it("does not render data with null x or y values", () => { const data = [ { x: 1, y: 2 }, { x: null, y: 4 }, { x: 5, y: null }, ]; const { container } = render(); expect(container.querySelectorAll("path")).toHaveLength(1); }); }); describe("event handling", () => { let clickHandler; beforeEach(() => { clickHandler = jest.fn(); }); afterEach(() => { clickHandler.mockClear(); }); it("attaches an event to the parent svg", () => { const { container } = render( , ); const svg = container.querySelector("svg"); fireEvent.click(svg!); expect(clickHandler).toBeCalled(); // the first argument is the standard evt object expect(Object.keys(clickHandler.mock.calls[0][1])).toEqual( expect.arrayContaining(["data", "scale", "width", "height", "style"]), ); }); it("attaches an event to data", () => { const { container } = render( , ); const Data = container.querySelectorAll("path"); expect(Data).not.toHaveLength(0); Data.forEach((node, index) => { fireEvent.click(node); expect(clickHandler).toHaveBeenCalled(); expect(`${clickHandler.mock.calls[index][2]}`).toEqual(`${index}`); }); }); it("attaches an event to a label", () => { const data = [ { eventKey: 0, _x: 0, _y: 0, x: 0, y: 0, label: "0" }, { eventKey: 1, _x: 1, _y: 1, x: 1, y: 1, label: "1" }, { eventKey: 2, _x: 2, _y: 2, x: 2, y: 2, label: "2" }, ]; render( } events={[ { target: "labels", eventHandlers: { onClick: clickHandler }, }, ]} />, ); const Labels = screen.getAllByTestId("label"); Labels.forEach((node, index) => { fireEvent.click(node); expect(clickHandler).toHaveBeenCalled(); // the first argument is the standard evt object expect(clickHandler.mock.calls[index][1].datum).toEqual(data[index]); expect(`${clickHandler.mock.calls[index][2]}`).toEqual(`${index}`); }); }); }); describe("accessibility", () => { it("adds an aria role to each point in the series", () => { const data = Helpers.range(5).map((y, x) => ({ x, y })); render(); expect(screen.getAllByRole("presentation")).toHaveLength(5); }); it("adds an aria-label and tabIndex to Point primitive", () => { const data = Helpers.range(2, 7).map((x) => ({ x, y: x + 2 })); render( `scatter point x: ${datum.x}, y:${datum.y}` } tabIndex={({ index }) => Number(index) + 10} /> } />, ); const points = screen.getAllByTestId("point"); expect(points).toHaveLength(5); points.forEach((p, i) => { expect(p.getAttribute("aria-label")).toEqual( `scatter point x: ${data[i].x}, y:${data[i].y}`, ); expect(parseInt(p.getAttribute("tabindex")!, 10)).toEqual(i + 10); }); }); }); }); ================================================ FILE: packages/victory-scatter/src/victory-scatter.tsx ================================================ import React from "react"; import { Helpers, VictoryLabel, addEvents, VictoryContainer, VictoryTheme, DefaultTransitions, Data, Domain, Point, UserProps, EventPropTypeInterface, ScatterSymbolType, StringOrNumberOrCallback, EventsMixinClass, VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, VictoryStyleInterface, } from "victory-core"; import { getBaseProps } from "./helper-methods"; const fallbackProps = { width: 450, height: 300, padding: 50, size: 3, symbol: "circle", }; export type VictoryScatterTTargetType = "data" | "labels" | "parent"; export interface VictoryScatterProps extends VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps { bubbleProperty?: string; children?: React.ReactElement | React.ReactElement[]; events?: EventPropTypeInterface< VictoryScatterTTargetType, StringOrNumberOrCallback >[]; eventKey?: StringOrNumberOrCallback; maxBubbleSize?: number; minBubbleSize?: number; size?: number | { (data: any): number }; style?: VictoryStyleInterface; symbol?: ScatterSymbolType | { (data: any): ScatterSymbolType }; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryScatterBase extends EventsMixinClass {} /** * Draw area charts with React. VictoryArea is a composable component, so it doesn't include axes. * Add VictoryArea as a child of VictoryChart for a complete chart. */ class VictoryScatterBase extends React.Component { static animationWhitelist = [ "data", "domain", "height", "maxBubbleSize", "padding", "samples", "size", "style", "width", ]; static displayName = "VictoryScatter"; static role = "scatter"; static defaultTransitions = DefaultTransitions.discreteTransitions(); static defaultProps: VictoryScatterProps = { containerComponent: , dataComponent: , labelComponent: , groupComponent: , samples: 50, sortOrder: "ascending", standalone: true, theme: VictoryTheme.grayscale, }; static getDomain = Domain.getDomain; static getData = Data.getData; static getBaseProps(props) { return getBaseProps(props, fallbackProps); } static expectedComponents = [ "dataComponent", "labelComponent", "groupComponent", "containerComponent", ]; // Overridden in native versions shouldAnimate() { return !!this.props.animate; } render(): React.ReactElement { const { animationWhitelist, role } = VictoryScatter; const props = Helpers.modifyProps(this.props, fallbackProps, role); if (this.shouldAnimate()) { return this.animateComponent(props, animationWhitelist); } const children = this.renderData(props); const component = props.standalone ? this.renderContainer(props.containerComponent, children) : children; return UserProps.withSafeUserProps(component, props); } } export const VictoryScatter = addEvents(VictoryScatterBase); ================================================ FILE: packages/victory-scatter/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-scatter/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-selection-container/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-selection-container/CHANGELOG.md ================================================ # victory-selection-container ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Refactor containers and portal to function components ([#2799](https://github.com/FormidableLabs/victory/pull/2799)) * Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash keys with native code ([#2811](https://github.com/FormidableLabs/victory/pull/2811)) * Replace lodash array utils with native code ([#2810](https://github.com/FormidableLabs/victory/pull/2810)) - Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ### Patch Changes - Fix victory-native component prop types ([#2785](https://github.com/FormidableLabs/victory/pull/2785)) ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ### Patch Changes - Migrated victory-selection-container to TypeScript ([#2706](https://github.com/FormidableLabs/victory/pull/2706)) ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Improved the exported types (fixes [#2451](https://github.com/FormidableLabs/victory/issues/2451)) ([#2452](https://github.com/FormidableLabs/victory/pull/2452)) - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Added explicit `any` type defs (fixes [#2358](https://github.com/FormidableLabs/victory/issues/2358)) ([`57ed0fe30`](https://github.com/FormidableLabs/victory/commit/57ed0fe304dbc8753da1126a02d44de8004e96aa)) * Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) * Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-selection-container/README.md ================================================ # VictorySelectionContainer `victory-selection-container@^30.0.0` exports `VictorySelectionContainer`, `selectionContainerMixin` and `SelectionHelpers` To view documentation for `VictorySelectionContainer` please see https://commerce.nearform.com/open-source/victory/docs/victory-selection-container To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-selection-container.md ================================================ FILE: packages/victory-selection-container/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-selection-container/package.json ================================================ { "name": "victory-selection-container", "version": "37.3.6", "description": "Interactive Selection Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "devDependencies": { "victory-bar": "*", "victory-vendor": "*" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-bar:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-bar:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-bar:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-bar:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-selection-container/src/index.ts ================================================ export * from "./victory-selection-container"; export * from "./selection-helpers"; ================================================ FILE: packages/victory-selection-container/src/selection-helpers.test.tsx ================================================ import React from "react"; import { VictoryBar } from "victory-bar"; import * as d3Scale from "victory-vendor/d3-scale"; import { SelectionHelpers } from "./selection-helpers"; const scale = { x: d3Scale.scaleLinear(), y: d3Scale.scaleLinear() }; describe("helpers/selection", () => { describe("getDatasets", () => { it("returns data from props", () => { const data = [ { x: 1, y: 3 }, { x: 2, y: 5 }, ]; const props = { data }; const dataset = SelectionHelpers.getDatasets(props); expect(dataset).toEqual([{ data }]); }); it("returns data from children", () => { const data = [ { eventKey: 0, x: 1, y: 3 }, { eventKey: 1, x: 2, y: 5 }, ]; const expectedReturn = [ { eventKey: 0, x: 1, _x: 1, y: 3, _y: 3 }, { eventKey: 1, x: 2, _x: 2, y: 5, _y: 5 }, ]; const name = "points"; const children = [React.createElement(VictoryBar, { name, data })]; const props = { children }; const dataset = SelectionHelpers.getDatasets(props); expect(dataset).toEqual([{ childName: name, data: expectedReturn }]); }); }); describe("filterDatasets", () => { it("returns null when no datasets are within bounds", () => { const datasets = [ { childName: "a", data: [ { eventKey: 0, _x: 1, _y: 3 }, { eventKey: 1, _x: 2, _y: 5 }, ], }, ]; const props = { scale, x1: 0, y1: 0, x2: 0.5, y2: 0.5 }; const filteredData = SelectionHelpers.filterDatasets(props, datasets); expect(filteredData).toBeNull(); }); it("returns data points within bounds", () => { const data = [ { eventKey: 0, _x: 0, _y: 0 }, { eventKey: 1, _x: 2, _y: 5 }, ]; const childName = "a"; const datasets = [{ childName, data }]; const props = { scale, x1: 0, y1: 0, x2: 0.5, y2: 0.5 }; const filteredData = SelectionHelpers.filterDatasets(props, datasets); const expected = { eventKey: [0], data: [data[0]] }; expect(filteredData).toEqual([Object.assign({ childName }, expected)]); }); }); }); ================================================ FILE: packages/victory-selection-container/src/selection-helpers.tsx ================================================ import { Selection, Data, Helpers, Datum } from "victory-core"; import defaults from "lodash/defaults"; import throttle from "lodash/throttle"; import React from "react"; const ON_MOUSE_MOVE_THROTTLE_MS = 16; class SelectionHelpersClass { getDimension(props) { const { horizontal, selectionDimension } = props; if (!horizontal || !selectionDimension) { return selectionDimension; } return selectionDimension === "x" ? "y" : "x"; } getDatasets(props) { if (props.data) { return [{ data: props.data }]; } const getData = (childProps) => { const data = Data.getData(childProps); return Array.isArray(data) && data.length > 0 ? data : undefined; }; const iteratee = (child, childName, parent) => { const blacklist = props.selectionBlacklist || []; let childElement; if (!Data.isDataComponent(child) || blacklist.includes(childName)) { return null; } else if (child.type && Helpers.isFunction(child.type.getData)) { childElement = parent ? React.cloneElement(child, parent.props) : child; const childData = childElement.props && childElement.type.getData(childElement.props); return childData ? { childName, data: childData } : null; } const childData = getData(childElement.props); return childData ? { childName, data: childData } : null; }; return Helpers.reduceChildren( React.Children.toArray(props.children), iteratee, props, ); } filterDatasets(props, datasets) { const filtered = datasets.reduce((memo, dataset) => { const selectedData = this.getSelectedData(props, dataset.data); return selectedData ? memo.concat({ childName: dataset.childName, eventKey: selectedData.eventKey, data: selectedData.data, }) : memo; }, []); return filtered.length ? filtered : null; } getSelectedData(props, dataset) { const { x1, y1, x2, y2 } = props; const withinBounds = (d) => { const scaledPoint = Helpers.scalePoint(props, d); return ( scaledPoint.x >= Math.min(x1, x2) && scaledPoint.x <= Math.max(x1, x2) && scaledPoint.y >= Math.min(y1, y2) && scaledPoint.y <= Math.max(y1, y2) ); }; const eventKey: number[] = []; const data: Datum[] = []; let count = 0; for (let index = 0, len = dataset.length; index < len; index++) { const datum = dataset[index]; if (withinBounds(datum)) { data[count] = datum; eventKey[count] = datum.eventKey === undefined ? index : datum.eventKey; count++; } } return count > 0 ? { eventKey, data } : null; } onMouseDown = (evt, targetProps) => { evt.preventDefault(); const { activateSelectedData, allowSelection, polar, selectedData } = targetProps; if (!allowSelection) { return {}; } const dimension = this.getDimension(targetProps); const parentSVG = targetProps.parentSVG || Selection.getParentSVG(evt); const { x, y } = Selection.getSVGEventCoordinates(evt, parentSVG); const x1 = polar || dimension !== "y" ? x : Selection.getDomainCoordinates(targetProps).x[0]; const y1 = polar || dimension !== "x" ? y : Selection.getDomainCoordinates(targetProps).y[0]; const x2 = polar || dimension !== "y" ? x : Selection.getDomainCoordinates(targetProps).x[1]; const y2 = polar || dimension !== "x" ? y : Selection.getDomainCoordinates(targetProps).y[1]; const mutatedProps = { x1, y1, select: true, x2, y2, parentSVG }; if (selectedData && Helpers.isFunction(targetProps.onSelectionCleared)) { targetProps.onSelectionCleared(defaults({}, mutatedProps, targetProps)); } const parentMutation = [{ target: "parent", mutation: () => mutatedProps }]; const dataMutation = selectedData && activateSelectedData ? selectedData.map((d) => { return { childName: d.childName, eventKey: d.eventKey, target: "data", mutation: () => null, }; }) : []; return parentMutation.concat(...dataMutation); }; private handleMouseMove = (evt, targetProps) => { const { allowSelection, select, polar } = targetProps; const dimension = this.getDimension(targetProps); if (!allowSelection || !select) { return null; } const parentSVG = targetProps.parentSVG || Selection.getParentSVG(evt); const { x, y } = Selection.getSVGEventCoordinates(evt, parentSVG); const x2 = polar || dimension !== "y" ? x : Selection.getDomainCoordinates(targetProps).x[1]; const y2 = polar || dimension !== "x" ? y : Selection.getDomainCoordinates(targetProps).y[1]; return { target: "parent", mutation: () => { return { x2, y2, parentSVG }; }, }; }; onMouseMove = throttle(this.handleMouseMove, ON_MOUSE_MOVE_THROTTLE_MS, { leading: true, trailing: false, }); onMouseUp = (evt, targetProps) => { const { activateSelectedData, allowSelection, x2, y2 } = targetProps; if (!allowSelection) { return null; } if (!x2 || !y2) { return [ { target: "parent", mutation: () => { return { select: false, x1: null, x2: null, y1: null, y2: null }; }, }, ]; } const datasets = this.getDatasets(targetProps); const bounds = Selection.getBounds(targetProps); const selectedData = this.filterDatasets(targetProps, datasets); const mutatedProps = { selectedData, datasets, select: false, x1: null, x2: null, y1: null, y2: null, }; const callbackMutation = selectedData && Helpers.isFunction(targetProps.onSelection) ? targetProps.onSelection( selectedData, bounds, defaults({}, mutatedProps, targetProps), ) : {}; const parentMutation = [ { target: "parent", mutation: () => mutatedProps, }, ]; const dataMutation = selectedData && activateSelectedData ? selectedData.map((d) => { return { childName: d.childName, eventKey: d.eventKey, target: "data", mutation: () => { return Object.assign({ active: true }, callbackMutation); }, }; }) : []; return parentMutation.concat(dataMutation); }; } export const SelectionHelpers = new SelectionHelpersClass(); ================================================ FILE: packages/victory-selection-container/src/victory-selection-container.tsx ================================================ import React from "react"; import { Datum, Rect, VictoryContainer, VictoryContainerProps, VictoryEventHandler, } from "victory-core"; import { SelectionHelpers } from "./selection-helpers"; export interface VictorySelectionContainerProps extends VictoryContainerProps { activateSelectedData?: boolean; allowSelection?: boolean; disable?: boolean; onSelection?: ( points: { childName?: string | string[]; eventKey?: string | number; data?: Datum[]; }[], bounds: { x: number | Date; y: number | Date; }[], props: VictorySelectionContainerProps, ) => void; horizontal?: boolean; onSelectionCleared?: (props: VictorySelectionContainerProps) => void; selectionBlacklist?: string[]; selectionComponent?: React.ReactElement; selectionDimension?: "x" | "y"; selectionStyle?: React.CSSProperties; } export const VICTORY_SELECTION_CONTAINER_DEFAULT_PROPS = { activateSelectedData: true, allowSelection: true, selectionComponent: , selectionStyle: { stroke: "transparent", fill: "black", fillOpacity: 0.1, }, }; interface VictorySelectionContainerMutatedProps extends VictorySelectionContainerProps { x1: number; x2: number; y1: number; y2: number; } export const useVictorySelectionContainer = ( initialProps: VictorySelectionContainerProps, ) => { const props = { ...VICTORY_SELECTION_CONTAINER_DEFAULT_PROPS, ...(initialProps as VictorySelectionContainerMutatedProps), }; const { x1, x2, y1, y2, selectionStyle, selectionComponent, children, name } = props; const width = Math.abs(x2 - x1) || 1; const height = Math.abs(y2 - y1) || 1; const x = Math.min(x1, x2); const y = Math.min(y1, y2); const shouldRenderRect = y1 && y2 && x1 && x2; return { props, children: [ children, shouldRenderRect && React.cloneElement(selectionComponent, { key: `${name}-selection`, x, y, width, height, style: selectionStyle, }), ] as React.ReactElement[], }; }; export const VictorySelectionContainer = ( initialProps: VictorySelectionContainerProps, ) => { const { props, children } = useVictorySelectionContainer(initialProps); return {children}; }; VictorySelectionContainer.role = "container"; VictorySelectionContainer.defaultEvents = ( initialProps: VictorySelectionContainerProps, ) => { const props = { ...VICTORY_SELECTION_CONTAINER_DEFAULT_PROPS, ...initialProps, }; const createEventHandler = (handler: VictoryEventHandler, disabled?: boolean): VictoryEventHandler => // eslint-disable-next-line max-params (event, targetProps, eventKey, context) => disabled || props.disable ? {} : handler(event, { ...props, ...targetProps }, eventKey, context); return [ { target: "parent", eventHandlers: { onMouseDown: createEventHandler(SelectionHelpers.onMouseDown), onTouchStart: createEventHandler(SelectionHelpers.onMouseDown), onMouseMove: createEventHandler(SelectionHelpers.onMouseMove), onTouchMove: createEventHandler(SelectionHelpers.onMouseMove), onMouseUp: createEventHandler(SelectionHelpers.onMouseUp), onTouchEnd: createEventHandler(SelectionHelpers.onMouseUp), }, }, ]; }; ================================================ FILE: packages/victory-selection-container/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-selection-container/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-shared-events/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-shared-events/CHANGELOG.md ================================================ # victory-shared-events ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash keys with native code ([#2811](https://github.com/FormidableLabs/victory/pull/2811)) * Replace lodash difference with native code ([#2828](https://github.com/FormidableLabs/victory/pull/2828)) - Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ### Patch Changes - Fix VictorySharedEvents defaultProps ([#2752](https://github.com/FormidableLabs/victory/pull/2752)) ## 36.8.3 ### Patch Changes - Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) * Migrate victory-shared-events to TypeScript ([#2733](https://github.com/FormidableLabs/victory/pull/2733)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-shared-events/README.md ================================================ # VictorySharedEvents `victory-shared-events@^30.0.0` exports `VictorySharedEvents` To view documentation for `VictorySharedEvents` please see https://commerce.nearform.com/open-source/victory/docs/victory-shared-events To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-shared-events.md ================================================ FILE: packages/victory-shared-events/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-shared-events/package.json ================================================ { "name": "victory-shared-events", "version": "37.3.6", "description": "Shared Event Helper for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "json-stringify-safe": "^5.0.1", "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-shared-events/src/index.ts ================================================ export * from "./victory-shared-events"; ================================================ FILE: packages/victory-shared-events/src/victory-shared-events.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import isEmpty from "lodash/isEmpty"; import fromPairs from "lodash/fromPairs"; import isEqual from "react-fast-compare"; import stringify from "json-stringify-safe"; import { Collection, EventCallbackInterface, EventMixinCalculatedValues, EventPropTypeInterface, Events, Helpers, StringOrNumberOrCallback, TimerContext, } from "victory-core"; export type VictorySharedEventsProps = { children?: React.ReactElement | React.ReactElement[]; container?: React.ReactElement; groupComponent?: React.ReactElement; events?: EventPropTypeInterface[]; eventKey?: StringOrNumberOrCallback; externalEventMutations?: EventCallbackInterface< string | string[], string | number | (string | number)[] >[]; }; // DISCLAIMER: // This file is not currently tested, and it is first on the list of files // to refactor in our current refactoring effort. Please do not make changes // to this file without manual testing and/or refactoring and adding tests. // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface VictorySharedEvents extends EventMixinCalculatedValues {} export class VictorySharedEvents extends React.Component { static displayName = "VictorySharedEvents"; static role = "shared-event-wrapper"; static contextType = TimerContext; static defaultProps = { groupComponent: , }; getScopedEvents; getEventState; baseProps; sharedEventsCache; globalEvents; prevGlobalEventKeys; boundGlobalEvents; constructor(props: VictorySharedEventsProps) { super(props); this.getScopedEvents = Events.getScopedEvents.bind(this); this.getEventState = Events.getEventState.bind(this); this.state = this.state || {}; this.sharedEventsCache = {}; this.globalEvents = {}; this.prevGlobalEventKeys = []; this.boundGlobalEvents = {}; this.baseProps = this.getBaseProps(props); } shouldComponentUpdate(nextProps) { if (!isEqual(this.props, nextProps)) { this.baseProps = this.getBaseProps(nextProps); const externalMutations = this.getExternalMutations( nextProps, this.baseProps, ); this.applyExternalMutations(nextProps, externalMutations); } return true; } componentDidMount() { const globalEventKeys = Object.keys(this.globalEvents); globalEventKeys.forEach((key) => this.addGlobalListener(key)); this.prevGlobalEventKeys = globalEventKeys; } componentDidUpdate() { const globalEventKeys = Object.keys(this.globalEvents); const removedGlobalEventKeys = Collection.difference( this.prevGlobalEventKeys, globalEventKeys, ); removedGlobalEventKeys.forEach((key) => this.removeGlobalListener(key)); const addedGlobalEventKeys = Collection.difference( globalEventKeys, this.prevGlobalEventKeys, ); addedGlobalEventKeys.forEach((key) => this.addGlobalListener(key)); this.prevGlobalEventKeys = globalEventKeys; } componentWillUnmount() { this.prevGlobalEventKeys.forEach((key) => this.removeGlobalListener(key)); } addGlobalListener(key) { const boundListener = (event) => { const listener = this.globalEvents[key]; return listener && listener(Events.emulateReactEvent(event)); }; this.boundGlobalEvents[key] = boundListener; window.addEventListener( Events.getGlobalEventNameFromKey(key), boundListener, ); } removeGlobalListener(key) { window.removeEventListener( Events.getGlobalEventNameFromKey(key), this.boundGlobalEvents[key], ); } getAllEvents(props) { const components = ["container", "groupComponent"]; const componentEvents = Events.getComponentEvents(props, components); if (Array.isArray(componentEvents)) { return Array.isArray(props.events) ? componentEvents.concat(...props.events) : componentEvents; } return props.events; } applyExternalMutations(props, externalMutations) { if (!isEmpty(externalMutations)) { const callbacks = props.externalEventMutations.reduce( (memo, mutation) => Helpers.isFunction(mutation.callback) ? memo.concat(mutation.callback) : memo, [], ); const compiledCallbacks = callbacks.length ? () => { callbacks.forEach((c) => c()); } : undefined; this.setState(externalMutations, compiledCallbacks); } } getExternalMutations(props, baseProps) { return !isEmpty(props.externalEventMutations) ? Events.getExternalMutationsWithChildren( props.externalEventMutations, baseProps, this.state, Object.keys(baseProps), ) : undefined; } cacheSharedEvents(name, sharedEvents, cacheValues) { this.sharedEventsCache[name] = [sharedEvents, cacheValues]; } getCachedSharedEvents(name, cacheValues) { const [sharedEvents, prevCacheValues] = this.sharedEventsCache[name] || []; if (sharedEvents && isEqual(cacheValues, prevCacheValues)) { return sharedEvents; } return undefined; } getBaseProps(props) { const { container } = props; const children = React.Children.toArray(this.props.children); const childBaseProps = this.getBasePropsFromChildren(children); const parentBaseProps = container ? container.props : {}; return Object.assign({}, childBaseProps, { parent: parentBaseProps }); } getBasePropsFromChildren(childComponents) { const iteratee = (child, childName) => { if (child.type && Helpers.isFunction(child.type.getBaseProps)) { const baseProps = child.props && child.type.getBaseProps(child.props); return baseProps ? [[childName, baseProps]] : null; } return null; }; const baseProps = Helpers.reduceChildren(childComponents, iteratee); return fromPairs(baseProps); } getNewChildren(props, baseProps) { const { events, eventKey } = props; const alterChildren = (children, childNames) => { return children.reduce((memo, child, index) => { if (child.props.children) { const newChildren = React.Children.toArray(child.props.children); const names = childNames.slice(index, index + newChildren.length); const results = React.cloneElement( child, child.props, alterChildren(newChildren, names), ); return memo.concat(results); } else if ( childNames[index] !== "parent" && child.type && Helpers.isFunction(child.type.getBaseProps) ) { const name = child.props.name || childNames[index]; const childEvents = Array.isArray(events) && events.filter((event) => { if (event.target === "parent") { return false; } return Array.isArray(event.childName) ? event.childName.indexOf(name) > -1 : event.childName === name || event.childName === "all"; }); const sharedEventsCacheValues = [ name, baseProps, childEvents, stringify(this.state[name]), ]; const sharedEvents = this.getCachedSharedEvents( name, sharedEventsCacheValues, ) || { events: childEvents, // partially apply child name and baseProps, getEvents: (evts, target) => this.getScopedEvents(evts, target, name, baseProps), // partially apply child name getEventState: (key, target) => this.getEventState(key, target, name), }; this.cacheSharedEvents(name, sharedEvents, sharedEventsCacheValues); return memo.concat( React.cloneElement( child, Object.assign( { key: `events-${name}`, sharedEvents, eventKey, name }, child.props, ), ), ); } return memo.concat(child); }, []); }; const childNames = Object.keys(baseProps); const childComponents = React.Children.toArray(props.children); return alterChildren(childComponents, childNames); } getContainer(props, baseProps, events) { const children = this.getNewChildren(props, baseProps); const parents = Array.isArray(events) ? events.filter((event) => event.target === "parent") : []; const sharedEvents = parents.length > 0 ? { events: parents, // partially apply childName (null) and baseProps, getEvents: (evts, target) => this.getScopedEvents(evts, target, null, baseProps), getEventState: this.getEventState, } : null; const container = props.container || props.groupComponent; const role = container.type && container.type.role; const containerProps = container.props || {}; const boundGetEvents = Events.getEvents.bind(this); const parentEvents = sharedEvents && boundGetEvents({ sharedEvents }, "parent"); const parentProps = defaults( {}, this.getEventState("parent", "parent"), containerProps, baseProps.parent, { children }, ); const containerEvents = defaults( {}, Events.getPartialEvents(parentEvents, "parent", parentProps), containerProps.events, ); this.globalEvents = Events.getGlobalEvents(containerEvents); const localEvents = Events.omitGlobalEvents(containerEvents); return role === "container" ? React.cloneElement( container, Object.assign({}, parentProps, { events: localEvents }), ) : React.cloneElement(container, localEvents, children); } render(): React.ReactElement { const events = this.getAllEvents(this.props); if (events) { return this.getContainer(this.props, this.baseProps, events); } return React.cloneElement(this.props.container as React.ReactElement, { children: this.props.children, }); } } ================================================ FILE: packages/victory-shared-events/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-shared-events/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-stack/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-stack/CHANGELOG.md ================================================ # victory-stack ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) * Improve types in victory-core helpers ([#2999](https://github.com/FormidableLabs/victory/pull/2999)) ## 37.3.2 ## 37.3.1 ### Patch Changes - Remove duplicate types from interfaces ([#2940](https://github.com/FormidableLabs/victory/pull/2940)) ## 37.3.0 ## 37.2.0 ### Minor Changes - Minor updates for clean theme ([#2909](https://github.com/FormidableLabs/victory/pull/2909)) ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ### Patch Changes - Fix bug when one child of a Stack has a date axis and all null values for the other axis ([#2888](https://github.com/FormidableLabs/victory/pull/2888)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash keys with native code ([#2811](https://github.com/FormidableLabs/victory/pull/2811)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ### Patch Changes - Assign merged props to a const instead of modifying initialProps ([#2718](https://github.com/FormidableLabs/victory/pull/2718)) ## 36.8.1 ## 36.8.0 ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - TypeScript: Added strongly-typed props to `VictoryStack` (fixes [#2449](https://github.com/FormidableLabs/victory/issues/2449)) ([#2480](https://github.com/FormidableLabs/victory/pull/2480)) - Updated dependencies []: - victory-core@36.6.8 - victory-shared-events@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 - victory-shared-events@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 - victory-shared-events@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 - victory-shared-events@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 - victory-shared-events@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 - victory-shared-events@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 - victory-shared-events@36.6.2 ## 36.6.1 ### Patch Changes - - Migrate `victory-stack` to TypeScript ([#2422](https://github.com/FormidableLabs/victory/pull/2422)) - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 - victory-shared-events@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 - victory-shared-events@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-stack/README.md ================================================ # VictoryStack `victory-stack@^30.0.0` exports `VictoryStack` To view documentation for `VictoryStack` please see https://commerce.nearform.com/open-source/victory/docs/victory-stack To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-stack.md ================================================ FILE: packages/victory-stack/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-stack/package.json ================================================ { "name": "victory-stack", "version": "37.3.6", "description": "Stack Layout Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", "victory-core": "37.3.6", "victory-shared-events": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "devDependencies": { "victory-area": "*", "victory-bar": "*", "victory-histogram": "*" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-shared-events:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs", "../victory-shared-events:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-shared-events:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-shared-events:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-shared-events:types:create", "../victory-area:types:create", "../victory-bar:types:create", "../victory-histogram:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create", "../victory-shared-events:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-shared-events:types:create", "../victory-area:types:create", "../victory-bar:types:create", "../victory-histogram:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-shared-events:types:create", "../victory-area:types:create", "../victory-bar:types:create", "../victory-histogram:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-shared-events:build", "../victory-area:build", "../victory-bar:build", "../victory-histogram:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-stack/src/helper-methods.tsx ================================================ import orderBy from "lodash/orderBy"; import React from "react"; import { Helpers, Scale, Wrapper } from "victory-core"; import isEqual from "react-fast-compare"; const fallbackProps = { width: 450, height: 300, padding: 50, }; // Assumes data in `datasets` is sorted by `Data.getData`. function fillData(props, datasets) { const { fillInMissingData } = props; const xMap = datasets.reduce((prev, dataset) => { dataset.forEach((datum) => { prev[datum._x instanceof Date ? datum._x.getTime() : datum._x] = true; }); return prev; }, {}); const xKeys = Object.keys(xMap).map((k) => Number(k)); const xArr = orderBy(xKeys); return datasets.map((dataset) => { let indexOffset = 0; const isDate = dataset[0] && dataset[0]._x instanceof Date; const filledInData = xArr.map((x: number | Date, index) => { let parsedX: number | Date = Number(x); const datum = dataset[index - indexOffset]; if (datum) { const x1 = isDate ? datum._x.getTime() : datum._x; if (x1 === parsedX) { return datum; } indexOffset++; const y = fillInMissingData ? 0 : null; parsedX = isDate ? new Date(parsedX) : parsedX; return { x: parsedX, y, _x: parsedX, _y: y }; } const y = fillInMissingData ? 0 : null; parsedX = isDate ? new Date(parsedX) : parsedX; return { x: parsedX, y, _x: parsedX, _y: y }; }); return filledInData; }); } function getY0(datum, index, datasets) { if (datum.y0) { return datum.y0; } const y = datum._y; const group = datum._group; const firstDatasetBaseline = datasets[0].map((d) => d.y0); const previousDatasets = datasets.slice(0, index); const previousPoints = previousDatasets.reduce((prev, dataset) => { return prev.concat( dataset .filter((previousDatum) => datum._x instanceof Date ? previousDatum._x.getTime() === datum._x.getTime() : previousDatum._x === datum._x, ) .map((previousDatum) => previousDatum._y || 0), ); }, []); const y0 = previousPoints.length && previousPoints.reduce((memo, value) => { const sameSign = (y < 0 && value < 0) || (y >= 0 && value >= 0); return sameSign ? Number(value) + memo : memo; }, firstDatasetBaseline[group] || 0); return previousPoints.some((point) => point instanceof Date) ? new Date(y0) : y0; } /* eslint-disable no-nested-ternary */ function addLayoutData(props, datasets, index) { const xOffset = props.xOffset || 0; return datasets[index].map((datum) => { const yOffset = getY0(datum, index, datasets) || 0; return Object.assign({}, datum, { _y0: !(datum._y instanceof Date) ? yOffset : yOffset ? new Date(yOffset) : datum._y, _y1: datum._y === null ? null : datum._y instanceof Date ? new Date(Number(datum._y) + Number(yOffset)) : datum._y + yOffset, _x1: datum._x === null ? null : datum._x instanceof Date ? new Date(Number(datum._x) + Number(xOffset)) : datum._x + xOffset, }); }); } /* eslint-enable no-nested-ternary */ function stackData(props, childComponents) { const dataFromChildren = Wrapper.getDataFromChildren(props, childComponents); const filledDatasets = fillData(props, dataFromChildren); const filteredNullChild = filledDatasets.map((dataset) => dataset.filter((datum) => datum._x !== null && datum._y !== null), ); return filteredNullChild.map((d, i) => addLayoutData(props, filledDatasets, i), ); } export function getCalculatedProps(initialProps, childComponents) { const children = childComponents || React.Children.toArray(initialProps.children); const role = "stack"; const props = Helpers.modifyProps(initialProps, fallbackProps, role); const style = Wrapper.getStyle(props.theme, props.style, role); const categories = props.categories || Wrapper.getCategories(props, children); const datasets = props.datasets || stackData(props, children); const clonedChildren = children.map((c, i) => { return React.cloneElement(c, { data: datasets[i] }); }); const domain = { x: Wrapper.getDomain( Object.assign({}, props, { categories }), "x", clonedChildren, ), y: Wrapper.getDomain( Object.assign({}, props, { categories }), "y", clonedChildren, ), }; const range = props.range || { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; const baseScale = { x: Scale.getScaleFromProps(props, "x") || Wrapper.getScale(props, "x"), y: Scale.getScaleFromProps(props, "y") || Wrapper.getScale(props, "y"), }; const scale = { x: baseScale.x.domain(domain.x).range(props.horizontal ? range.y : range.x), y: baseScale.y.domain(domain.y).range(props.horizontal ? range.x : range.y), }; const { colorScale, horizontal } = props; return { datasets, categories, range, domain, horizontal, scale, style, colorScale, role, }; } // We need to remove sharedEvents in order to memoize the calculated data // With shared events, the props change on every event, and every value is re-calculated const withoutSharedEvents = (props) => { const { children } = props; const modifiedChildren = React.Children.toArray(children).map((_child) => { const child = _child as React.ReactElement; return { ...child, props: Helpers.omit(child.props, ["sharedEvents"]), }; }); props.children = modifiedChildren; return props; }; export function useMemoizedProps(initialProps) { const modifiedProps = withoutSharedEvents(initialProps); const [props, setProps] = React.useState(modifiedProps); // React.memo uses shallow equality to compare objects. This way props // will only be re-calculated when they change. React.useEffect(() => { if (!isEqual(modifiedProps, props)) { setProps(modifiedProps); } }, [props, setProps, modifiedProps]); return React.useMemo(() => { return getCalculatedProps(props, props.children); }, [props]); } function getLabels(props, datasets, index) { if (!props.labels) { return undefined; } return datasets.length === index + 1 ? props.labels : undefined; } export function getChildProps(props, calculatedProps) { const { categories, domain, range, scale, horizontal } = calculatedProps; return { height: props.height, width: props.width, padding: Helpers.getPadding(props.padding), standalone: false, theme: props.theme, categories, domain, range, scale, horizontal, }; } function getColorScale(props, child) { const role = child.type && child.type.role; const colorScaleOptions = child.props.colorScale || props.colorScale; if (role !== "group" && role !== "stack") { return undefined; } return props.theme ? colorScaleOptions || props.theme.props.colorScale : colorScaleOptions; } export function getChildren(initialProps, childComponents, calculatedProps) { const props = Helpers.modifyProps(initialProps, fallbackProps, "stack"); const children = childComponents || React.Children.toArray(props.children); const newCalculatedProps = calculatedProps || getCalculatedProps(props, children); const { datasets } = newCalculatedProps; const childProps = getChildProps(props, newCalculatedProps); const parentName = props.name || "stack"; const { theme } = props; return children.map((child, index) => { const role = child.type && child.type.role; const data = datasets[index]; const style = Wrapper.getChildStyle( child, index, newCalculatedProps, theme, ); const labels = props.labels ? getLabels(props, datasets, index) : child.props.labels; const name = child.props.name || `${parentName}-${role}-${index}`; return React.cloneElement( child, Object.assign( { key: `${name}-key-${index}`, labels, name, domainPadding: child.props.domainPadding || props.domainPadding, theme: props.theme, labelComponent: props.labelComponent || child.props.labelComponent, style, colorScale: getColorScale(props, child), data, polar: props.polar, }, childProps, ), ); }); } ================================================ FILE: packages/victory-stack/src/index.ts ================================================ export * from "./victory-stack"; ================================================ FILE: packages/victory-stack/src/victory-stack.test.tsx ================================================ /* eslint-disable no-console */ import { render } from "@testing-library/react"; import React from "react"; import { VictoryBar } from "victory-bar"; import { VictoryHistogram } from "victory-histogram"; import { VictoryArea } from "victory-area"; import { VictoryStack } from "./victory-stack"; describe("components/victory-stack", () => { describe("default component rendering", () => { it("renders an svg with the correct width and height", () => { const { container } = render( , ); const svg = container.querySelector("svg")!; expect(svg.style.width).toEqual("100%"); expect(svg.style.height).toEqual("100%"); }); it("renders an svg with the correct viewBox", () => { const { container } = render( , ); const svg = container.querySelector("svg")!; const viewBoxValue = `0 0 ${450} ${300}`; expect(svg.getAttribute("viewBox")).toEqual(viewBoxValue); }); it("accepts user props", () => { const { container } = render( , ); const svgNode = container.querySelector("svg")!; expect(svgNode.getAttribute("data-testid")).toEqual("victory-stack"); expect(svgNode.getAttribute("aria-label")).toEqual("Stack"); }); }); describe("children data", () => { it("should be able to handle all null values when using dates", () => { const { container } = render( , ); const svgNode = container.querySelector("svg")!; expect(svgNode.getAttribute("data-testid")).toEqual("victory-stack"); expect(svgNode.getAttribute("aria-label")).toEqual("Stack"); }); }); describe("warnings", () => { beforeEach(() => { jest.spyOn(console, "warn").mockImplementation(() => {}); }); afterEach(() => { jest.clearAllMocks(); }); it("should warn when histogram children are mixed with non-histogram children", () => { render( , ); expect(console.warn).toHaveBeenCalledWith( "VictoryHistogram only supports being stacked with other VictoryHistogram components. Check to make sure that you are only passing VictoryHistogram components to VictoryStack", ); }); it("should not warn when only histogram children are passed", () => { render( , ); expect(console.warn).not.toHaveBeenCalled(); }); }); }); ================================================ FILE: packages/victory-stack/src/victory-stack.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import isEmpty from "lodash/isEmpty"; import { CategoryPropType, DomainPropType, EventPropTypeInterface, StringOrNumberOrCallback, VictoryCommonProps, VictoryMultiLabelableProps, VictoryStyleInterface, Helpers, Hooks, UserProps, VictoryComponentConfiguration, VictoryContainer, VictoryTheme, Wrapper, } from "victory-core"; import { VictorySharedEvents } from "victory-shared-events"; import { getChildren, useMemoizedProps } from "./helper-methods"; import isEqual from "react-fast-compare"; const fallbackProps = { width: 450, height: 300, padding: 50, }; export type VictoryStackTTargetType = "data" | "labels" | "parent"; export interface VictoryStackProps extends VictoryCommonProps, VictoryMultiLabelableProps { bins?: number | number[] | Date[]; categories?: CategoryPropType; children?: React.ReactNode | React.ReactNode[]; domain?: DomainPropType; events?: EventPropTypeInterface< VictoryStackTTargetType, StringOrNumberOrCallback >[]; eventKey?: StringOrNumberOrCallback; fillInMissingData?: boolean; style?: VictoryStyleInterface; xOffset?: number; } const defaultProps = { containerComponent: , groupComponent: , standalone: true, theme: VictoryTheme.grayscale, fillInMissingData: true, }; const VictoryStackBase = (initialProps: VictoryStackProps) => { const { role } = VictoryStack; const propsWithDefaults = React.useMemo( () => defaults({}, initialProps, defaultProps), [initialProps], ); const { setAnimationState, getAnimationProps, getProps } = Hooks.useAnimationState(); const props = getProps(propsWithDefaults); const modifiedProps = Helpers.modifyProps(props, fallbackProps, role); const { eventKey, containerComponent, standalone, groupComponent, externalEventMutations, width, height, theme, polar, horizontal, name, } = modifiedProps; const childComponents = React.Children.toArray(modifiedProps.children); const calculatedProps = useMemoizedProps(modifiedProps); const { domain, scale, style } = calculatedProps; const newChildren = React.useMemo(() => { const children = getChildren(props, childComponents, calculatedProps); const orderedChildren = children.map((child, index) => { const childProps = Object.assign( { animate: getAnimationProps(props, child, index) }, child.props, ); return React.cloneElement(child, childProps); }); /* reverse render order for children of `VictoryStack` so that higher children in the stack are rendered behind lower children. This looks nicer for stacked bars with cornerRadius, and areas with strokes */ return orderedChildren.reverse(); }, [props, childComponents, calculatedProps, getAnimationProps]); const containerProps = React.useMemo(() => { if (standalone) { return { domain, scale, width, height, standalone, theme, style: style.parent, horizontal, polar, name, }; } return {}; }, [ standalone, domain, scale, width, height, theme, style, horizontal, polar, name, ]); const userProps = React.useMemo( () => UserProps.getSafeUserProps(propsWithDefaults), [propsWithDefaults], ); const container = React.useMemo(() => { if (standalone) { const defaultContainerProps = defaults( {}, containerComponent.props, containerProps, userProps, ); return React.cloneElement(containerComponent, defaultContainerProps); } return React.cloneElement(groupComponent, userProps); }, [ groupComponent, standalone, containerComponent, containerProps, userProps, ]); const events = React.useMemo(() => { return Wrapper.getAllEvents(props); }, [props]); const previousProps = Hooks.usePreviousProps(propsWithDefaults); React.useEffect(() => { // This is called before dismount to keep state in sync return () => { if (propsWithDefaults.animate) { setAnimationState(previousProps, propsWithDefaults); } }; }, [setAnimationState, previousProps, propsWithDefaults]); if (!isEmpty(events)) { return ( {newChildren} ); } return React.cloneElement(container, container.props, newChildren); }; const componentConfig: VictoryComponentConfiguration = { role: "stack", expectedComponents: [ "groupComponent", "containerComponent", "labelComponent", ], getChildren, }; export const VictoryStack = Object.assign( React.memo(VictoryStackBase, isEqual), componentConfig, ); VictoryStack.displayName = "VictoryStack"; ================================================ FILE: packages/victory-stack/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-stack/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-tooltip/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-tooltip/CHANGELOG.md ================================================ # victory-tooltip ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) * Improve types in victory-core helpers ([#2999](https://github.com/FormidableLabs/victory/pull/2999)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Refactor containers and portal to function components ([#2799](https://github.com/FormidableLabs/victory/pull/2799)) * Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ### Patch Changes - Migrate victory-native to TypeScript ([#2739](https://github.com/FormidableLabs/victory/pull/2739)) ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Migrate victory-tooltip to typescript ([#2725](https://github.com/FormidableLabs/victory/pull/2725)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-tooltip/README.md ================================================ # VictoryTooltip `victory-tooltip@^30.0.0` exports `VictoryTooltip` and `Flyout` components To view documentation for `VictoryTooltip` please see https://commerce.nearform.com/open-source/victory/docs/victory-tooltip To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-tooltip.md ================================================ FILE: packages/victory-tooltip/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-tooltip/package.json ================================================ { "name": "victory-tooltip", "version": "37.3.6", "description": "Tooltip Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-tooltip/src/flyout.test.tsx ================================================ import React from "react"; import { render } from "@testing-library/react"; import { SVGWrapper } from "../../../test/helpers"; import { Flyout } from "./flyout"; describe("victory-primitives/flyout", () => { const baseProps = { x: 100, y: 100, dx: 0, dy: 0, width: 50, height: 50, cornerRadius: 5, pointerLength: 10, pointerWidth: 10, }; describe("rendering", () => { it("renders a flyout path", () => { const { container } = render(, { wrapper: SVGWrapper, }); const path = container.querySelector("path"); // Make sure the path is rendered: expect(path).toMatchInlineSnapshot(` `); }); }); }); ================================================ FILE: packages/victory-tooltip/src/flyout.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import { Helpers, Path, UserProps, VictoryCommonProps, OrientationTypes, VictoryStyleObject, StringOrNumberOrCallback, } from "victory-core"; export interface FlyoutProps extends VictoryCommonProps { active?: boolean; center?: { x: number; y: number; }; className?: string; clipPath?: string; cornerRadius?: number; data?: any[]; datum?: object; dx?: number; dy?: number; events?: object; id?: StringOrNumberOrCallback; index?: number; orientation?: OrientationTypes; pathComponent?: React.ReactElement; pointerLength?: number; pointerWidth?: number; role?: string; shapeRendering?: string; style?: VictoryStyleObject; transform?: string; x?: number; y?: number; } interface FlyoutPathProps { center: { x: number; y: number; }; cornerRadius: number; dx?: number; dy?: number; height: number; orientation: OrientationTypes; pointerLength: number; pointerWidth: number; width: number; x: number; y: number; } const getVerticalPath = (props: FlyoutPathProps) => { const { pointerWidth, cornerRadius, orientation, width, height, center } = props; const sign = orientation === "bottom" ? 1 : -1; const x = props.x + (props.dx || 0); const y = props.y + (props.dy || 0); const centerX = center.x; const centerY = center.y; const pointerEdge = centerY + sign * (height / 2); const oppositeEdge = centerY - sign * (height / 2); const rightEdge = centerX + width / 2; const leftEdge = centerX - width / 2; const pointerLength = sign * (y - pointerEdge) < 0 ? 0 : props.pointerLength; const direction = orientation === "bottom" ? "0 0 0" : "0 0 1"; const arc = `${cornerRadius} ${cornerRadius} ${direction}`; return `M ${centerX - pointerWidth / 2}, ${pointerEdge} L ${pointerLength ? x : centerX + pointerWidth / 2}, ${ pointerLength ? y : pointerEdge } L ${centerX + pointerWidth / 2}, ${pointerEdge} L ${rightEdge - cornerRadius}, ${pointerEdge} A ${arc} ${rightEdge}, ${pointerEdge - sign * cornerRadius} L ${rightEdge}, ${oppositeEdge + sign * cornerRadius} A ${arc} ${rightEdge - cornerRadius}, ${oppositeEdge} L ${leftEdge + cornerRadius}, ${oppositeEdge} A ${arc} ${leftEdge}, ${oppositeEdge + sign * cornerRadius} L ${leftEdge}, ${pointerEdge - sign * cornerRadius} A ${arc} ${leftEdge + cornerRadius}, ${pointerEdge} z`; }; const getHorizontalPath = (props: FlyoutPathProps) => { const { pointerWidth, cornerRadius, orientation, width, height, center } = props; const sign = orientation === "left" ? 1 : -1; const x = props.x + (props.dx || 0); const y = props.y + (props.dy || 0); const centerX = center.x; const centerY = center.y; const pointerEdge = centerX - sign * (width / 2); const oppositeEdge = centerX + sign * (width / 2); const bottomEdge = centerY + height / 2; const topEdge = centerY - height / 2; const pointerLength = sign * (x - pointerEdge) > 0 ? 0 : props.pointerLength; const direction = orientation === "left" ? "0 0 0" : "0 0 1"; const arc = `${cornerRadius} ${cornerRadius} ${direction}`; return `M ${pointerEdge}, ${centerY - pointerWidth / 2} L ${pointerLength ? x : pointerEdge}, ${ pointerLength ? y : centerY + pointerWidth / 2 } L ${pointerEdge}, ${centerY + pointerWidth / 2} L ${pointerEdge}, ${bottomEdge - cornerRadius} A ${arc} ${pointerEdge + sign * cornerRadius}, ${bottomEdge} L ${oppositeEdge - sign * cornerRadius}, ${bottomEdge} A ${arc} ${oppositeEdge}, ${bottomEdge - cornerRadius} L ${oppositeEdge}, ${topEdge + cornerRadius} A ${arc} ${oppositeEdge - sign * cornerRadius}, ${topEdge} L ${pointerEdge + sign * cornerRadius}, ${topEdge} A ${arc} ${pointerEdge}, ${topEdge + cornerRadius} z`; }; const getFlyoutPath = (props: FlyoutPathProps) => { const orientation = props.orientation || "top"; return orientation === "left" || orientation === "right" ? getHorizontalPath(props) : getVerticalPath(props); }; const evaluateProps = (props: FlyoutProps) => { /** * Potential evaluated props are: * `id` * `style` */ const id = Helpers.evaluateProp(props.id, props); const style = Helpers.evaluateStyle(props.style, props); return { ...props, id, style }; }; const defaultProps = { pathComponent: , role: "presentation", shapeRendering: "auto", }; export const Flyout: React.FC = (initialProps) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); const userProps = UserProps.getSafeUserProps(props); // check for required props for this subcomponent // they should be passed in from the wrapper UserProps.assert(props.height, "Flyout props[height] is undefined"); UserProps.assert(props.width, "Flyout props[width] is undefined"); UserProps.assert(props.x, "Flyout props[x] is undefined"); UserProps.assert(props.y, "Flyout props[y] is undefined"); const flyoutPathProps: FlyoutPathProps = { center: props.center || { x: 0, y: 0 }, cornerRadius: props.cornerRadius || 0, dx: props.dx, dy: props.dy, height: props.height, orientation: props.orientation || "top", pointerLength: props.pointerLength || 0, pointerWidth: props.pointerWidth || 0, width: props.width, x: props.x, y: props.y, }; return React.cloneElement(props.pathComponent!, { ...props.events, ...userProps, style: props.style, d: getFlyoutPath(flyoutPathProps), className: props.className, shapeRendering: props.shapeRendering, role: props.role, transform: props.transform, clipPath: props.clipPath, }); }; ================================================ FILE: packages/victory-tooltip/src/index.ts ================================================ export * from "./victory-tooltip"; export * from "./flyout"; ================================================ FILE: packages/victory-tooltip/src/victory-tooltip.test.tsx ================================================ import React from "react"; import { fireEvent, screen, render } from "@testing-library/react"; import { VictoryContainer, VictoryLabel } from "victory-core"; import { Flyout } from "./flyout"; import { VictoryTooltip } from "./victory-tooltip"; describe("components/victory-tooltip", () => { const flyoutId = "flyout-1"; const labelId = "label-1"; const baseProps = { x: 0, y: 0, datum: { some: "object" }, index: 3, active: true, text: "such text, wow", flyoutComponent: , labelComponent: , }; it("renders nothing when not active", () => { render(, { wrapper: VictoryContainer as React.JSXElementConstructor<{ children: React.ReactNode; }>, }); const output = screen.queryByTestId(labelId); expect(output).not.toBeInTheDocument(); }); it("renders the expected text", () => { render(, { wrapper: VictoryContainer as React.JSXElementConstructor<{ children: React.ReactNode; }>, }); const output = screen.getByTestId(labelId); expect(output).toBeInTheDocument(); expect(output).toBeVisible(); expect(output).toHaveTextContent(baseProps.text); }); it("renders a flyout and a label", () => { render(, { wrapper: VictoryContainer as React.JSXElementConstructor<{ children: React.ReactNode; }>, }); const label = screen.getByTestId(labelId); const flyout = screen.getByTestId(flyoutId); expect(label).toBeInTheDocument(); expect(flyout).toBeInTheDocument(); }); describe("event handling", () => { it("attaches an to the flyout object", () => { const clickHandler = jest.fn(); render( , { wrapper: VictoryContainer as React.JSXElementConstructor<{ children: React.ReactNode; }>, }, ); fireEvent.click(screen.getByTestId(flyoutId)); expect(clickHandler).toBeCalled(); }); }); }); ================================================ FILE: packages/victory-tooltip/src/victory-tooltip.tsx ================================================ import React from "react"; import { TextSize, Helpers, LabelHelpers, VictoryLabel, VictoryTheme, VictoryPortal, VictoryLabelableProps, VictoryLabelProps, VictoryLabelStyleObject, NumberOrCallback, PaddingOrCallback, VictoryStyleObject, OrientationTypes, VictoryThemeDefinition, } from "victory-core"; import defaults from "lodash/defaults"; import uniqueId from "lodash/uniqueId"; import isPlainObject from "lodash/isPlainObject"; import orderBy from "lodash/orderBy"; import { Flyout } from "./flyout"; const fallbackProps = { cornerRadius: 5, pointerLength: 10, pointerWidth: 10, }; export interface VictoryTooltipProps extends VictoryLabelableProps, VictoryLabelProps { activateData?: boolean; active?: boolean; activePoints?: any[]; angle?: number; center?: { x?: number; y?: number }; centerOffset?: { x?: NumberOrCallback; y?: NumberOrCallback; }; constrainToVisibleArea?: boolean; cornerRadius?: NumberOrCallback; events?: any; height?: number; horizontal?: boolean; flyoutComponent?: React.ReactElement; flyoutHeight?: NumberOrCallback; flyoutPadding?: PaddingOrCallback; flyoutStyle?: VictoryStyleObject; flyoutWidth?: NumberOrCallback; id?: number | string; index?: number | string; orientation?: OrientationTypes | ((...args: any[]) => OrientationTypes); pointerLength?: NumberOrCallback; pointerOrientation?: | OrientationTypes | ((...args: any[]) => OrientationTypes); pointerWidth?: NumberOrCallback; style?: | (VictoryLabelStyleObject & { angle?: number; }) | VictoryLabelStyleObject[]; theme?: VictoryThemeDefinition; width?: number; } type InternalEvaluatedProps = VictoryTooltipProps & { centerOffset: { x: number; y: number }; cornerRadius?: number; dx?: string | number; dy?: string | number; flyoutHeight: number; flyoutPadding: { top: number; bottom: number; left: number; right: number; }; flyoutWidth: number; orientation: OrientationTypes; pointerLength?: number; pointerWidth?: number; // TODO: This is a hack to get around the fact that the type of // style is used in akward ways in the functions style: any; text: string | string[]; }; type EventHandlers = Record< string, (props?: any) => { target: string; mutation: () => { active?: boolean | undefined }; }[] >; export class VictoryTooltip extends React.Component { static displayName = "VictoryTooltip"; static role = "tooltip"; static defaultProps = { active: false, renderInPortal: true, labelComponent: , flyoutComponent: , groupComponent: , }; static defaultEvents(props: VictoryTooltipProps): { target: string; eventHandlers: EventHandlers; }[] { const activate = props.activateData ? [ { target: "labels", mutation: () => ({ active: true }) }, { target: "data", mutation: () => ({ active: true }) }, ] : [{ target: "labels", mutation: () => ({ active: true }) }]; const deactivate = props.activateData ? [ { target: "labels", mutation: () => ({ active: undefined }) }, { target: "data", mutation: () => ({ active: undefined }) }, ] : [{ target: "labels", mutation: () => ({ active: undefined }) }]; return [ { target: "data", eventHandlers: { onMouseOver: () => activate, onFocus: () => activate, onTouchStart: () => activate, onMouseOut: () => deactivate, onBlur: () => deactivate, onTouchEnd: () => deactivate, }, }, ]; } id: string | number; constructor(props: VictoryTooltipProps) { super(props); this.id = props.id === undefined ? uniqueId("tooltip-") : props.id; } getDefaultOrientation(props: VictoryTooltipProps): OrientationTypes { const { datum, horizontal, polar } = props; if (!polar) { const positive = horizontal ? "right" : "top"; const negative = horizontal ? "left" : "bottom"; return datum && datum.y < 0 ? negative : positive; } return this.getPolarOrientation(props); } getPolarOrientation(props: VictoryTooltipProps): OrientationTypes { const degrees = LabelHelpers.getDegrees(props, props.datum); const placement = props.labelPlacement || "vertical"; if (placement === "vertical") { return this.getVerticalOrientations(degrees); } else if (placement === "parallel") { return degrees < 90 || degrees > 270 ? "right" : "left"; } return degrees > 180 ? "bottom" : "top"; } getVerticalOrientations(degrees: number): OrientationTypes { // eslint-disable-next-line no-magic-numbers if (degrees < 45 || degrees > 315) { return "right"; // eslint-disable-next-line no-magic-numbers } else if (degrees >= 45 && degrees <= 135) { return "top"; // eslint-disable-next-line no-magic-numbers } else if (degrees > 135 && degrees < 225) { return "left"; } return "bottom"; } getStyles(props) { const theme = props.theme || VictoryTheme.grayscale; const defaultLabelStyles = theme && theme.tooltip && theme.tooltip.style ? theme.tooltip.style : {}; const baseLabelStyle = Array.isArray(props.style) ? props.style.map((s) => defaults({}, s, defaultLabelStyles)) : defaults({}, props.style, defaultLabelStyles); const defaultFlyoutStyles = theme && theme.tooltip && theme.tooltip.flyoutStyle ? theme.tooltip.flyoutStyle : {}; const baseFlyoutStyle = props.flyoutStyle ? defaults({}, props.flyoutStyle, defaultFlyoutStyles) : defaultFlyoutStyles; const style = Array.isArray(baseLabelStyle) ? baseLabelStyle.map((s) => Helpers.evaluateStyle(s, props)) : Helpers.evaluateStyle(baseLabelStyle, props); const flyoutStyle = Helpers.evaluateStyle( baseFlyoutStyle, Object.assign({}, props, { style }), ); return { style, flyoutStyle }; } getEvaluatedProps(props: VictoryTooltipProps): InternalEvaluatedProps { const { cornerRadius, centerOffset, dx, dy } = props; const active = Helpers.evaluateProp(props.active, props); let text = Helpers.evaluateProp( props.text, Object.assign({}, props, { active }), ); if (text === undefined || text === null) { text = ""; } if (typeof text === "number") { text = text.toString(); } const { style, flyoutStyle } = this.getStyles( Object.assign({}, props, { active, text }), ); const orientation = Helpers.evaluateProp( props.orientation, Object.assign({}, props, { active, text, style, flyoutStyle }), ) || this.getDefaultOrientation(props); const padding = Helpers.evaluateProp( props.flyoutPadding, Object.assign({}, props, { active, text, style, flyoutStyle, orientation, }), ) || this.getLabelPadding(style); const flyoutPadding = Helpers.getPadding(padding); const pointerWidth = Helpers.evaluateProp( props.pointerWidth, Object.assign({}, props, { active, text, style, flyoutStyle, orientation, }), ); const pointerLength = Helpers.evaluateProp( props.pointerLength, Object.assign({}, props, { active, text, style, flyoutStyle, orientation, }), ); const labelSize = TextSize.approximateTextSize(text, style); const { flyoutHeight, flyoutWidth } = this.getDimensions( Object.assign({}, props, { style, flyoutStyle, active, text, orientation, flyoutPadding, pointerWidth, pointerLength, }), labelSize, ); const evaluatedProps = Object.assign({}, props, { active, text, style, flyoutStyle, orientation, flyoutHeight, flyoutWidth, flyoutPadding, pointerWidth, pointerLength, }); const offsetX = isPlainObject(centerOffset) && centerOffset?.x !== undefined ? Helpers.evaluateProp(centerOffset.x, evaluatedProps) : 0; const offsetY = isPlainObject(centerOffset) && centerOffset?.y !== undefined ? Helpers.evaluateProp(centerOffset.y, evaluatedProps) : 0; return { ...evaluatedProps, centerOffset: { x: offsetX, y: offsetY }, dx: dx !== undefined ? Helpers.evaluateProp(dx, evaluatedProps) : 0, dy: dy !== undefined ? Helpers.evaluateProp(dy, evaluatedProps) : 0, cornerRadius: Helpers.evaluateProp(cornerRadius, evaluatedProps), }; } getCalculatedValues(props: InternalEvaluatedProps): { style: any; flyoutStyle?: VictoryStyleObject; labelSize: { width: number; height: number }; flyoutDimensions: { height: number; width: number }; flyoutCenter: { x: number; y: number }; transform?: string; } { const { style, text, flyoutStyle, flyoutHeight, flyoutWidth } = props; const labelSize = TextSize.approximateTextSize(text, style); const flyoutDimensions = { height: flyoutHeight, width: flyoutWidth }; const flyoutCenter = this.getFlyoutCenter(props, flyoutDimensions); const transform = this.getTransform(props); return { style, flyoutStyle, labelSize, flyoutDimensions, flyoutCenter, transform, }; } getTransform(props): string | undefined { const { x, y, style } = props; const labelStyle = style || {}; const angle = labelStyle.angle || props.angle || this.getDefaultAngle(props); return angle ? `rotate(${angle} ${x} ${y})` : undefined; } getDefaultAngle(props): number { const { polar, labelPlacement, orientation, datum } = props; if (!polar || !labelPlacement || labelPlacement === "vertical") { return 0; } const degrees = LabelHelpers.getDegrees(props, datum); const sign = (degrees > 90 && degrees < 180) || degrees > 270 ? 1 : -1; const labelRotation = labelPlacement === "perpendicular" ? 0 : 90; let angle = 0; if (degrees === 0 || degrees === 180) { angle = orientation === "top" && degrees === 180 ? 270 : 90; } else if (degrees > 0 && degrees < 180) { angle = 90 - degrees; } else if (degrees > 180 && degrees < 360) { angle = 270 - degrees; } return angle + sign * labelRotation; } constrainTooltip(center, props, dimensions) { const { x, y } = center; const { width, height } = dimensions; const extent = { x: [0, props.width], y: [0, props.height], }; const flyoutExtent = { x: [x - width / 2, x + width / 2], y: [y - height / 2, y + height / 2], }; const adjustments = { x: [ flyoutExtent.x[0] < extent.x[0] ? extent.x[0] - flyoutExtent.x[0] : 0, flyoutExtent.x[1] > extent.x[1] ? flyoutExtent.x[1] - extent.x[1] : 0, ], y: [ flyoutExtent.y[0] < extent.y[0] ? extent.y[0] - flyoutExtent.y[0] : 0, flyoutExtent.y[1] > extent.y[1] ? flyoutExtent.y[1] - extent.y[1] : 0, ], }; return { x: Math.round(x + adjustments.x[0] - adjustments.x[1]), y: Math.round(y + adjustments.y[0] - adjustments.y[1]), }; } getFlyoutCenter(props, dimensions) { const { x, y, dx, dy, pointerLength, orientation, constrainToVisibleArea, centerOffset, } = props; const { height, width } = dimensions; const xSign = orientation === "left" ? -1 : 1; const ySign = orientation === "bottom" ? -1 : 1; const flyoutCenter = { x: orientation === "left" || orientation === "right" ? x + xSign * (pointerLength + width / 2 + xSign * dx) : x + dx, y: orientation === "top" || orientation === "bottom" ? y - ySign * (pointerLength + height / 2 - ySign * dy) : y + dy, }; const center = { x: isPlainObject(props.center) && props.center.x !== undefined ? props.center.x : flyoutCenter.x, y: isPlainObject(props.center) && props.center.y !== undefined ? props.center.y : flyoutCenter.y, }; const centerWithOffset = { x: center.x + centerOffset.x, y: center.y + centerOffset.y, }; return constrainToVisibleArea ? this.constrainTooltip(centerWithOffset, props, dimensions) : centerWithOffset; } getLabelPadding(style) { if (!style) { return 0; } const paddings = Array.isArray(style) ? style.map((s) => s.padding) : [style.padding]; return Math.max(...paddings, 0); } getDimensions(props, labelSize) { const { orientation, pointerLength, pointerWidth, flyoutHeight, flyoutWidth, flyoutPadding, } = props; const cornerRadius = Helpers.evaluateProp(props.cornerRadius, props); const getHeight = () => { const calculatedHeight = labelSize.height + flyoutPadding.top + flyoutPadding.bottom; const minHeight = orientation === "top" || orientation === "bottom" ? 2 * cornerRadius : 2 * cornerRadius + pointerWidth; return Math.max(minHeight, calculatedHeight); }; const getWidth = () => { const calculatedWidth = labelSize.width + flyoutPadding.left + flyoutPadding.right; const minWidth = orientation === "left" || orientation === "right" ? 2 * cornerRadius + pointerLength : 2 * cornerRadius; return Math.max(minWidth, calculatedWidth); }; return { flyoutHeight: flyoutHeight ? Helpers.evaluateProp(flyoutHeight, props) : getHeight(), flyoutWidth: flyoutWidth ? Helpers.evaluateProp(flyoutWidth, props) : getWidth(), }; } getLabelProps(props: InternalEvaluatedProps, calculatedValues) { const { flyoutCenter, style, labelSize, dy = 0, dx = 0 } = calculatedValues; const { text, datum, activePoints, labelComponent, index, flyoutPadding } = props; const textAnchor = (Array.isArray(style) && style.length ? style[0].textAnchor : style.textAnchor) || "middle"; const getLabelX = () => { if (!textAnchor || textAnchor === "middle") { return flyoutCenter.x; } const sign = textAnchor === "end" ? -1 : 1; return flyoutCenter.x - sign * (labelSize.width / 2); }; return defaults({}, labelComponent!.props, { key: `${this.id}-label-${index}`, text, datum, activePoints, textAnchor, dy, dx, style, x: getLabelX() + (flyoutPadding.left - flyoutPadding.right) / 2, y: flyoutCenter.y + (flyoutPadding.top - flyoutPadding.bottom) / 2, verticalAnchor: "middle", angle: style.angle, }); } getPointerOrientation( point: { x: number; y: number }, center: { x: number; y: number }, flyoutDimensions: { height: number; width: number }, ): string { const edges = { bottom: center.y + flyoutDimensions.height / 2, top: center.y - flyoutDimensions.height / 2, left: center.x - flyoutDimensions.width / 2, right: center.x + flyoutDimensions.width / 2, }; const gaps = [ { side: "top", val: edges.top > point.y ? edges.top - point.y : -1 }, { side: "bottom", val: edges.bottom < point.y ? point.y - edges.bottom : -1, }, { side: "right", val: edges.right < point.x ? point.x - edges.right : -1, }, { side: "left", val: edges.left > point.x ? edges.left - point.x : -1 }, ]; return orderBy(gaps, "val", "desc")[0].side; } getFlyoutProps(props: InternalEvaluatedProps, calculatedValues) { const { flyoutDimensions, flyoutStyle, flyoutCenter } = calculatedValues; const { x, y, dx, dy, datum, activePoints, index, pointerLength, pointerWidth, cornerRadius, events, flyoutComponent, } = props; const pointerOrientation = Helpers.evaluateProp( props.pointerOrientation, props, ); return defaults({}, flyoutComponent!.props, { x, y, dx, dy, datum, activePoints, index, pointerLength, pointerWidth, cornerRadius, events, orientation: pointerOrientation || this.getPointerOrientation( { x: x!, y: y! }, flyoutCenter, flyoutDimensions, ), key: `${this.id}-tooltip-${index}`, width: flyoutDimensions.width, height: flyoutDimensions.height, style: flyoutStyle, center: flyoutCenter, }); } // Overridden in victory-core-native renderTooltip(props: VictoryTooltipProps): React.ReactElement | null { const active = Helpers.evaluateProp(props.active, props); const { renderInPortal } = props; if (!active) { return null; } const evaluatedProps = this.getEvaluatedProps(props); const { flyoutComponent, labelComponent, groupComponent } = evaluatedProps; const calculatedValues = this.getCalculatedValues(evaluatedProps); const children = [ React.cloneElement( flyoutComponent!, this.getFlyoutProps(evaluatedProps, calculatedValues), ), React.cloneElement( labelComponent!, this.getLabelProps(evaluatedProps, calculatedValues), ), ]; const tooltip = React.cloneElement( groupComponent!, { role: "presentation", transform: calculatedValues.transform }, children, ); return renderInPortal ? {tooltip} : tooltip; } render(): React.ReactElement | null { const props = Helpers.modifyProps(this.props, fallbackProps, "tooltip"); return this.renderTooltip(props); } } ================================================ FILE: packages/victory-tooltip/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-tooltip/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-vendor/.babelrc.js ================================================ /** * Transform d3 ESM libraries to vendored CommonJS libraries * * This produces `lib-vendor/d3-/src` files that have * internally consistent references to other d3 packages. It is only meant * to be used for the CommonJS import path. */ const path = require("path"); module.exports = { only: ["node_modules/*/src/**/*.js"], plugins: [ [ "@babel/transform-modules-commonjs", { strict: false, allowTopLevelThis: true, }, ], [ "module-resolver", { // Convert all imports for _other_ d3 dependencies to the relative // path in our vendor package. resolvePath(sourcePath, currentFile) { const d3pattern = /^(?(d3-[^\/]+|internmap))(?.*)/; const match = d3pattern.exec(sourcePath); if (match) { // We're assuming a common shape of d3 packages: // - Only top level imports "d3-" // - With no path components (like "d3-/path/to.js") if (match.groups.path) { throw new Error( `Unable to process ${sourcePath} import in ${currentFile}`, ); } // Get Vendor package path. const vendorPkg = `lib-vendor/${match.groups.pkg}/src/index.js`; // Derive relative path to vendor lib to have a file like move from: // - 'node_modules/d3-interpolate/src/rgb.js' // - 'lib-vendor/d3-interpolate/src/rgb.js' // and have an import transform like: // - `d3-color` // - `../../d3-color` const currentFileVendor = currentFile.replace( /^node_modules/, "lib-vendor", ); const relPathToPkg = path .relative(path.dirname(currentFileVendor), vendorPkg) .replace(/\\/g, "/"); return relPathToPkg; } return sourcePath; }, }, ], ], }; ================================================ FILE: packages/victory-vendor/.gitignore ================================================ /lib-vendor /d3-* /internmap.js ================================================ FILE: packages/victory-vendor/.npmignore ================================================ /* !/dist !/es !/lib !/lib-vendor !/src !/d3-* !/internmap.js !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-vendor/CHANGELOG.md ================================================ # victory-vendor ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ## 37.0.2 ## 37.0.1 ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ## 36.9.1 ## 36.9.0 ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ## 36.6.7 ## 36.6.6 ## 36.6.5 ### Patch Changes - Export types directly from d3-\* (fixes [#2439](https://github.com/FormidableLabs/victory/issues/2439)) ([#2440](https://github.com/FormidableLabs/victory/pull/2440)) ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) ## 36.6.2 ## 36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-vendor/README.md ================================================ # VictoryVendor Vendored dependencies for Victory. ## Background D3 has released most of its libraries as ESM-only. This means that consumers in Node.js applications can no longer just `require()` anything with a d3 transitive dependency, including much of Victory. To help provide an easy path to folks still using CommonJS in their Node.js applications that consume Victory, we now provide this package to vendor in various d3-related packages. ## Packages We presently provide the following top-level libraries: - d3-ease - d3-interpolate - d3-scale - d3-shape - d3-timer This is the total list of top and transitive libraries we vendor: - d3-array - d3-color - d3-ease - d3-format - d3-interpolate - d3-path - d3-scale - d3-shape - d3-time - d3-time-format - d3-timer - internmap Note that this does _not_ include the following D3 libraries that still support CommonJS: - d3-voronoi ## How it works We provide two alternate paths and behaviors -- for ESM and CommonJS ### ESM If you do a Node.js import like: ```js import { interpolate } from "victory-vendor/d3-interpolate"; ``` under the hood it's going to just re-export and pass you through to `node_modules/d3-interpolate`, the **real** ESM library from D3. ### CommonJS If you do a Node.js import like: ```js const { interpolate } = require("victory-vendor/d3-interpolate"); ``` under the hood it's going to will go to an alternate path that contains the transpiled version of the underlying d3 library to be found at `victory-vendor/lib-vendor/d3-interpolate/**/*.js`. This futher has internally consistent import references to other `victory-vendor/lib-vendor/` paths. Note that for some tooling (like Jest) that doesn't play well with `package.json:exports` routing to this CommonJS path, we **also** output a root file in the form of `victory-vendor/d3-interpolate.js`. ## Licenses This project is released under the MIT license, but the vendor'ed in libraries include other licenses (e.g. ISC) that we enumerate in our `package.json:license` field. ================================================ FILE: packages/victory-vendor/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-vendor/package.json ================================================ { "name": "victory-vendor", "version": "37.3.6", "description": "Vendored dependencies for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "author": "Formidable", "license": "MIT AND ISC", "exports": { "./package.json": "./package.json", "./d3-*": { "types": "./d3-*.d.ts", "import": "./es/d3-*.js", "default": "./lib/d3-*.js" } }, "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" }, "devDependencies": { "d3-color": "^3.1.0", "d3-format": "^3.1.0", "d3-path": "^3.0.1", "d3-time-format": "^4.1.0", "d3-voronoi": "^1.1.4", "internmap": "^2.0.3", "execa": "^6.1.0", "rimraf": "^3.0.2" }, "publishConfig": { "provenance": true }, "scripts": { "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "build": { "command": "node ./scripts/build.js", "files": [ ".babelrc.js", "scripts/build.js", "node_modules/**" ], "output": [ "es/**", "lib/**", "lib-vendor/**", "d3-*" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:esm": { "dependencies": [ "build" ] }, "build:lib:cjs": { "dependencies": [ "build" ] }, "build:dist": { "dependencies": [ "build" ] }, "build:dist:dev": { "dependencies": [ "build" ] }, "build:dist:min": { "dependencies": [ "build" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "echo \"No types to check here\"", "files": [], "output": [] }, "types:create": { "dependencies": [ "build" ] }, "lint": { "command": "eslint scripts", "files": [ "scripts/**" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix scripts", "files": [ "scripts/**" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "echo victory-vendor has no tests", "files": [], "output": [] } } } ================================================ FILE: packages/victory-vendor/scripts/build.js ================================================ /* global __dirname:false */ /** * Build d3 vendor libraries from `node_modules`. * * **Note - transitive dependencies**: Because pnpm lacks a `nohoist` option, * if you have a `d3-*` dependency that has a transitive dependency on another * module (e.g., `d3-interpolate` depends on `d3-color`) you need to add a * compatible version to `package.json:devDependencies` here to make sure we * get the library in our `node_modules` and appropriately build it. */ const fs = require("fs").promises; const path = require("path"); const { promisify } = require("util"); const rimraf = require("rimraf"); const rimrafP = promisify(rimraf); const vendorPkg = require("../package.json"); const VENDOR_PKGS = new Set(Object.keys(vendorPkg.dependencies)); const { log, error } = console; // Templates. const getEsmIndex = (pkg) => ` // \`victory-vendor/${pkg.name}\` (ESM) // See upstream license: ${pkg.repository.url.replace( /\.git$/, "", )}/blob/main/LICENSE // // Our ESM package uses the underlying installed dependencies of \`node_modules/${ pkg.name }\` export * from "${pkg.name}"; `; const getCjsIndex = (pkg) => ` // \`victory-vendor/${pkg.name}\` (CommonJS) // See upstream license: ${pkg.repository.url.replace( /\.git$/, "", )}/blob/main/LICENSE // // Our CommonJS package relies on transpiled vendor files in \`lib-vendor/${ pkg.name }\` module.exports = require("../lib-vendor/${pkg.name}/src/index.js"); `; const getCjsRootIndex = (pkg) => ` // \`victory-vendor/${pkg.name}\` (CommonJS) // See upstream license: ${pkg.repository.url.replace( /\.git$/, "", )}/blob/main/LICENSE // // This file only exists for tooling that doesn't work yet with package.json:exports // by proxying through the CommonJS version. module.exports = require("./lib/${pkg.name}"); `; const getTypeDefinitionFile = (pkg) => ` // \`victory-vendor/${pkg.name}\` (TypeScript) // // Export the type definitions for this package: export * from "${pkg.name}"; `; // Main. const main = async () => { // Lazy ESM imports. const { execa } = await import("execa"); // Get d3-related packages we want to vendor. const pkgs = ( await fs.readdir(path.resolve(__dirname, "../node_modules/")) ).filter((name) => /^(d3-|internmap)/.test(name)); // Safety check: we assume that **all** are flattened to root level of this // package, and want to make sure there are no nested dependencies. for (const pkgName of pkgs) { const pkgModsPath = path.resolve( __dirname, `../node_modules/git${pkgName}/node_modules`, ); const stat = await fs.lstat(pkgModsPath).catch(() => null); if (stat) { throw new Error(`Found nested modules: ${pkgModsPath}`); } } // Clean out and ensure base library paths exist const EsmBasePath = path.resolve(__dirname, `../es`); const CjsBasePath = path.resolve(__dirname, `../lib`); const VendorBasePath = path.resolve(__dirname, `../lib-vendor`); const baseDirs = [EsmBasePath, CjsBasePath, VendorBasePath]; const cleanGlobs = [].concat(baseDirs, path.resolve(__dirname, "../d3-*")); log("Cleaning old vendor directories."); await Promise.all(cleanGlobs.map((glob) => rimrafP(glob))); log("Creating empty vendor directories."); await Promise.all( baseDirs.map((libPath) => fs.mkdir(libPath, { recursive: true })), ); // Transpile. log("Transpiling vendor sources."); await execa( "pnpm", [ "babel", "--config-file", path.resolve(__dirname, "../.babelrc.js"), "-d", path.resolve(__dirname, "../lib-vendor"), path.resolve(__dirname, "../node_modules"), ], { stdio: "inherit", }, ); // Iterate and generate index files. log("Copying licenses and generating indexes."); for (const pkgName of pkgs) { log(`- ${pkgName}`); const pkgBase = path.resolve(__dirname, `../node_modules/${pkgName}`); const pkgPath = path.join(pkgBase, `package.json`); const pkg = await fs .readFile(pkgPath) .then((buf) => JSON.parse(buf.toString())); const libVendorPath = path.resolve(__dirname, `../lib-vendor/${pkgName}`); // Create library indexes and copy licenses to `lib-vendor. await Promise.all([ fs.writeFile(path.join(EsmBasePath, `${pkgName}.js`), getEsmIndex(pkg)), fs.writeFile(path.join(CjsBasePath, `${pkgName}.js`), getCjsIndex(pkg)), fs.copyFile( path.join(pkgBase, "LICENSE"), path.join(libVendorPath, "LICENSE"), ), // Root hack file for non package.json:exports systems VENDOR_PKGS.has(pkgName) && fs.writeFile( path.resolve(__dirname, `../${pkgName}.js`), getCjsRootIndex(pkg), ), // Generate TypeScript definitions VENDOR_PKGS.has(pkgName) && fs.writeFile( path.resolve(__dirname, `../${pkgName}.d.ts`), getTypeDefinitionFile(pkg), ), ]); } }; if (require.main === module) { main() // eslint-disable-next-line promise/always-return .then(() => { log("Build finished."); }) .catch((err) => { error(err); process.exit(-1); }); } ================================================ FILE: packages/victory-vendor/tests/d3-array.test.ts ================================================ /* * This test verifies that these modules and types are exported correctly */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { // @ts-expect-error Make sure invalid imports fail: INVALID_TYPE, Adder, Bin, Bisector, bin, bisect, bisectCenter, bisectLeft, bisectRight, bisector, count, } from "victory-vendor/d3-array"; describe("d3-array", () => { it("exports valid functions", () => { expect(bin).toBeInstanceOf(Function); expect(bisect).toBeInstanceOf(Function); }); }); ================================================ FILE: packages/victory-vendor/tests/d3-ease.test.ts ================================================ /* * This test verifies that these modules and types are exported correctly */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { // @ts-expect-error Make sure invalid imports fail: INVALID_TYPE, BackEasingFactory, ElasticEasingFactory, easeBackIn, easeBackInOut, easeBackOut, easeBounceIn, easeBounceInOut, easeBounceOut, easeCircle, easeCircleOut, easeLinear, } from "victory-vendor/d3-ease"; describe("d3-ease", () => { it("exports valid functions", () => { expect(easeLinear).toBeInstanceOf(Function); }); }); ================================================ FILE: packages/victory-vendor/tests/d3-interpolate.test.ts ================================================ /* * This test verifies that these modules and types are exported correctly */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { // @ts-expect-error Make sure invalid imports fail: INVALID_TYPE, interpolate, NumberArray, } from "victory-vendor/d3-interpolate"; describe("d3-interpolate", () => { it("exports valid functions", () => { expect(interpolate).toBeInstanceOf(Function); }); }); ================================================ FILE: packages/victory-vendor/tests/d3-scale.test.ts ================================================ /* eslint-disable @typescript-eslint/no-unused-vars */ import { // @ts-expect-error Make sure invalid imports fail: INVALID_TYPE, InterpolatorFactory, NumberValue, ScaleBand, ScaleContinuousNumeric, ScaleDiverging, ScaleIdentity, ScaleLinear, ScaleLogarithmic, ScaleOrdinal, ScalePoint, ScalePower, ScaleQuantile, ScaleQuantize, ScaleRadial, ScaleSequential, ScaleSequentialBase, ScaleSequentialQuantile, ScaleSymLog, ScaleThreshold, ScaleTime, UnknownReturnType, scaleBand, scaleDiverging, scaleDivergingLog, scaleDivergingPow, scaleDivergingSqrt, scaleDivergingSymlog, scaleIdentity, scaleImplicit, scaleLinear, scaleLog, scaleOrdinal, scalePoint, scalePow, scaleQuantile, scaleQuantize, scaleRadial, scaleSequential, scaleSequentialLog, scaleSequentialPow, scaleSequentialQuantile, scaleSequentialSqrt, scaleSequentialSymlog, scaleSqrt, scaleSymlog, scaleThreshold, scaleTime, scaleUtc, tickFormat, } from "victory-vendor/d3-scale"; describe("d3-scale", () => { it("exports valid functions", () => { expect(scaleLinear).toBeInstanceOf(Function); expect(scaleLog).toBeInstanceOf(Function); expect(scalePow).toBeInstanceOf(Function); expect(scaleSqrt).toBeInstanceOf(Function); }); }); ================================================ FILE: packages/victory-vendor/tests/d3-shape.test.ts ================================================ /* * This test verifies that these modules and types are exported correctly */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { // @ts-expect-error Make sure invalid imports fail: INVALID_TYPE, Arc, Area, arc, area, } from "victory-vendor/d3-shape"; describe("d3-shape", () => { it("exports valid functions", () => { expect(arc).toBeInstanceOf(Function); expect(area).toBeInstanceOf(Function); }); }); ================================================ FILE: packages/victory-vendor/tests/d3-time.test.ts ================================================ /* * This test verifies that these modules and types are exported correctly */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { // @ts-expect-error Make sure invalid imports fail: INVALID_TYPE, CountableTimeInterval, TimeInterval, timeDay, timeInterval, } from "victory-vendor/d3-time"; describe("d3-time", () => { it("exports valid functions", () => { expect(timeDay).toBeInstanceOf(Function); expect(timeInterval).toBeInstanceOf(Function); }); }); ================================================ FILE: packages/victory-vendor/tests/d3-timer.test.ts ================================================ /* * This test verifies that these modules and types are exported correctly */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { // @ts-expect-error Make sure invalid imports fail: INVALID_TYPE, now, Timer, timer, timerFlush, timeout, interval, } from "victory-vendor/d3-timer"; describe("d3-timer", () => { it("exports valid functions", () => { expect(timer).toBeInstanceOf(Function); expect(now).toBeInstanceOf(Function); }); }); ================================================ FILE: packages/victory-vendor/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-voronoi/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-voronoi/CHANGELOG.md ================================================ # victory-voronoi ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ### Patch Changes - Ensure undefined props do not overwrite defaults ([#2852](https://github.com/FormidableLabs/victory/pull/2852)) ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash keys with native code ([#2811](https://github.com/FormidableLabs/victory/pull/2811)) * Replace lodash array utils with native code ([#2810](https://github.com/FormidableLabs/victory/pull/2810)) - Replace lodash isNil and isNan with native code ([#2800](https://github.com/FormidableLabs/victory/pull/2800)) * Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) * Replace instances of lodash.range with equivalent native code ([#2760](https://github.com/FormidableLabs/victory/pull/2760)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Migrate victory-voronoi to TypeScript ([#2726](https://github.com/FormidableLabs/victory/pull/2726)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ### Patch Changes - Remove usage of defaultProps from components ([#2679](https://github.com/FormidableLabs/victory/pull/2679)) ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) - Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-voronoi/README.md ================================================ # VictoryVoronoi `victory-voronoi@^30.0.0` exports `VictoryVoronoi` and `Voronoi` components To view documentation for `VictoryVoronoi` please see https://commerce.nearform.com/open-source/victory/docs/victory-voronoi To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-voronoi.md ================================================ FILE: packages/victory-voronoi/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-voronoi/package.json ================================================ { "name": "victory-voronoi", "version": "37.3.6", "description": "Voronoi Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "d3-voronoi": "^1.1.4", "lodash": "^4.17.19", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-voronoi/src/helper-methods.ts ================================================ // victory-vendor note: This module is still CommonJS, so not part of victory-vendor. import { voronoi as d3Voronoi } from "d3-voronoi"; import { Helpers, LabelHelpers, Scale, Domain, Data } from "victory-core"; // Re-export for tests export { d3Voronoi as _internalD3Voronoi }; const getVoronoi = (props, range, scale) => { const minRange = [Math.min(...range.x), Math.min(...range.y)]; const maxRange = [Math.max(...range.x), Math.max(...range.y)]; const angleAccessor = (d) => { const x = scale.x(d._x1 !== undefined ? d._x1 : d._x); return -1 * x + Math.PI / 2; }; const xAccessor = (d) => { return props.horizontal ? scale.y(d._y1 !== undefined ? d._y1 : d._y) : scale.x(d._x1 !== undefined ? d._x1 : d._x); }; const yAccessor = (d) => { return props.horizontal ? scale.x(d._x1 !== undefined ? d._x1 : d._x) : scale.y(d._y1 !== undefined ? d._y1 : d._y); }; return d3Voronoi() .x((d) => (props.polar ? angleAccessor(d) : xAccessor(d))) .y((d) => yAccessor(d)) .extent([minRange, maxRange]); }; const getCalculatedValues = (props) => { const defaultStyles = props.theme && props.theme.voronoi && props.theme.voronoi.style ? props.theme.voronoi.style : {}; const style = Helpers.getStyles(props.style, defaultStyles); const range = { x: Helpers.getRange(props, "x"), y: Helpers.getRange(props, "y"), }; const domain = { x: Domain.getDomain(props, "x"), y: Domain.getDomain(props, "y"), }; const scale = { x: Scale.getBaseScale(props, "x") .domain(domain.x) .range(props.horizontal ? range.y : range.x), y: Scale.getBaseScale(props, "y") .domain(domain.y) .range(props.horizontal ? range.x : range.y), }; let data = Data.getData(props); data = Data.formatDataFromDomain(data, domain); // Manually remove data with null _x or _y values. // Otherwise, we hit null error in: d3-voronoi/src/Cell.js data = data.filter((datum) => { if (datum._x === null) { return false; } if (datum._y === null) { return false; } return true; }); const voronoi = getVoronoi(props, range, scale); const polygons = voronoi.polygons(data); const origin = props.polar ? props.origin || Helpers.getPolarOrigin(props) : undefined; return { domain, data, scale, style, polygons, origin }; }; export const getBaseProps = (initialProps, fallbackProps) => { const modifiedProps = Helpers.modifyProps( initialProps, fallbackProps, "scatter", ); const props = Object.assign( {}, modifiedProps, getCalculatedValues(modifiedProps), ); const { data, domain, events, height, origin, padding, polar, polygons, scale, sharedEvents, standalone, style, theme, width, labels, name, } = props; const initialChildProps = { parent: { style: style.parent, scale, domain, data, standalone, height, width, theme, origin, polar, padding, name, }, }; return data.reduce((childProps, datum, index) => { const polygon = polygons[index]?.filter((value) => value !== "data"); const eventKey = !Helpers.isNil(datum.eventKey) ? datum.eventKey : index; const { x, y } = Helpers.scalePoint(props, datum); const dataProps = { x, y, datum, data, index, scale, polygon, origin, size: props.size, style: style.data, }; childProps[eventKey] = { data: dataProps }; const text = LabelHelpers.getText(props, datum, index); if ( (text !== undefined && text !== null) || (labels && (events || sharedEvents)) ) { childProps[eventKey].labels = LabelHelpers.getProps(props, index); } return childProps; }, initialChildProps); }; ================================================ FILE: packages/victory-voronoi/src/index.ts ================================================ export * from "./victory-voronoi"; export * from "./voronoi"; ================================================ FILE: packages/victory-voronoi/src/victory-voronoi.test.tsx ================================================ import React from "react"; import random from "lodash/random"; import { fireEvent, render, screen } from "@testing-library/react"; import { Helpers } from "victory-core"; import { calculateD3Path } from "../../../test/helpers"; import { VictoryVoronoi, VictoryVoronoiProps } from "./victory-voronoi"; import { Voronoi } from "./voronoi"; describe("components/victory-voronoi", () => { describe("default component rendering", () => { it("accepts user props", () => { const { container } = render( , ); const svgNode = container.querySelector("svg"); expect(svgNode).toHaveAttribute("data-testid", "victory-voronoi"); expect(svgNode).toHaveAttribute("aria-label", "Chart"); }); it("renders an svg with the correct width and height", () => { const { container } = render(); const svg = container.querySelector("svg"); expect(svg?.style.width).toEqual("100%"); expect(svg?.style.height).toEqual("100%"); }); it("renders an svg with the correct viewbox", () => { const { container } = render(); const svg = container.querySelector("svg"); const viewBoxValue = `0 0 ${450} ${300}`; expect(svg).toHaveAttribute("viewBox", viewBoxValue); }); }); describe("component rendering with data", () => { it("renders the correct d3 path", () => { const props: VictoryVoronoiProps = { width: 400, height: 300, padding: 50, domain: { x: [0, 5], y: [0, 5] }, data: [ { x: 0, y: 0 }, { x: 2, y: 3 }, { x: 4, y: 1 }, ], }; const { container } = render(); expect(container.querySelector("path")).toHaveAttribute( "d", calculateD3Path(props, "voronoi", 0), ); }); it("sorts data by sortKey prop", () => { const data = Helpers.range(5) .map((i) => ({ x: i, y: i })) .reverse(); render( } />, ); const renderedDataProps = screen .getAllByTestId("voronoi-1") .map((node) => JSON.parse(node.getAttribute("data-props-json") || "")); expect(renderedDataProps.map((props) => props.datum._x)).toEqual([ 0, 1, 2, 3, 4, ]); }); it("reverses sorted data with the sortOrder prop", () => { const data = Helpers.range(5) .map((i) => ({ x: i, y: i })) .reverse(); render( } />, ); const renderedDataProps = screen .getAllByTestId("voronoi-1") .map((node) => JSON.parse(node.getAttribute("data-props-json") || "")); expect(renderedDataProps.map((props) => props.datum._x)).toEqual([ 4, 3, 2, 1, 0, ]); }); it("does not render data with null x or y values", () => { const data = [ { x: 1, y: 2 }, { x: null, y: 4 }, { x: 5, y: null }, { x: 1, y: 2 }, ]; const { container } = render(); const paths = container.querySelectorAll("path"); expect(paths).toHaveLength(2); }); }); describe("event handling", () => { it("attaches an event to the parent svg", () => { const clickHandler = jest.fn(); const { container } = render( , ); const svg = container.querySelector("svg"); fireEvent.click(svg!); expect(clickHandler).toBeCalled(); // the first argument is the standard evt object expect(Object.keys(clickHandler.mock.calls[0][1])).toEqual( expect.arrayContaining(["data", "scale", "width", "height", "style"]), ); }); it("attaches an event to data", () => { const clickHandler = jest.fn(); const { container } = render( , ); const data = container.querySelectorAll("path"); expect(data).toHaveLength(51); data.forEach((node, index) => { clickHandler.mockClear(); fireEvent.click(node); expect(clickHandler).toBeCalled(); // the first argument is the standard evt object const [, evProps, evIndex] = clickHandler.mock.calls[0]; expect(evProps).toMatchObject({ id: `voronoi-data-${index}` }); expect(evIndex).toEqual(`${index}`); }); }); it("attaches an event to a label", () => { const clickHandler = jest.fn(); const { container } = render( , ); const labels = container.querySelectorAll("text"); expect(labels).toHaveLength(1); labels.forEach((node, index) => { clickHandler.mockClear(); fireEvent.click(node); expect(clickHandler).toBeCalled(); // the first argument is the standard evt object const [, evProps, evIndex] = clickHandler.mock.calls[0]; expect(evProps).toMatchObject({ text: "okay" }); expect(evIndex).toEqual(`${index}`); }); }); }); describe("accessibility", () => { it("adds an aria role to the path area", () => { const { container } = render(); const paths = container.querySelectorAll("path"); expect(paths).toHaveLength(51); paths.forEach((p) => { expect(p).toHaveAttribute("role", "presentation"); }); }); it("adds an aria-label and tabindex to Voronoi primitive", () => { const data = Helpers.range(3, 6).map((x) => ({ x, y: random(5) })); const { container } = render( `${datum.x}`} tabIndex={({ index }) => Number(index) + 6} /> } />, ); const paths = container.querySelectorAll("path"); expect(paths).toHaveLength(3); paths.forEach((p, i) => { expect(p).toHaveAttribute("aria-label", `${data[i].x}`); expect(p).toHaveAttribute("tabindex", `${i + 6}`); }); }); }); }); ================================================ FILE: packages/victory-voronoi/src/victory-voronoi.tsx ================================================ import React from "react"; import { Helpers, VictoryLabel, addEvents, VictoryContainer, VictoryTheme, DefaultTransitions, Data, Domain, UserProps, EventPropTypeInterface, EventsMixinClass, VictoryCommonProps, VictoryDatableProps, VictoryLabelableProps, VictoryMultiLabelableProps, VictoryStyleInterface, } from "victory-core"; import { Voronoi } from "./voronoi"; import { getBaseProps } from "./helper-methods"; export type VictoryVoronoiSortOrderType = "ascending" | "descending"; export interface VictoryVoronoiProps extends Omit, VictoryDatableProps, VictoryLabelableProps, VictoryMultiLabelableProps { events?: EventPropTypeInterface< string, string | number | (string | number)[] >[]; type?: number; sortOrder?: VictoryVoronoiSortOrderType; size?: number | { (data: any): number }; style?: VictoryStyleInterface; } const fallbackProps = { width: 450, height: 300, padding: 50, }; // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface VictoryVoronoiBase extends EventsMixinClass {} class VictoryVoronoiBase extends React.Component { static animationWhitelist: (keyof VictoryVoronoiProps)[] = [ "data", "domain", "height", "padding", "samples", "size", "style", "width", ]; static displayName = "VictoryVoronoi"; static role = "voronoi"; static defaultTransitions = DefaultTransitions.discreteTransitions(); static defaultProps: VictoryVoronoiProps = { containerComponent: , dataComponent: , labelComponent: , groupComponent: , samples: 50, sortOrder: "ascending", standalone: true, theme: VictoryTheme.grayscale, }; static getDomain = Domain.getDomain; static getData = Data.getData; static getBaseProps(props: VictoryVoronoiProps) { return getBaseProps(props, fallbackProps); } static expectedComponents: (keyof VictoryVoronoiProps)[] = [ "dataComponent", "labelComponent", "groupComponent", "containerComponent", ]; // Overridden in native versions shouldAnimate() { return !!this.props.animate; } render(): React.ReactElement { const { animationWhitelist, role } = VictoryVoronoi; const props = Helpers.modifyProps(this.props, fallbackProps, role); if (this.shouldAnimate()) { return this.animateComponent(props, animationWhitelist); } const children = this.renderData(props); const component = props.standalone ? this.renderContainer(props.containerComponent, children) : children; return UserProps.withSafeUserProps(component, props); } } export const VictoryVoronoi = addEvents(VictoryVoronoiBase); ================================================ FILE: packages/victory-voronoi/src/voronoi.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import { Helpers, ClipPath, Path, Circle, UserProps, VictoryCommonPrimitiveProps, } from "victory-core"; export interface VoronoiProps extends VictoryCommonPrimitiveProps { circleComponent?: React.ReactElement; clipId?: number | string; clipPathComponent?: React.ReactElement; datum?: any; groupComponent?: React.ReactElement; pathComponent?: React.ReactElement; polygon?: []; size?: number; x?: number; y?: number; } const getVoronoiPath = (props: VoronoiProps) => { const { polygon } = props; return Array.isArray(polygon) && polygon.length ? `M ${props.polygon?.join("L")} Z` : ""; }; function evaluateProps(props: T) { /** * Potential evaluated props are: * `aria-label` * `id` * `size` * `style` * `tabIndex */ const ariaLabel = Helpers.evaluateProp(props.ariaLabel, props); const id = Helpers.evaluateProp(props.id, props); const size = Helpers.evaluateProp(props.size, props); const style = Helpers.evaluateStyle(props.style, props); const tabIndex = Helpers.evaluateProp(props.tabIndex, props); return Object.assign({}, props, { ariaLabel, id, size, style, tabIndex }); } const defaultProps = { pathComponent: , circleComponent: , clipPathComponent: , groupComponent: , role: "presentation", shapeRendering: "auto", }; export const Voronoi = (initialProps: VoronoiProps) => { const props = evaluateProps(defaults({}, initialProps, defaultProps)); const { ariaLabel, role, shapeRendering, className, events, transform, style, size, tabIndex, } = props; const voronoiPath = getVoronoiPath(props); const sharedProps = { "aria-label": ariaLabel, className, role, shapeRendering, style, tabIndex, transform, ...events, }; const userProps = UserProps.getSafeUserProps(props); if (size) { const circle = React.cloneElement(props.circleComponent, { ...sharedProps, key: `${props.id}-circle-clip`, clipPath: `url(#${props.clipId})`, cx: props.x, cy: props.y, r: size, }); const voronoiClipPath = React.cloneElement( props.clipPathComponent, { key: `${props.id}-voronoi-clip`, clipId: props.clipId }, React.cloneElement(props.pathComponent, { d: voronoiPath, className }), ); return React.cloneElement(props.groupComponent, {}, [ voronoiClipPath, circle, ]); } return React.cloneElement(props.pathComponent, { ...sharedProps, ...userProps, d: voronoiPath, }); }; ================================================ FILE: packages/victory-voronoi/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-voronoi/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-voronoi-container/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-voronoi-container/CHANGELOG.md ================================================ # victory-voronoi-container ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) * Improve types in victory-core helpers ([#2999](https://github.com/FormidableLabs/victory/pull/2999)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ### Patch Changes - Fix: #2369 `` tooltip breaks when mouse moves quickly between different charts ([#2911](https://github.com/FormidableLabs/victory/pull/2911)) ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ## 37.1.0 ### Minor Changes - Refactor containers and portal to function components ([#2799](https://github.com/FormidableLabs/victory/pull/2799)) * Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash keys with native code ([#2811](https://github.com/FormidableLabs/victory/pull/2811)) * Replace lodash isString with native equivalent ([#2827](https://github.com/FormidableLabs/victory/pull/2827)) - Replace lodash array utils with native code ([#2810](https://github.com/FormidableLabs/victory/pull/2810)) * Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ### Patch Changes - Fix victory-native component prop types ([#2785](https://github.com/FormidableLabs/victory/pull/2785)) ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Migrate victory-voronoi-container to TypeScript ([#2727](https://github.com/FormidableLabs/victory/pull/2727)) * Refactor param reassignments ([#2724](https://github.com/FormidableLabs/victory/pull/2724)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Updated dependencies []: - victory-core@36.6.8 - victory-tooltip@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 - victory-tooltip@36.6.7 ## 36.6.6 ### Patch Changes - Improved the exported types (fixes [#2451](https://github.com/FormidableLabs/victory/issues/2451)) ([#2452](https://github.com/FormidableLabs/victory/pull/2452)) - Updated dependencies []: - victory-core@36.6.6 - victory-tooltip@36.6.6 ## 36.6.5 ### Patch Changes - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 - victory-tooltip@36.6.5 ## 36.6.4 ### Patch Changes - Added explicit `any` type defs (fixes [#2358](https://github.com/FormidableLabs/victory/issues/2358)) ([`57ed0fe30`](https://github.com/FormidableLabs/victory/commit/57ed0fe304dbc8753da1126a02d44de8004e96aa)) * Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) * Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 - victory-tooltip@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 - victory-tooltip@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 - victory-tooltip@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 - victory-tooltip@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 - victory-tooltip@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-voronoi-container/README.md ================================================ # VictoryVoronoiContainer `victory-voronoi-container@^30.0.0` exports `VictoryVoronoiContainer`, `voronoiContainerMixin` and `VoronoiHelpers` To view documentation for `VictoryVoronoiContainer` please see https://commerce.nearform.com/open-source/victory/docs/victory-voronoi-container To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-voronoi-container.md ================================================ FILE: packages/victory-voronoi-container/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-voronoi-container/package.json ================================================ { "name": "victory-voronoi-container", "version": "37.3.6", "description": "Interactive Voronoi Mouseover Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "delaunay-find": "0.0.6", "lodash": "^4.17.19", "react-fast-compare": "^3.2.0", "victory-core": "37.3.6", "victory-tooltip": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-tooltip:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs", "../victory-tooltip:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-tooltip:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm", "../victory-tooltip:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-tooltip:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create", "../victory-tooltip:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-tooltip:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-tooltip:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-tooltip:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-voronoi-container/src/index.ts ================================================ export * from "./victory-voronoi-container"; export * from "./voronoi-helpers"; ================================================ FILE: packages/victory-voronoi-container/src/victory-voronoi-container.tsx ================================================ import React from "react"; import defaults from "lodash/defaults"; import pick from "lodash/pick"; import { VictoryTooltip } from "victory-tooltip"; import { Helpers, VictoryContainerProps, PaddingProps, VictoryContainer, VictoryEventHandler, } from "victory-core"; import { VoronoiHelpers } from "./voronoi-helpers"; export interface VictoryVoronoiContainerProps extends VictoryContainerProps { activateData?: boolean; activateLabels?: boolean; disable?: boolean; labels?: (point: any, index?: number, points?: any[]) => string; labelComponent?: React.ReactElement; mouseFollowTooltips?: boolean; onActivated?: (points: any[], props: VictoryVoronoiContainerProps) => void; onDeactivated?: (points: any[], props: VictoryVoronoiContainerProps) => void; radius?: number; voronoiBlacklist?: (string | RegExp)[]; voronoiDimension?: "x" | "y"; voronoiPadding?: PaddingProps; horizontal?: boolean; } interface VictoryVoronoiContainerMutatedProps extends VictoryVoronoiContainerProps { mousePosition: { x: number; y: number }; activePoints: any[]; } export const VICTORY_VORONOI_CONTAINER_DEFAULT_PROPS = { activateData: true, activateLabels: true, labelComponent: , voronoiPadding: 5, }; const getPoint = (point) => { const whitelist = ["_x", "_x1", "_x0", "_y", "_y1", "_y0"]; return pick(point, whitelist); }; export const useVictoryVoronoiContainer = ( initialProps: VictoryVoronoiContainerProps, ) => { const props = { ...VICTORY_VORONOI_CONTAINER_DEFAULT_PROPS, ...(initialProps as VictoryVoronoiContainerMutatedProps), }; const { children } = props; const getDimension = () => { const { horizontal, voronoiDimension } = props; if (!horizontal || !voronoiDimension) { return voronoiDimension; } return voronoiDimension === "x" ? "y" : "x"; }; const getLabelPosition = (labelProps, points) => { const { mousePosition, mouseFollowTooltips } = props; const voronoiDimension = getDimension(); const point = getPoint(points[0]); // @ts-expect-error scale is defined but the types do not reflect that const basePosition = Helpers.scalePoint(props, point); let center = mouseFollowTooltips ? mousePosition : undefined; if (!voronoiDimension || points.length < 2) { return { ...basePosition, center: defaults({}, labelProps.center, center), }; } const x = voronoiDimension === "y" ? mousePosition.x : basePosition.x; const y = voronoiDimension === "x" ? mousePosition.y : basePosition.y; center = mouseFollowTooltips ? mousePosition : { x, y }; return { x, y, center: defaults({}, labelProps.center, center) }; }; const getStyle = (points, type) => { const { labels, labelComponent, theme } = props; const componentProps = labelComponent.props || {}; const themeStyles = theme && theme.voronoi && theme.voronoi.style ? theme.voronoi.style : {}; const componentStyleArray = type === "flyout" ? componentProps.flyoutStyle : componentProps.style; return points.reduce((memo, datum, index) => { const labelProps = defaults({}, componentProps, { datum, active: true, }); const text = Helpers.isFunction(labels) ? labels(labelProps) : undefined; const textArray = text !== undefined ? `${text}`.split("\n") : []; const baseStyle = (datum.style && datum.style[type]) || {}; const componentStyle = Array.isArray(componentStyleArray) ? componentStyleArray[index] : componentStyleArray; const style = Helpers.evaluateStyle( defaults({}, componentStyle, baseStyle, themeStyles[type]), labelProps, ); const styleArray = textArray.length ? textArray.map(() => style) : [style]; return memo.concat(styleArray); }, []); }; const getDefaultLabelProps = (points) => { const { voronoiDimension, horizontal, mouseFollowTooltips } = props; const point = getPoint(points[0]); const multiPoint = voronoiDimension && points.length > 1; const y = point._y1 !== undefined ? point._y1 : point._y; const defaultHorizontalOrientation = y < 0 ? "left" : "right"; const defaultOrientation = y < 0 ? "bottom" : "top"; const labelOrientation = horizontal ? defaultHorizontalOrientation : defaultOrientation; const orientation = mouseFollowTooltips ? undefined : labelOrientation; return { orientation, pointerLength: multiPoint ? 0 : undefined, constrainToVisibleArea: multiPoint || mouseFollowTooltips ? true : undefined, }; }; const getLabelProps = (points) => { const { labels, scale, labelComponent, theme, width, height } = props; const componentProps = labelComponent.props || {}; const text = points.reduce((memo, datum) => { const labelProps = defaults({}, componentProps, { datum, active: true, }); const t = Helpers.isFunction(labels) ? labels(labelProps) : null; if (t === null || t === undefined) { return memo; } return memo.concat(`${t}`.split("\n")); }, []); // remove properties from first point to make datum // eslint-disable-next-line @typescript-eslint/no-unused-vars const { childName, eventKey, style, continuous, ...datum } = points[0]; const name = props.name === childName ? childName : `${props.name}-${childName}`; const labelProps = defaults( { key: `${name}-${eventKey}-voronoi-tooltip`, id: `${name}-${eventKey}-voronoi-tooltip`, active: true, renderInPortal: false, activePoints: points, datum, scale, theme, }, componentProps, { text, width, height, style: getStyle(points, "labels"), flyoutStyle: getStyle(points, "flyout")[0], }, getDefaultLabelProps(points), ); const labelPosition = getLabelPosition(labelProps, points); return defaults({}, labelPosition, labelProps); }; const getTooltip = () => { const { labels, activePoints, labelComponent } = props; if (!labels) { return null; } if (Array.isArray(activePoints) && activePoints.length) { const labelProps = getLabelProps(activePoints); const { text } = labelProps; const showLabel = Array.isArray(text) ? text.filter(Boolean).length : text; return showLabel ? React.cloneElement(labelComponent, labelProps) : null; } return null; }; return { props, children: [ ...React.Children.toArray(children), getTooltip(), ] as React.ReactElement[], }; }; export const VictoryVoronoiContainer = ( initialProps: VictoryVoronoiContainerProps, ) => { const { props, children } = useVictoryVoronoiContainer(initialProps); return {children}; }; VictoryVoronoiContainer.role = "container"; VictoryVoronoiContainer.defaultEvents = ( initialProps: VictoryVoronoiContainerProps, ) => { const props = { ...VICTORY_VORONOI_CONTAINER_DEFAULT_PROPS, ...initialProps }; const createEventHandler = (handler: VictoryEventHandler, disabled?: boolean): VictoryEventHandler => // eslint-disable-next-line max-params (event, targetProps, eventKey, context) => disabled || props.disable ? {} : handler(event, { ...props, ...targetProps }, eventKey, context); return [ { target: "parent", eventHandlers: { onMouseLeave: createEventHandler(VoronoiHelpers.onMouseLeave), onTouchCancel: createEventHandler(VoronoiHelpers.onMouseLeave), onMouseMove: createEventHandler(VoronoiHelpers.onMouseMove), onTouchMove: createEventHandler(VoronoiHelpers.onMouseMove), }, }, { target: "data", eventHandlers: props.disable ? {} : { onMouseOver: () => null, onMouseOut: () => null, onMouseMove: () => null, }, }, ]; }; ================================================ FILE: packages/victory-voronoi-container/src/voronoi-helpers.ts ================================================ import { Collection, Selection, Data, Helpers } from "victory-core"; import isEmpty from "lodash/isEmpty"; import isRegExp from "lodash/isRegExp"; import throttle from "lodash/throttle"; import isEqual from "react-fast-compare"; import Delaunay from "delaunay-find/lib/index.js"; import React from "react"; const ON_MOUSE_MOVE_THROTTLE_MS = 32; class VoronoiHelpersClass { withinBounds(props, point) { const { width, height, polar, origin, scale } = props; const padding = Helpers.getPadding(props.voronoiPadding); const { x, y } = point; if (polar) { const distanceSquared = Math.pow(x - origin.x, 2) + Math.pow(y - origin.y, 2); const radius = Math.max(...scale.y.range()); return distanceSquared < Math.pow(radius, 2); } return ( x >= padding.left && x <= width - padding.right && y >= padding.top && y <= height - padding.bottom ); } getDatasets(props) { const minDomain = { x: Collection.getMinValue(props.domain.x), y: Collection.getMinValue(props.domain.y), }; const children = React.Children.toArray(props.children); const addMeta = (data, name?, child?) => { const continuous = child && child.type && child.type.continuous; const style = child ? child.props && child.props.style : props.style; return data.map((datum, index) => { const { x, y, y0, x0 } = Helpers.getPoint(datum); const voronoiX = (Number(x) + Number(x0)) / 2; const voronoiY = (Number(y) + Number(y0)) / 2; return Object.assign( { _voronoiX: props.voronoiDimension === "y" ? minDomain.x : voronoiX, _voronoiY: props.voronoiDimension === "x" ? minDomain.y : voronoiY, eventKey: index, childName: name, continuous, style, }, datum, ); }); }; if (props.data) { return addMeta(props.data); } const getData = (childProps) => { const data = Data.getData(childProps); return Array.isArray(data) && data.length > 0 ? data : undefined; }; const iteratee = (child, childName) => { const childProps = child.props || {}; const name = childProps.name || childName; const blacklist = props.voronoiBlacklist || []; const blacklistStr = blacklist.filter( (value) => !!value && typeof value.valueOf() === "string", ); const blacklistRegExp = blacklist.filter(isRegExp); const isRegExpMatch = blacklistRegExp.some((regExp) => regExp.test(name)); if ( !Data.isDataComponent(child) || blacklistStr.includes(name) || isRegExpMatch ) { return null; } const getChildData = child.type && Helpers.isFunction(child.type.getData) ? child.type.getData : getData; const childData = getChildData(child.props); return childData ? addMeta(childData, name, child) : null; }; return Helpers.reduceChildren(children, iteratee, props); } findPoints(datasets, point) { return datasets.filter((d) => { return point._voronoiX === d._voronoiX && point._voronoiY === d._voronoiY; }); } withinRadius(point, mousePosition, radius) { if (!point) { return false; } if (!radius) { return true; } const { x, y } = mousePosition; const distanceSquared = Math.pow(x - point[0], 2) + Math.pow(y - point[1], 2); return distanceSquared < Math.pow(radius, 2); } getVoronoiPoints(props, mousePosition) { const datasets = this.getDatasets(props); const scaledData = datasets.map((d) => { const { x, y } = Helpers.scalePoint(props, d); return [x, y]; }); const delaunay = Delaunay.from(scaledData); const index = delaunay.find(mousePosition.x, mousePosition.y); const withinRadius = this.withinRadius( scaledData[index], mousePosition, props.radius, ); const points = withinRadius ? this.findPoints(datasets, datasets[index]) : []; return { points, index }; } getActiveMutations(props, point) { const { childName, continuous } = point; const { activateData, activateLabels, labels } = props; if (!activateData && !activateLabels) { return []; } const defaultTarget = activateData ? ["data"] : []; const targets = labels && !activateLabels ? defaultTarget : defaultTarget.concat("labels"); if (isEmpty(targets)) { return []; } return targets.map((target) => { const eventKey = continuous === true && target === "data" ? "all" : point.eventKey; return { childName, eventKey, target, mutation: () => ({ active: true }), }; }); } getInactiveMutations(props, point) { const { childName, continuous } = point; const { activateData, activateLabels, labels } = props; if (!activateData && !activateLabels) { return []; } const defaultTarget = activateData ? ["data"] : []; const targets = labels && !activateLabels ? defaultTarget : defaultTarget.concat("labels"); if (isEmpty(targets)) { return []; } return targets.map((target) => { const eventKey = continuous && target === "data" ? "all" : point.eventKey; return { childName, eventKey, target, mutation: () => null, }; }); } // eslint-disable-next-line max-params getParentMutation(activePoints, mousePosition?, parentSVG?, vIndex?) { return [ { target: "parent", eventKey: "parent", mutation: () => ({ activePoints, mousePosition, parentSVG, vIndex }), }, ]; } onActivated(props, points) { if (Helpers.isFunction(props.onActivated)) { props.onActivated(points, props); } } onDeactivated(props, points) { if (Helpers.isFunction(props.onDeactivated)) { props.onDeactivated(points, props); } } onMouseLeave = (evt, targetProps) => { this.onMouseMove.cancel(); const activePoints = targetProps.activePoints || []; this.onDeactivated(targetProps, activePoints); const inactiveMutations = activePoints.length ? activePoints.map((point) => this.getInactiveMutations(targetProps, point), ) : []; return this.getParentMutation([]).concat(...inactiveMutations); }; private handleMouseMove = (evt, targetProps) => { const activePoints = targetProps.activePoints || []; const parentSVG = targetProps.parentSVG || Selection.getParentSVG(evt); const mousePosition = Selection.getSVGEventCoordinates(evt, parentSVG); if (!this.withinBounds(targetProps, mousePosition)) { this.onDeactivated(targetProps, activePoints); const inactiveMutations = activePoints.length ? activePoints.map((point) => this.getInactiveMutations(targetProps, point), ) : []; return this.getParentMutation([], mousePosition, parentSVG).concat( ...inactiveMutations, ); } const { points = [], index } = this.getVoronoiPoints( targetProps, mousePosition, ); const parentMutations = this.getParentMutation( points, mousePosition, parentSVG, index, ); if (activePoints.length && isEqual(points, activePoints)) { return parentMutations; } this.onActivated(targetProps, points); this.onDeactivated(targetProps, activePoints); const activeMutations = points.length ? points.map((point) => this.getActiveMutations(targetProps, point)) : []; const inactiveMutations = activePoints.length ? activePoints.map((point) => this.getInactiveMutations(targetProps, point), ) : []; return parentMutations.concat(...inactiveMutations, ...activeMutations); }; onMouseMove = throttle(this.handleMouseMove, ON_MOUSE_MOVE_THROTTLE_MS, { leading: true, trailing: false, }); } export const VoronoiHelpers = new VoronoiHelpersClass(); ================================================ FILE: packages/victory-voronoi-container/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-voronoi-container/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-zoom-container/.npmignore ================================================ /* !/dist !/es !/lib !/src !LICENSE.txt !CHANGELOG.md !README.md !package.json *.map **/*.test.* ================================================ FILE: packages/victory-zoom-container/CHANGELOG.md ================================================ # victory-zoom-container ## 37.3.6 ## 37.3.5 ## 37.3.4 ## 37.3.3 ### Patch Changes - Remove deprecated babel-plugin-lodash plugin ([#2965](https://github.com/FormidableLabs/victory/pull/2965)) ## 37.3.2 ## 37.3.1 ## 37.3.0 ## 37.2.0 ## 37.1.2 ### Patch Changes - Fix victory-native container styles ([`eae3fe5dd`](https://github.com/FormidableLabs/victory/commit/eae3fe5dde175e68e146576655cb2e8054ad6456)) ## 37.1.1 ### Patch Changes - Fix: #2761 zoom out zooming in if cursor outside chart container ([#2893](https://github.com/FormidableLabs/victory/pull/2893)) ## 37.1.0 ### Minor Changes - Refactor containers and portal to function components ([#2799](https://github.com/FormidableLabs/victory/pull/2799)) * Pin all internal victory package versions ([#2876](https://github.com/FormidableLabs/victory/pull/2876)) ## 37.0.2 ## 37.0.1 ### Patch Changes - Fix the signature of class static functions in components ([#2840](https://github.com/FormidableLabs/victory/pull/2840)) ## 37.0.0 ### Major Changes - Upgrade babel dependencies and build target to modern browsers ([#2804](https://github.com/FormidableLabs/victory/pull/2804)) ## 36.9.2 ### Patch Changes - Replace lodash isFunction with native code ([#2802](https://github.com/FormidableLabs/victory/pull/2802)) ## 36.9.1 ### Patch Changes - Fix victory-native component prop types ([#2785](https://github.com/FormidableLabs/victory/pull/2785)) ## 36.9.0 ### Minor Changes - Remove prop-types definitions and dependency ([#2758](https://github.com/FormidableLabs/victory/pull/2758)) ## 36.8.6 ## 36.8.5 ### Patch Changes - Replace instances of lodash.assign with Object.assign ([#2757](https://github.com/FormidableLabs/victory/pull/2757)) ## 36.8.4 ## 36.8.3 ### Patch Changes - Migrate victory-zoom-container to TypeScript ([#2730](https://github.com/FormidableLabs/victory/pull/2730)) ## 36.8.2 ## 36.8.1 ## 36.8.0 ## 36.7.0 ## 36.6.12 ## 36.6.11 ## 36.6.10 ### Patch Changes - Setup NPM Provenance ([#2590](https://github.com/FormidableLabs/victory/pull/2590)) ## 36.6.9 ### Patch Changes - Setup NPM Provenance ([#2587](https://github.com/FormidableLabs/victory/pull/2587)) ## 36.6.8 ### Patch Changes - Test for Changesets GitHub Actions workflow fix ([#2469](https://github.com/FormidableLabs/victory/pull/2469)) - Updated dependencies []: - victory-core@36.6.8 ## 36.6.7 ### Patch Changes - Updated dependencies []: - victory-core@36.6.7 ## 36.6.6 ### Patch Changes - Improved the exported types (fixes [#2451](https://github.com/FormidableLabs/victory/issues/2451)) ([#2452](https://github.com/FormidableLabs/victory/pull/2452)) - Updated dependencies []: - victory-core@36.6.6 ## 36.6.5 ### Patch Changes - Added explicit any for certain parameters (fixes [#2439](https://github.com/FormidableLabs/victory/issues/2439)) ([#2440](https://github.com/FormidableLabs/victory/pull/2440)) - Updated dependencies [[`6f4972123`](https://github.com/FormidableLabs/victory/commit/6f49721238332bb5ee879571a45b34a04e44d416)]: - victory-core@36.6.5 ## 36.6.4 ### Patch Changes - Added explicit `any` type defs (fixes [#2358](https://github.com/FormidableLabs/victory/issues/2358)) ([`57ed0fe30`](https://github.com/FormidableLabs/victory/commit/57ed0fe304dbc8753da1126a02d44de8004e96aa)) * Allow data accessors to accept any data types (fixes [#2360](https://github.com/FormidableLabs/victory/issues/2360)) ([#2436](https://github.com/FormidableLabs/victory/pull/2436)) * Updated dependencies [[`9a6319cff`](https://github.com/FormidableLabs/victory/commit/9a6319cffbc480711b8c286dcae00575081170f0)]: - victory-core@36.6.4 ## 36.6.3 ### Patch Changes - Do not generate \*.js.map sourcemaps (fixes [#2346](https://github.com/FormidableLabs/victory/issues/2346)) ([#2432](https://github.com/FormidableLabs/victory/pull/2432)) - Updated dependencies [[`4bfc65df5`](https://github.com/FormidableLabs/victory/commit/4bfc65df5a10aa6a10084882ed5c6d0d894dec6f)]: - victory-core@36.6.3 ## 36.6.2 ### Patch Changes - Updated dependencies []: - victory-core@36.6.2 ## 36.6.1 ### Patch Changes - Updated dependencies [[`d1f281104`](https://github.com/FormidableLabs/victory/commit/d1f281104c7598c43e220dafd57546ab03daeeb5)]: - victory-core@36.6.1 ## 36.6.0 ### Patch Changes - Update source code with minor lint-based improvements (see [#2236](https://github.com/FormidableLabs/victory/issues/2236)). ([#2403](https://github.com/FormidableLabs/victory/pull/2403)) - Updated dependencies [[`fed5a5072`](https://github.com/FormidableLabs/victory/commit/fed5a507299b337846eed3d873ec7eb91bc69668), [`a2f48555a`](https://github.com/FormidableLabs/victory/commit/a2f48555adfed15bdb004dc0793f197d90c950a2)]: - victory-core@36.6.0 ## 36.5.3 and earlier Change history for version 36.5.3 and earlier can be found in our root [CHANGELOG.md](https://github.com/FormidableLabs/victory/blob/main/CHANGELOG.md). ================================================ FILE: packages/victory-zoom-container/README.md ================================================ # VictoryZoomContainer `victory-zoom-container@^30.0.0` exports `VictoryZoomContainer`, `zoomContainerMixin`, `ZoomHelpers`, and `RawZoomHelpers` To view documentation for `VictoryZoomContainer` please see https://commerce.nearform.com/open-source/victory/docs/victory-zoom-container To suggest an addition or correction to this documentation please see https://github.com/FormidableLabs/victory/blob/main/docs/src/content/docs/victory-zoom-container.md ================================================ FILE: packages/victory-zoom-container/jest.config.ts ================================================ import rootConfig from "../../test/jest.config"; export default { ...rootConfig, }; ================================================ FILE: packages/victory-zoom-container/package.json ================================================ { "name": "victory-zoom-container", "version": "37.3.6", "description": "Interactive Zoom Component for Victory", "keywords": [ "data visualization", "React", "d3", "charting" ], "repository": { "type": "git", "url": "https://github.com/FormidableLabs/victory" }, "homepage": "https://commerce.nearform.com/open-source/victory", "sideEffects": false, "main": "lib/index.js", "module": "es/index.js", "jsnext:main": "es/index.js", "author": "Formidable", "license": "MIT", "dependencies": { "lodash": "^4.17.19", "victory-core": "37.3.6" }, "peerDependencies": { "react": ">=16.6.0" }, "publishConfig": { "provenance": true }, "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ] }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ] }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js" ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ "../victory-core:build:lib:cjs" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ] }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js" ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js" ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ "../victory-core:build:lib:esm" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint" ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json" ], "dependencies": [ "types:create", "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "output": [], "packageLocks": [ "pnpm-lock.yaml" ] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json" ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map" ], "dependencies": [ "../victory-core:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint": { "command": "eslint src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "lint:fix": { "command": "eslint --fix src", "files": [ "src/**" ], "output": [], "dependencies": [ "../victory-core:types:create", "../victory-vendor:types:create", "../victory-voronoi:types:create" ], "packageLocks": [ "pnpm-lock.yaml" ] }, "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts" ], "output": [], "dependencies": [ "../victory-core:build", "../victory-vendor:build", "../victory-voronoi:build" ], "packageLocks": [ "pnpm-lock.yaml" ] } }, "engines": { "node": ">=18.0.0" } } ================================================ FILE: packages/victory-zoom-container/src/index.ts ================================================ export * from "./victory-zoom-container"; export * from "./zoom-helpers"; ================================================ FILE: packages/victory-zoom-container/src/victory-zoom-container.tsx ================================================ import React from "react"; import { ZoomHelpers } from "./zoom-helpers"; import { VictoryClipContainer, VictoryContainerProps, DomainTuple, VictoryContainer, Data, VictoryEventHandler, } from "victory-core"; import defaults from "lodash/defaults"; const DEFAULT_DOWNSAMPLE = 150; export type ZoomDimensionType = "x" | "y"; export type ZoomDomain = { x: DomainTuple; y: DomainTuple; }; export interface VictoryZoomContainerProps extends VictoryContainerProps { allowPan?: boolean; allowZoom?: boolean; clipContainerComponent?: React.ReactElement; disable?: boolean; downsample?: number | boolean; minimumZoom?: { x?: number; y?: number }; onZoomDomainChange?: ( domain: ZoomDomain, props: VictoryZoomContainerProps, ) => void; zoomDimension?: ZoomDimensionType; zoomDomain?: Partial; horizontal?: boolean; } interface VictoryZoomContainerMutatedProps extends VictoryZoomContainerProps { domain: ZoomDomain; originalDomain: ZoomDomain; currentDomain: ZoomDomain; cachedZoomDomain: ZoomDomain; scale: any; polar: boolean; origin: { x: number; y: number }; } export const VICTORY_ZOOM_CONTAINER_DEFAULT_PROPS = { clipContainerComponent: , allowPan: true, allowZoom: true, zoomActive: false, }; export const useVictoryZoomContainer = ( initialProps: VictoryZoomContainerProps, ) => { const props = { ...VICTORY_ZOOM_CONTAINER_DEFAULT_PROPS, ...(initialProps as VictoryZoomContainerMutatedProps), }; const { children, currentDomain, zoomActive, allowZoom, downsample, scale, clipContainerComponent, polar, origin, horizontal, } = props; const downsampleZoomData = (child: React.ReactElement, domain) => { const getData = (childProps) => { const { data, x, y } = childProps; const defaultGetData = child.type && typeof (child.type as any).getData === "function" ? (child.type as any).getData : () => undefined; // skip costly data formatting if x and y accessors are not present return Array.isArray(data) && !x && !y ? data : defaultGetData(childProps); }; const data = getData(child.props); // return undefined if downsample is not run, then default() will replace with child.props.data if (!downsample || !domain || !data) { return undefined; } const maxPoints = downsample === true ? DEFAULT_DOWNSAMPLE : downsample; const dimension = props.zoomDimension || "x"; // important: assumes data is ordered by dimension // get the start and end of the data that is in the current visible domain let startIndex = data.findIndex( (d) => d[dimension] >= domain[dimension][0], ); let endIndex = data.findIndex((d) => d[dimension] > domain[dimension][1]); // pick one more point (if available) at each end so that VictoryLine, VictoryArea connect if (startIndex !== 0) { startIndex -= 1; } if (endIndex !== -1) { endIndex += 1; } const visibleData = data.slice(startIndex, endIndex); return Data.downsample(visibleData, maxPoints, startIndex); }; const modifiedChildren = ( React.Children.toArray(children) as React.ReactElement[] ).map((child) => { const role = (child as any).type && (child as any).type.role; const isDataComponent = Data.isDataComponent(child); const originalDomain = defaults({}, props.originalDomain, props.domain); const zoomDomain = defaults({}, props.zoomDomain, props.domain); const cachedZoomDomain = defaults({}, props.cachedZoomDomain, props.domain); let domain: ZoomDomain; if (!ZoomHelpers.checkDomainEquality(zoomDomain, cachedZoomDomain)) { // if zoomDomain has been changed, use it domain = zoomDomain; } else if (allowZoom && !zoomActive) { // if user has zoomed all the way out, use the child domain domain = child.props.domain; } else { // default: use currentDomain, set by the event handlers domain = defaults({}, currentDomain, originalDomain); } let newDomain = props.polar ? { x: originalDomain.x, y: [0, domain.y[1]], } : domain; if (newDomain && props.zoomDimension) { // if zooming is restricted to a dimension, don't squash changes to zoomDomain in other dim newDomain = { ...zoomDomain, [props.zoomDimension]: newDomain[props.zoomDimension], }; } // don't downsample stacked data const childProps = isDataComponent && role !== "stack" ? { domain: newDomain, data: downsampleZoomData(child, newDomain), } : { domain: newDomain }; const newChild = React.cloneElement( child, defaults(childProps, child.props), ); // Clip data components if (Data.isDataComponent(newChild)) { const rangeX = horizontal ? scale.y.range() : scale.x.range(); const rangeY = horizontal ? scale.x.range() : scale.y.range(); const plottableWidth = Math.abs(rangeX[0] - rangeX[1]); const plottableHeight = Math.abs(rangeY[0] - rangeY[1]); const radius = Math.max(...rangeY); const groupComponent = React.cloneElement(clipContainerComponent, { clipWidth: plottableWidth, clipHeight: plottableHeight, translateX: Math.min(...rangeX), translateY: Math.min(...rangeY), polar, origin: polar ? origin : undefined, radius: polar ? radius : undefined, ...clipContainerComponent.props, }); return React.cloneElement(newChild, { groupComponent, }); } return newChild; }); return { props, children: modifiedChildren }; }; export const VictoryZoomContainer = ( initialProps: VictoryZoomContainerProps, ) => { const { props, children } = useVictoryZoomContainer(initialProps); return {children}; }; VictoryZoomContainer.role = "container"; VictoryZoomContainer.defaultEvents = ( initialProps: VictoryZoomContainerProps, ) => { const props = { ...VICTORY_ZOOM_CONTAINER_DEFAULT_PROPS, ...initialProps }; const createEventHandler = (handler: VictoryEventHandler, disabled?: boolean): VictoryEventHandler => // eslint-disable-next-line max-params (event, targetProps, eventKey, context) => disabled || props.disable ? {} : handler(event, { ...props, ...targetProps }, eventKey, context); return [ { target: "parent", eventHandlers: { onMouseDown: createEventHandler(ZoomHelpers.onMouseDown), onTouchStart: createEventHandler(ZoomHelpers.onMouseDown), onMouseUp: createEventHandler(ZoomHelpers.onMouseUp), onTouchEnd: createEventHandler(ZoomHelpers.onMouseUp), onMouseLeave: createEventHandler(ZoomHelpers.onMouseLeave), onTouchCancel: createEventHandler(ZoomHelpers.onMouseLeave), onMouseMove: createEventHandler(ZoomHelpers.onMouseMove), onTouchMove: createEventHandler(ZoomHelpers.onMouseMove), onWheel: createEventHandler(ZoomHelpers.onWheel, !props.allowZoom), }, }, ]; }; ================================================ FILE: packages/victory-zoom-container/src/zoom-helpers.test.ts ================================================ import { RawZoomHelpers } from "./zoom-helpers"; describe("RawZoomHelpers.getMinimumDomain", () => { afterEach(() => { jest.restoreAllMocks(); }); it("should be calculating the minimum domain", () => { jest.spyOn(RawZoomHelpers, "getDomain").mockImplementation(() => ({ x: [0, 100], })); expect( RawZoomHelpers.getMinimumDomain(30, { minimumZoom: true }, "x"), ).toStrictEqual([29.95, 30.05]); }); }); describe("RawZoomHelpers.getScaledDomain", () => { it("should scale the domain correctly with a zoom in factor", () => { expectToBeCloseToArray( RawZoomHelpers.getScaledDomain([0, 100], 0.9, 0.5), [5, 95], ); }); it("should scale the domain correctly with a zoom out factor", () => { expectToBeCloseToArray( RawZoomHelpers.getScaledDomain([0, 100], 1.1, 0.5), [-5, 105], ); }); }); describe("RawZoomHelpers.scale", () => { afterEach(() => { jest.restoreAllMocks(); }); it("should get the correct domain", () => { jest.spyOn(RawZoomHelpers, "getDomain").mockImplementation(() => ({ x: [0, 100], })); jest.spyOn(RawZoomHelpers, "getScalePercent").mockImplementation(() => 0.5); jest .spyOn(RawZoomHelpers, "getMinimumDomain") .mockImplementation(() => [29.955, 30.045]); expectToBeCloseToArray( RawZoomHelpers.scale([0, 100], { deltaY: -1 }, {}, "x"), [0.166, 99.833], ); }); it("should't change the domain when zooming out with max zoom out", () => { jest.spyOn(RawZoomHelpers, "getDomain").mockImplementation(() => ({ x: [0, 100], })); jest.spyOn(RawZoomHelpers, "getScalePercent").mockImplementation(() => 0.1); jest .spyOn(RawZoomHelpers, "getMinimumDomain") .mockImplementation(() => [29.955, 30.045]); expectToBeCloseToArray( RawZoomHelpers.scale([0, 100], { deltaY: 1 }, {}, "x"), [0, 100], ); }); it("should't change the domain when zooming out with max zoom out with the cursor outside the container boundary", () => { jest.spyOn(RawZoomHelpers, "getDomain").mockImplementation(() => ({ x: [0, 100], })); jest .spyOn(RawZoomHelpers, "getScalePercent") .mockImplementation(() => -0.1); jest .spyOn(RawZoomHelpers, "getMinimumDomain") .mockImplementation(() => [29.955, 30.045]); expectToBeCloseToArray( RawZoomHelpers.scale([0, 100], { deltaY: 1 }, {}, "x"), [0, 100], ); }); }); function expectToBeCloseToArray(actual, expected) { expect(actual.length).toBe(expected.length); actual.forEach((x, i) => expect(x).toBeCloseTo(expected[i])); } ================================================ FILE: packages/victory-zoom-container/src/zoom-helpers.ts ================================================ /* eslint no-magic-numbers: ["error", { "ignore": [-1, 0, 1, 2, 1000] }]*/ import { Children } from "react"; import defaults from "lodash/defaults"; import delay from "lodash/delay"; import throttle from "lodash/throttle"; import { Helpers, Selection, Collection, Wrapper } from "victory-core"; export const RawZoomHelpers = { checkDomainEquality(a, b) { const checkDimension = (dim) => { const val1 = a && a[dim]; const val2 = b && b[dim]; if (!val1 && !val2) { return true; } else if (!val1 || !val2) { return false; } return ( Number(val1[0]) === Number(val2[0]) && Number(val1[1]) === Number(val2[1]) ); }; return checkDimension("x") && checkDimension("y"); }, /** * Generates a new domain scaled by factor and constrained by the original domain. * @param {[Number, Number]} currentDomain The domain to be scaled. * @param {Object} evt the event object * @param {Object} props the props of the targeted component * @param {String} axis the desired dimension (either x or y) * @return {[Number, Number]} The scale domain */ // eslint-disable-next-line max-params scale(currentDomain, evt, props, axis) { const [from, to] = currentDomain; const range = Math.abs(to - from); const minimumZoom = props.minimumZoom && props.minimumZoom[axis]; const factor = this.getScaleFactor(evt); if (minimumZoom && range <= minimumZoom && factor < 1) { return currentDomain; } const [fromBound, toBound] = this.getDomain(props)[axis]; const percent = this.getScalePercent(evt, props, axis); const point = factor * from + percent * (factor * range); const minDomain = this.getMinimumDomain(point, props, axis); const [newMin, newMax] = this.getScaledDomain( currentDomain, factor, percent, ); const newDomain = [ newMin > fromBound && newMin < toBound ? newMin : fromBound, newMax < toBound && newMax > fromBound ? newMax : toBound, ]; const domain = Math.abs(minDomain[1] - minDomain[0]) > Math.abs(newDomain[1] - newDomain[0]) ? minDomain : newDomain; return Collection.containsDates([fromBound, toBound]) ? [new Date(domain[0]), new Date(domain[1])] : domain; }, getScaledDomain(currentDomain, factor, percent) { const [from, to] = currentDomain; const range = Math.abs(to - from); const diff = range - range * factor; const newMin = Number(from) + diff * Math.max(percent, 0); const newMax = Number(to) - diff * Math.max(1 - percent, 0); return [Math.min(newMin, newMax), Math.max(newMin, newMax)]; }, getMinimumDomain(point, props, axis) { const { minimumZoom } = props; const originalDomain = this.getDomain(props)[axis]; const [from, to] = originalDomain; const defaultMin = Math.abs(from - to) / 1000; const extent = minimumZoom ? minimumZoom[axis] || defaultMin : defaultMin; const minExtent = point - extent / 2; const maxExtent = point + extent / 2; return [ minExtent > from && minExtent < to ? minExtent : from, maxExtent < to && maxExtent > from ? maxExtent : Number(from) + extent / 2, ]; }, zoommingOut(evt) { return evt.deltaY > 0; }, getScaleFactor(evt) { const sign = this.zoommingOut(evt) ? 1 : -1; // eslint-disable-next-line no-magic-numbers const delta = Math.min(Math.abs(evt.deltaY / 300), 0.5); // TODO: Check scale factor return Math.abs(1 + sign * delta); }, getScalePercent(evt, props, axis) { const originalDomain = this.getDomain(props); const [from, to] = originalDomain[axis]; const position = this.getPosition(evt, props, originalDomain); return (position[axis] - from) / Math.abs(to - from); }, getPosition(evt, props, originalDomain) { const { x, y } = Selection.getSVGEventCoordinates(evt); const originalScale = { x: props.scale.x.domain(originalDomain.x), y: props.scale.y.domain(originalDomain.y), }; return Selection.getDataCoordinates(props, originalScale, x, y); }, /** * Generate a new domain translated by the delta and constrained by the original domain. * @param {[Number, Number]} currentDomain The domain to be translated. * @param {[Number, Number]} originalDomain The original domain for the data set. * @param {Number} delta The delta to translate by * @return {[Number, Number]} The translated domain */ pan(currentDomain, originalDomain, delta) { const [fromCurrent, toCurrent] = currentDomain.map((val) => Number(val)); const [fromOriginal, toOriginal] = originalDomain.map((val) => Number(val)); const lowerBound = fromCurrent + delta; const upperBound = toCurrent + delta; let newDomain; if (lowerBound > fromOriginal && upperBound < toOriginal) { newDomain = [lowerBound, upperBound]; } else if (lowerBound < fromOriginal) { // Clamp to lower limit const dx = toCurrent - fromCurrent; newDomain = [fromOriginal, fromOriginal + dx]; } else if (upperBound > toOriginal) { // Clamp to upper limit const dx = toCurrent - fromCurrent; newDomain = [toOriginal - dx, toOriginal]; } else { newDomain = currentDomain; } return Collection.containsDates(currentDomain) || Collection.containsDates(originalDomain) ? newDomain.map((val) => new Date(val)) : newDomain; }, getDomainScale(domain, scale, axis) { const axisDomain = Array.isArray(domain) ? domain : domain[axis]; const [from, to] = axisDomain; const range = scale[axis].range(); const plottableWidth = Math.abs(range[0] - range[1]); return plottableWidth / (to - from); }, handleAnimation(ctx) { const animationTimer = ctx.context.animationTimer; const transitionTimer = ctx.context.transitionTimer; transitionTimer.bypassAnimation(); animationTimer.bypassAnimation(); const resumeAnimation = () => { animationTimer.resumeAnimation(); transitionTimer.resumeAnimation(); }; // delay the callback that resumes animation by ~1 frame so that animation does not interfere with wheel events return delay(resumeAnimation, 16); // eslint-disable-line no-magic-numbers }, getLastDomain(targetProps, originalDomain) { const { zoomDomain, cachedZoomDomain, currentDomain, domain } = targetProps; if (zoomDomain && !this.checkDomainEquality(zoomDomain, cachedZoomDomain)) { return defaults({}, zoomDomain, domain); } return defaults({}, currentDomain || zoomDomain || originalDomain, domain); }, getDomain(props) { const { originalDomain, domain, children, zoomDimension } = props; const childComponents = Children.toArray(children); let childrenDomain = {}; if (childComponents.length) { childrenDomain = zoomDimension ? { [zoomDimension]: Wrapper.getDomainFromChildren( props, zoomDimension, childComponents, ), } : { x: Wrapper.getDomainFromChildren(props, "x", childComponents), y: Wrapper.getDomainFromChildren(props, "y", childComponents), }; } return defaults({}, childrenDomain, originalDomain, domain); }, onMouseDown(evt, targetProps) { evt.preventDefault(); if (!targetProps.allowPan) { return undefined; } const parentSVG = targetProps.parentSVG || Selection.getParentSVG(evt); const { x, y } = Selection.getSVGEventCoordinates(evt, parentSVG); return [ { target: "parent", mutation: () => { return { startX: x, startY: y, panning: true, parentSVG, parentControlledProps: ["domain"], }; }, }, ]; }, onMouseUp(evt, targetProps) { if (!targetProps.allowPan) { return undefined; } return [ { target: "parent", mutation: () => { return { panning: false }; }, }, ]; }, onMouseLeave(evt, targetProps) { if (!targetProps.allowPan) { return undefined; } return [ { target: "parent", mutation: () => { return { panning: false }; }, }, ]; }, // eslint-disable-next-line max-params onMouseMove(evt, targetProps, eventKey, ctx) { if (targetProps.panning && targetProps.allowPan) { const { scale, startX, startY, onZoomDomainChange, zoomDomain, zoomDimension, horizontal, } = targetProps; const parentSVG = targetProps.parentSVG || Selection.getParentSVG(evt); const { x, y } = Selection.getSVGEventCoordinates(evt, parentSVG); const originalDomain = this.getDomain(targetProps); const lastDomain = this.getLastDomain(targetProps, originalDomain); const deltaX = horizontal ? y - startY : startX - x; const deltaY = horizontal ? startX - x : y - startY; const dx = deltaX / this.getDomainScale(lastDomain, scale, "x"); const dy = deltaY / this.getDomainScale(lastDomain, scale, "y"); const currentDomain = { x: zoomDimension === "y" ? originalDomain.x : this.pan(lastDomain.x, originalDomain.x, dx), y: zoomDimension === "x" ? originalDomain.y : this.pan(lastDomain.y, originalDomain.y, dy), }; const resumeAnimation = this.handleAnimation(ctx); const zoomActive = !this.checkDomainEquality(originalDomain, lastDomain); const mutatedProps = { parentControlledProps: ["domain"], startX: x, startY: y, parentSVG, currentDomain, originalDomain, cachedZoomDomain: zoomDomain, zoomActive, }; if (Helpers.isFunction(onZoomDomainChange)) { onZoomDomainChange( currentDomain, defaults({}, mutatedProps, targetProps), ); } return [ { target: "parent", callback: resumeAnimation, mutation: () => mutatedProps, }, ]; } return undefined; }, // eslint-disable-next-line max-params onWheel(evt, targetProps, eventKey, ctx) { if (!targetProps.allowZoom) { return undefined; } const { onZoomDomainChange, zoomDimension, zoomDomain } = targetProps; const originalDomain = this.getDomain(targetProps); const lastDomain = this.getLastDomain(targetProps, originalDomain); const { x, y } = lastDomain; const currentDomain = { x: zoomDimension === "y" ? lastDomain.x : this.scale(x, evt, targetProps, "x"), y: zoomDimension === "x" ? lastDomain.y : this.scale(y, evt, targetProps, "y"), }; const resumeAnimation = this.handleAnimation(ctx); const zoomActive = !this.zoommingOut(evt) || // if zoomming in or // if zoomActive is already set AND user hasn't zoommed out all the way (targetProps.zoomActive && !this.checkDomainEquality(originalDomain, lastDomain)); const mutatedProps = { currentDomain, originalDomain, cachedZoomDomain: zoomDomain, parentControlledProps: ["domain"], panning: false, zoomActive, }; if (Helpers.isFunction(onZoomDomainChange)) { onZoomDomainChange( currentDomain, defaults({}, mutatedProps, targetProps), ); } return [ { target: "parent", callback: resumeAnimation, mutation: () => mutatedProps, }, ]; }, }; export const ZoomHelpers = { checkDomainEquality: RawZoomHelpers.checkDomainEquality.bind(RawZoomHelpers), onMouseDown: RawZoomHelpers.onMouseDown.bind(RawZoomHelpers), onMouseUp: RawZoomHelpers.onMouseUp.bind(RawZoomHelpers), onMouseLeave: RawZoomHelpers.onMouseLeave.bind(RawZoomHelpers), onMouseMove: throttle( RawZoomHelpers.onMouseMove.bind(RawZoomHelpers), 16, // eslint-disable-line no-magic-numbers { leading: true, trailing: false }, ), onWheel: throttle( RawZoomHelpers.onWheel.bind(RawZoomHelpers), 16, // eslint-disable-line no-magic-numbers { leading: true, trailing: false }, ), }; ================================================ FILE: packages/victory-zoom-container/tsconfig.build.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["**/*.test.*", "es", "lib", "jest.config.ts"] } ================================================ FILE: packages/victory-zoom-container/tsconfig.json ================================================ { "extends": "../../tsconfig.base.json", "exclude": ["es", "lib", "jest.config.ts"] } ================================================ FILE: patches/@changesets__cli@2.24.1.patch ================================================ diff --git a/dist/cli.cjs.dev.js b/dist/cli.cjs.dev.js index 55665d53b92288d89b71aab0c231b82bb8cb5ff1..a24dc555b52e95a2cb1232d8c8a0a37e7dbd3fcf 100644 --- a/dist/cli.cjs.dev.js +++ b/dist/cli.cjs.dev.js @@ -807,7 +807,6 @@ async function internalPublish(pkgName, opts, twoFactorState) { } // Due to a super annoying issue in yarn, we have to manually override this env variable // See: https://github.com/yarnpkg/yarn/issues/2935#issuecomment-355292633 - const envOverride = { npm_config_registry: getCorrectRegistry() }; @@ -921,8 +920,9 @@ async function publishPackages({ } return Promise.all(unpublishedPackagesInfo.map(pkgInfo => { - let pkg = packagesByName.get(pkgInfo.name); - return publishAPackage(pkg, access, twoFactorState, getReleaseTag(pkgInfo, preState, tag)); + const pkg = packagesByName.get(pkgInfo.name); + const publishTag = (pkg.packageJson.publishConfig && pkg.packageJson.publishConfig.tag) || tag; + return publishAPackage(pkg, access, twoFactorState, getReleaseTag(pkgInfo, preState, publishTag)); })); } @@ -933,7 +933,7 @@ async function publishAPackage(pkg, access, twoFactorState, tag) { publishConfig } = pkg.packageJson; const localAccess = publishConfig === null || publishConfig === void 0 ? void 0 : publishConfig.access; - logger.info(`Publishing ${chalk__default['default'].cyan(`"${name}"`)} at ${chalk__default['default'].green(`"${version}"`)}`); + logger.info(`Publishing ${chalk__default['default'].cyan(`"${name}"`)} at ${chalk__default['default'].green(`"${version}@${tag}"`)}`); const publishDir = (publishConfig === null || publishConfig === void 0 ? void 0 : publishConfig.directory) ? path.join(pkg.dir, publishConfig.directory) : pkg.dir; const publishConfirmation = await publish(name, { cwd: publishDir, @@ -1221,6 +1221,7 @@ async function run$1(cwd) { } async function run$2(input, flags, cwd) { + logger.warn("This is a patched version of @changesets/cli"); if (input[0] === "init") { await init(cwd); return; diff --git a/dist/cli.cjs.prod.js b/dist/cli.cjs.prod.js index fa6dd7c6365b814e44a253c4868b315de481d691..05f6f8beb9d2e26730bdb9450a6e68abedee7633 100644 --- a/dist/cli.cjs.prod.js +++ b/dist/cli.cjs.prod.js @@ -508,13 +508,20 @@ async function publishPackages({packages: packages, access: access, otp: otp, pr otp: otp, publicPackages: publicPackages }), unpublishedPackagesInfo = await getUnpublishedPackages(publicPackages, preState); - return 0 === unpublishedPackagesInfo.length && logger.warn("No unpublished packages to publish"), - Promise.all(unpublishedPackagesInfo.map((pkgInfo => publishAPackage(packagesByName.get(pkgInfo.name), access, twoFactorState, getReleaseTag(pkgInfo, preState, tag))))); + if (unpublishedPackagesInfo.length === 0) { + warn("No unpublished packages to publish"); + } + + return Promise.all(unpublishedPackagesInfo.map(pkgInfo => { + const pkg = packagesByName.get(pkgInfo.name); + const publishTag = (pkg.packageJson.publishConfig && pkg.packageJson.publishConfig.tag) || tag; + return publishAPackage(pkg, access, twoFactorState, getReleaseTag(pkgInfo, preState, publishTag)); + })); } async function publishAPackage(pkg, access, twoFactorState, tag) { const {name: name, version: version, publishConfig: publishConfig} = pkg.packageJson, localAccess = null == publishConfig ? void 0 : publishConfig.access; - logger.info(`Publishing ${chalk__default.default.cyan(`"${name}"`)} at ${chalk__default.default.green(`"${version}"`)}`); + logger.info(`Publishing ${chalk__default['default'].cyan(`"${name}"`)} at ${chalk__default['default'].green(`"${version}@${tag}"`)}`); const publishDir = (null == publishConfig ? void 0 : publishConfig.directory) ? path.join(pkg.dir, publishConfig.directory) : pkg.dir; return { name: name, @@ -661,6 +668,7 @@ async function run$1(cwd) { } async function run$2(input, flags, cwd) { + logger.warn("This is a patched version of @changesets/cli"); if ("init" === input[0]) return void await init(cwd); if (!fs__default.default.existsSync(path__default.default.resolve(cwd, ".changeset"))) throw logger.error("There is no .changeset folder. "), logger.error("If this is the first time `changesets` have been used in this project, run `yarn changeset init` to get set up."), diff --git a/dist/cli.esm.js b/dist/cli.esm.js index 4e319be77ee8a563803649324b7fc364bc05be6b..cb2a2bef5c4f75a2cfe59ce7f402078d89f1f778 100644 --- a/dist/cli.esm.js +++ b/dist/cli.esm.js @@ -784,7 +784,6 @@ async function internalPublish(pkgName, opts, twoFactorState) { } // Due to a super annoying issue in yarn, we have to manually override this env variable // See: https://github.com/yarnpkg/yarn/issues/2935#issuecomment-355292633 - const envOverride = { npm_config_registry: getCorrectRegistry() }; @@ -898,8 +897,9 @@ async function publishPackages({ } return Promise.all(unpublishedPackagesInfo.map(pkgInfo => { - let pkg = packagesByName.get(pkgInfo.name); - return publishAPackage(pkg, access, twoFactorState, getReleaseTag(pkgInfo, preState, tag)); + const pkg = packagesByName.get(pkgInfo.name); + const publishTag = (pkg.packageJson.publishConfig && pkg.packageJson.publishConfig.tag) || tag; + return publishAPackage(pkg, access, twoFactorState, getReleaseTag(pkgInfo, preState, publishTag)); })); } @@ -910,7 +910,7 @@ async function publishAPackage(pkg, access, twoFactorState, tag) { publishConfig } = pkg.packageJson; const localAccess = publishConfig === null || publishConfig === void 0 ? void 0 : publishConfig.access; - info(`Publishing ${chalk.cyan(`"${name}"`)} at ${chalk.green(`"${version}"`)}`); + info(`Publishing ${chalk.cyan(`"${name}"`)} at ${chalk.green(`"${version}@${tag}"`)}`); const publishDir = (publishConfig === null || publishConfig === void 0 ? void 0 : publishConfig.directory) ? join(pkg.dir, publishConfig.directory) : pkg.dir; const publishConfirmation = await publish(name, { cwd: publishDir, @@ -1198,6 +1198,7 @@ async function run$1(cwd) { } async function run$2(input, flags, cwd) { + logger.warn("This is a patched version of @changesets/cli"); if (input[0] === "init") { await init(cwd); return; ================================================ FILE: pnpm-workspace.yaml ================================================ packages: - 'packages/**' - 'website' - 'demo/rn' ================================================ FILE: scripts/changelog.js ================================================ const { default: changelogFunctions, } = require("@svitejs/changesets-changelog-github-compact"); const customFunctions = { ...changelogFunctions, getDependencyReleaseLine: async () => "", }; module.exports = { ...customFunctions, default: customFunctions, }; ================================================ FILE: scripts/release.ts ================================================ /** * The following code is adapted from a fork of the `changesets/actions` repository * to allow us to generate aggregated changelogs for our monorepo. * This code can be removed when changesets supports monorepo aggregations. */ /* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable no-console */ import fs from "fs-extra"; import path from "path"; import unified from "unified"; import remarkParse from "remark-parse"; import remarkStringify from "remark-stringify"; // @ts-ignore import mdastToString from "mdast-util-to-string"; import { Octokit } from "octokit"; export const BumpLevels = { dep: 0, patch: 1, minor: 2, major: 3, } as const; export function getChangelogEntry(changelog: string, version: string) { const ast = unified().use(remarkParse).parse(changelog) as any; let highestLevel: number = BumpLevels.dep; const nodes = ast.children as Array; let headingStartInfo: | { index: number; depth: number; } | undefined; let endIndex: number | undefined; for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; if (node.type === "heading") { const stringified: string = mdastToString(node); const match = stringified.toLowerCase().match(/(major|minor|patch)/); if (match !== null) { const level = BumpLevels[match[0] as "major" | "minor" | "patch"]; highestLevel = Math.max(level, highestLevel); } if (headingStartInfo === undefined && stringified === version) { headingStartInfo = { index: i, depth: node.depth, }; continue; } if ( endIndex === undefined && headingStartInfo !== undefined && headingStartInfo.depth === node.depth ) { endIndex = i; break; } } } if (headingStartInfo) { ast.children = ast.children.slice(headingStartInfo.index + 1, endIndex); } return { content: unified().use(remarkStringify).stringify(ast), highestLevel, }; } const createAggregatedRelease = async () => { const octokit = new Octokit({ auth: process.env.CHANGESETS_GITHUB_TOKEN }); const prevRelease = await octokit.rest.repos.getLatestRelease({ owner: "FormidableLabs", repo: "victory", }); const pkgFileName = path.join("packages", "victory", "package.json"); const packageJson = JSON.parse(await fs.readFile(pkgFileName, "utf8")); const changelogFileName = path.join("packages", "victory", "CHANGELOG.md"); const changelog = await fs.readFile(changelogFileName, "utf8"); const changelogEntry = getChangelogEntry(changelog, packageJson.version); if (!changelogEntry) { // we can find a changelog but not the entry for this version // if this is true, something has probably gone wrong throw new Error( `Could not find changelog entry for ${packageJson.name}@${packageJson.version}`, ); } const tag_name = `v${packageJson.version}`; const prevTag = prevRelease.data.tag_name; const link = `Full Changelog: [${prevTag}...${tag_name}](https://github.com/FormidableLabs/victory/compare/${prevTag}...${tag_name})`; const body = `## What's Changed\n\n${changelogEntry.content}\n\n${link}`; await octokit.rest.repos.createRelease({ owner: "FormidableLabs", repo: "victory", tag_name, name: tag_name, body, prerelease: false, }); }; (async () => { await createAggregatedRelease(); })() .then(() => console.log("Release created!")) .catch(console.error); ================================================ FILE: scripts/sync-pkgs-wireit-helpers.js ================================================ function unique(arr) { return [...new Set(arr)]; } function concat(...arrays) { return unique([].concat(...arrays)); } function generateWireitConfig(pkg, rootPkg) { const isVictoryPackage = (p) => p.startsWith("victory") && p !== pkg.name; const deps = Object.keys(pkg.dependencies || {}).filter(isVictoryPackage); const devDeps = Object.keys(pkg.devDependencies || {}).filter( isVictoryPackage, ); const rootDeps = Object.keys(rootPkg.devDependencies).filter( isVictoryPackage, ); // Lint require victory-vendor (if dependend) to be built const lintDeps = [ // victory-vendor has nested path accesses, which means it needs // to be built for lint to not error on it if a dependency. ...(deps.includes("victory-vendor") ? ["../victory-vendor:build"] : []), ...concat(deps, devDeps, rootDeps).map((dep) => `../${dep}:types:create`), ]; // We want this block to look like JSON, so disable prettier: // prettier-ignore return { "scripts": { "### THESE SCRIPTS ARE GENERATED ###": "true", "### DO NOT MODIFY THESE MANUALLY ###": "true", "build": "wireit", "build:lib": "wireit", "build:lib:esm": "wireit", "build:lib:cjs": "wireit", "build:dist": "wireit", "build:dist:dev": "wireit", "build:dist:min": "wireit", "check": "wireit", "types:check": "wireit", "types:create": "wireit", "lint": "wireit", "lint:fix": "wireit", "jest": "wireit", }, "engines": { "node": ">=18.0.0" }, "wireit": { "### THESE WIREIT CONFIGS ARE GENERATED ####": {}, "### DO NOT MODIFY THESE MANUALLY ####": {}, "build": { "dependencies": [ "build:lib", "build:dist", "types:create" ], }, "build:lib": { "dependencies": [ "build:lib:esm", "build:lib:cjs" ], }, "build:lib:esm": { "command": "nps build:lib:esm", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", ], "output": [ "es/**/*.js", "es/**/*.js.map" ], "dependencies": [ ...deps.map((dep) => `../${dep}:build:lib:esm`) ], "packageLocks": ["pnpm-lock.yaml"] }, "build:lib:cjs": { "command": "nps build:lib:cjs", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", ], "output": [ "lib/**/*.js", "lib/**/*.js.map" ], "dependencies": [ ...deps.map((dep) => `../${dep}:build:lib:cjs`) ], "packageLocks": ["pnpm-lock.yaml"] }, "build:dist": { "dependencies": [ "build:dist:dev", "build:dist:min" ], }, "build:dist:dev": { "command": "nps build:dist:dev", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", "../../config/webpack.config.dev.js", ], "output": [ "dist/victory*.js", "!dist/victory*.min.js*", ], // Webpack uses: // 1. Source (`src`) of package being built. // 2. The ESM version of dependencies specified in our // `package.json:module` fields. "dependencies": [ ...deps.map((dep) => `../${dep}:build:lib:esm`) ], "packageLocks": ["pnpm-lock.yaml"] }, "build:dist:min": { "command": "nps build:dist:min", "files": [ "src/**", "!src/**/*.test.*", "../../.babelrc.build.js", "../../.babelrc.js", "../../package-scripts.js", "../../config/webpack.config.js", ], "output": [ "dist/victory*.min.js*" ], "dependencies": [ ...deps.map((dep) => `../${dep}:build:lib:esm`) ], "packageLocks": ["pnpm-lock.yaml"] }, "check": { "dependencies": [ "types:check", "jest", "format", "lint", ] }, "types:check": { "command": "nps types:pkg:check", "files": [ "src/**/*.{ts,tsx}", "../../tsconfig.base.json", "tsconfig.json", ], "dependencies": [ "types:create", ...concat(deps, devDeps, rootDeps) .map((dep) => `../${dep}:types:create`), ], "output": [], "packageLocks": ["pnpm-lock.yaml"] }, "types:create": { "command": "nps types:pkg:create", "files": [ "src/**", "!src/**/*.test.*", "../../tsconfig.base.json", "tsconfig.build.json", ], "output": [ "es/**/*.d.ts", "es/**/*.d.ts.map", "lib/**/*.d.ts", "lib/**/*.d.ts.map", ], "dependencies": [ ...deps.map((dep) => `../${dep}:types:create`) ], "packageLocks": ["pnpm-lock.yaml"] }, // Same as above ...["lint", "lint:fix"].reduce((wireit, key) => { wireit[key] = { "command": key === "lint" ? "eslint src" : "eslint --fix src", "files": [ "src/**", ], "output": [], "dependencies": [ ...lintDeps, ], "packageLocks": ["pnpm-lock.yaml"] }; return wireit; }, {}), "jest": { "command": "jest --passWithNoTests", "files": [ "src/**/*.test.*", "../../.babelrc.js", "../../test/jest-config.js", "../../test/jest-setup.ts", ], "output": [], // Jest uses: // 1. Source (`src`) for actual test files within a package. // 2. The CommonJS (`lib`) versions of library files (dependencies // and the package at issue). "dependencies": [ ...concat(deps, devDeps, rootDeps).map((dep) => `../${dep}:build`), ], "packageLocks": ["pnpm-lock.yaml"] }, }, }; } module.exports = { generateWireitConfig }; ================================================ FILE: scripts/sync-pkgs-wireit.js ================================================ #!/usr/bin/env node /** * This helper script uses a template to mutate all library package.json's: * * 1. Adds all `scripts` and `wireit` configs to that package. * 2. Updates wireit config dependencies to match package.json dependencies. * * The script also adds `wireit` configs to the root package.json. * * Note that this script does _not_ mutate: * - victory-vendor * - victory-native * * If you are editing `victory-vendor` or `victory-native`, directly edit them. * For **all other packages**, make your changes in * `sync-pkgs-wireit-helpers.js`. */ const fs = require("fs/promises"); const path = require("path"); const { generateWireitConfig } = require("./sync-pkgs-wireit-helpers"); const { log, error } = console; // ============================================================================ // Config // ============================================================================ const ROOT = path.resolve(__dirname, ".."); const PKGS_ROOT = path.join(ROOT, "packages"); // Special packages const PKGS = { NATIVE: "victory-native", VENDOR: "victory-vendor", }; const SPECIAL_PKGS = new Set([PKGS.NATIVE, PKGS.VENDOR]); // ============================================================================ // Helpers // ============================================================================ const readPkg = async (pkgPath) => JSON.parse(await fs.readFile(pkgPath)); const writePkg = async (pkgPath, data, originalPkg) => { const json = JSON.stringify(data, null, 2); if (json === JSON.stringify(originalPkg, null, 2)) { log(`Skipping ${pkgPath} (no changes)`); return; } log(`Writing ${pkgPath}`); await fs.writeFile(pkgPath, `${json}\n`); }; const clone = (obj) => JSON.parse(JSON.stringify(obj)); const isVictoryPackage = (p) => p.startsWith("victory"); // Check for package locks, which we need to set on each wireit config // with non-empty `files`. const validateLocks = (pkgPath, pkg) => Object.entries(pkg.wireit).forEach(([key, obj]) => { if (obj.files && obj.files.length && !obj.packageLocks) { throw new Error( `Missing packageLocks for wireit config for: ${key} in ${pkgPath}`, ); } }); // Root mutation // // We want to use wireit directly to manage multi-build for better // cache hits (e.g. `pnpm -r run build` seems to get a lot of cache // misses). So create tasks with cross-package deps const updateRootPkg = async ({ allPkgs }) => { const rootPkgPath = `${ROOT}/package.json`; const originalPkg = await readPkg(rootPkgPath); const rootPkg = clone(originalPkg); rootPkg.wireit = rootPkg.wireit || {}; [ { rootTask: "build", pkgTask: "build" }, { rootTask: "build:lib:esm", pkgTask: "build:lib:esm" }, { rootTask: "jest:pkgs", pkgTask: "jest" }, { rootTask: "types:check", pkgTask: "types:check" }, { rootTask: "types:create", pkgTask: "types:create" }, ].forEach(({ rootTask, pkgTask }) => { rootPkg.wireit[rootTask] = rootPkg.wireit[rootTask] || {}; rootPkg.wireit[rootTask].dependencies = allPkgs.map( (p) => `./packages/${p}:${pkgTask}`, ); }); validateLocks(rootPkgPath, rootPkg); await writePkg(rootPkgPath, rootPkg, originalPkg); }; // Generate configurations for all packages: const updateLibPkgs = async ({ libPkgs }) => { const rootPkg = await readPkg(`${ROOT}/package.json`); for (const workspace of libPkgs) { const pkgPath = `${PKGS_ROOT}/${workspace}/package.json`; const originalPkg = await readPkg(pkgPath); const pkg = { ...originalPkg, ...generateWireitConfig(originalPkg, rootPkg), }; validateLocks(pkgPath, pkg); await writePkg(pkgPath, pkg, originalPkg); } }; // ============================================================================ // Script // ============================================================================ const cli = async () => { // Get packages. const libPkgs = (await fs.readdir(PKGS_ROOT)).filter( (p) => isVictoryPackage(p) && !SPECIAL_PKGS.has(p), ); const allPkgs = [...SPECIAL_PKGS, ...libPkgs]; // Mutate package.json's await updateRootPkg({ allPkgs }); await updateLibPkgs({ libPkgs }); log("Finished syncing."); }; if (require.main === module) { cli().catch((err) => { error(err); process.exit(1); }); } module.exports = { cli, }; ================================================ FILE: stories/utils/arg-types.tsx ================================================ import React from "react"; import { VictoryLabel, VictoryTheme, VictoryTooltip } from "@/victory"; export const VictoryAxisCommonProps = { dependentAxis: { control: "boolean" }, invertAxis: { control: "boolean" }, tickCount: { control: "number" }, // disable changing these values axisComponent: { control: false }, axisLabelComponent: { control: false }, axisValue: { control: false }, disableInlineStyles: { control: false }, gridComponent: { control: false }, style: { control: false }, tickComponent: { control: false }, tickFormat: { control: false }, tickLabelComponent: { control: false }, tickValues: { control: false }, } as const; export const VictoryCommonThemeProps = { animate: { control: "boolean" }, colorScale: { control: "select", options: [ "greyscale", "qualitative", "heatmap", "warm", "cool", "red", "green", "blue", ], }, disableInlineStyles: { control: "boolean" }, height: { control: "number" }, horizontal: { control: "boolean" }, name: { control: "text" }, padding: { control: "text" }, polar: { control: "boolean" }, standalone: { control: "boolean" }, width: { control: "number" }, // disable changing these values containerComponent: { control: false }, groupComponent: { control: false }, externalEventMutations: { control: false }, domainPadding: { control: false }, maxDomain: { control: false }, minDomain: { control: false }, origin: { control: false }, range: { control: false }, scale: { control: false }, sharedEvents: { control: false }, singleQuadrantDomainPadding: { control: false }, } as const; export const VictoryCommonProps = { ...VictoryCommonThemeProps, // disable changing these values theme: { control: false }, // custom control to allow us to hoist the theme to the parent // chart for better control of rendering themeKey: { control: "select", options: Object.keys(VictoryTheme) }, } as const; export const VictoryContainerProps = { "aria-describedby": { control: "text" }, "aria-labelledby": { control: "text" }, className: { control: "text" }, containerId: { control: "text" }, desc: { control: "text" }, height: { control: "number" }, name: { control: "text" }, ouiaId: { control: "text" }, ouiaSafe: { control: "boolean" }, ouiaType: { control: "text" }, polar: { control: "boolean" }, portalZIndex: { control: "number" }, preserveAspectRatio: { control: "text" }, responsive: { control: "boolean" }, role: { control: "text" }, tabIndex: { control: "number" }, title: { control: "text" }, width: { control: "number" }, } as const; export const VictoryDatableProps = { data: { control: "object" }, samples: { control: "number" }, sortKey: { control: "text" }, sortOrder: { control: "select", options: ["ascending", "descending"] }, // disable changing these values categories: { control: false }, dataComponent: { control: false }, domain: { control: false }, domainPadding: { control: false }, x: { control: false }, y: { control: false }, y0: { control: false }, } as const; export const VictoryLabelableProps = { labelComponent: { options: ["Label", "Tooltip"], mapping: { Label: , Tooltip: , }, }, } as const; export const VictoryMultiLabelableProps = { ...VictoryLabelableProps, // NOTE: controls not yet supported, but left for clarity // labels: { control: "text" }, } as const; export const VictorySingleLabelableProps = { ...VictoryLabelableProps, // NOTE: controls not yet supported, but left for clarity // label: { control: "text" }, } as const; ================================================ FILE: stories/utils/data.ts ================================================ import range from "lodash/range"; import seedrandom from "seedrandom"; export const getArrayData = (num: number, samples = 10) => { const seed = "getData"; const baseSeed = seedrandom(seed); const rand = () => baseSeed.quick() * 10; return range(num).map((v) => { return { x: v + 1, y: range(samples).map(() => rand()), }; }); }; export const getBoxPlotData = (num, seed = "getData") => { const baseSeed = seedrandom(seed); const rand = () => Math.round(1 + baseSeed.quick() * 5); return range(num).map((v) => { const min = rand(); const q1 = min + rand(); const median = q1 + rand(); const q3 = median + rand(); const max = q3 + rand(); return { x: v + 1, y: v + 1, min, q1, median, q3, max }; }); }; export const getBoxPlotRepeatData = (num, samples = 10) => { const seed = "getRepeatData"; const baseSeed = seedrandom(seed); const rand = () => baseSeed.quick() * 10; return range(num).reduce((memo, curr) => { const sampleData = range(samples).map(() => ({ x: curr + 1, y: rand(), })); return memo.concat(sampleData); }, [] as any); }; export const getCandlestickData = (num, seed = "getData") => { const baseSeed = seedrandom(seed); return range(num).map((v) => { const low = 2 + baseSeed.quick() * 5; const open = low + baseSeed.quick() * 5; const close = low + baseSeed.quick() * 5; const high = Math.max(open, close) + baseSeed.quick() * 5; return { x: v + 1, high, low, open, close }; }); }; export const getCandlestickTimeData = (num, seed = "getTimeData") => { const baseSeed = seedrandom(seed); const current = 1523389495000; return range(num).map((v) => { const low = 2 + baseSeed.quick() * 5; const open = low + baseSeed.quick() * 5; const close = low + baseSeed.quick() * 5; const high = Math.max(open, close) + baseSeed.quick() * 5; return { x: new Date((current / num) * (v + 1)), high, low, open, close, }; }); }; export const getData = (num: number, seed = "getData", max = 10) => { const baseSeed = seedrandom(seed); const rand = () => baseSeed.quick() * max; return range(num).map((v) => ({ x: v + 1, y: rand() })); }; export const getDataWithBaseline = ( num: number, seed = "getData", max = 10, ) => { const baseSeed = seedrandom(seed); const rand = () => baseSeed.quick() * max; return range(num).map((v) => ({ x: v + 1, y: rand(), y0: rand() })); }; export const getDescendingSmallData = () => { return [ { x: 1, y: 2 }, { x: 2, y: 1 }, { x: 3, y: 0.5 }, { x: 4, y: 0.2 }, { x: 5, y: 0.1 }, { x: 6, y: -0.1 }, { x: 7, y: -0.2 }, { x: 8, y: -0.5 }, { x: 9, y: -1 }, { x: 10, y: -2 }, ]; }; export const getErrorBarData = ( num: number, symmetric = false, seed = "getData", ) => { const baseSeed = seedrandom(seed); const rand = () => baseSeed.quick() * 3; return range(num).map((v) => { return { x: v + 3, y: baseSeed.quick() * 20 + 5, errorX: symmetric ? rand() : [rand(), rand()], errorY: symmetric ? rand() : [rand(), rand()], }; }); }; export const getFourQuadrantData = (num: number, seed = "getMixedData") => { const baseSeed = seedrandom(seed); const rand = () => baseSeed.quick() * 10 - 5; return range(num).map((v) => ({ x: v - Math.round(num / 2), y: rand() })); }; export const getLogData = (num: number, seed = "getData") => { const baseSeed = seedrandom(seed); const rand = () => baseSeed.quick() * 100000; return range(num).map((v) => ({ x: v + 1, y: rand() })); }; export const getMixedData = (num: number, seed = "getMixedData") => { const baseSeed = seedrandom(seed); const rand = () => baseSeed.quick() * 10 - 5; return range(num).map((v) => ({ x: v + 1, y: rand() })); }; export const getRandomValues = (num, seed = "random") => { const baseSeed = seedrandom(seed); const rand = () => Math.round(baseSeed.quick() * 100); const result = range(num).map(() => rand()); return result.sort((a, b) => a - b); }; export const getStackedData = (num: number, samples, useStrings) => { return range(num).map(() => { return useStrings ? getStringData(samples) : getData(samples); }); }; export const getStringData = (num: number, seed = "getData") => { const baseSeed = seedrandom(seed); const rand = () => baseSeed.quick() * 10; return range(num).map((v) => ({ x: `#${v + 1}`, y: rand() })); }; export const getTimeData = (num: number, seed = "getData") => { const baseSeed = seedrandom(seed); const rand = () => baseSeed.quick() * 10; const current = 1523389495000; return range(num).map((v) => { return { x: new Date((current / num) * (v + 1)), y: rand(), }; }); }; export const getTimeValues = (num) => { const current = 1523389495000; return range(num).map((v) => { return new Date((current / num) * (v + 1)); }); }; export const getValues = (num, min = 0, step = 1) => { return range(num).map((v) => v * step + min); }; ================================================ FILE: stories/utils/decorators.tsx ================================================ import React from "react"; const containerStyle: React.CSSProperties = { display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gridColumnGap: "1rem", gridRowGap: "1rem", }; export const componentContainer = (story: any) => (
{story()}
); ================================================ FILE: stories/victory-charts/victory-animation/config.ts ================================================ import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { VictoryAnimation } from "@/victory"; import { componentContainer } from "../../utils/decorators"; type StoryProps = React.ComponentProps; export const ComponentMeta: Meta = { component: VictoryAnimation, decorators: [componentContainer], argTypes: { delay: { control: "number" }, duration: { control: "number" }, easing: { control: "text" }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-animation/default.stories.tsx ================================================ /* eslint-disable react-hooks/rules-of-hooks */ import React, { useEffect, useState } from "react"; import type { Meta } from "@storybook/react"; import { VictoryAnimation, VictoryLabel, VictoryPie } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAnimation", }; const ANIMATION_DATA = [ [ { x: 1, y: 0 }, { x: 2, y: 100 }, ], [ { x: 1, y: 25 }, { x: 2, y: 75 }, ], [ { x: 1, y: 50 }, { x: 2, y: 50 }, ], [ { x: 1, y: 75 }, { x: 2, y: 25 }, ], [ { x: 1, y: 100 }, { x: 2, y: 0 }, ], ]; export const Default: Story = { args: {}, render: (props) => { const [data, setData] = useState(ANIMATION_DATA[0]); const [percent, setPercent] = useState(0); useEffect(() => { const interval = setInterval(() => { const nextIndex = (ANIMATION_DATA.indexOf(data) + 1) % ANIMATION_DATA.length; setData(ANIMATION_DATA[nextIndex]); setPercent(ANIMATION_DATA[nextIndex][0].y); }, 2000); // clean up interval on unmount return () => clearInterval(interval); }, [data, percent]); return (
null} style={{ data: { fill: ({ datum }) => { const color = datum.y > 30 ? "green" : "red"; return datum.x === 1 ? color : "transparent"; }, }, }} /> {(newProps) => { return ( ); }}
); }, }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryArea, VictoryAreaProps } from "@/victory"; import { VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryAreaProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryArea, decorators: [componentContainer], argTypes: { ...VictoryCommonProps, ...VictoryDatableProps, ...VictoryMultiLabelableProps, interpolation: { control: "select", options: [ "basis", "basisClosed", "basisOpen", "bundle", "cardinal", "cardinalClosed", "cardinalOpen", "catmullRom", "catmullRomClosed", "catmullRomOpen", "linear", "linearClosed", "monotoneX", "monotoneY", "natural", "radial", "step", "stepAfter", "stepBefore", ], }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-area/data-accessors.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryArea, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const DataAccessors: Story = { args: {}, render: (props) => ( <> data.pet + data.wild} /> data.pet + data.wild} /> data.pet + data.wild} /> d.y - 1} /> d.y - 1} /> d.y - 1} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryArea, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const Default: Story = { args: {}, render: (props) => ( ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/disable-inline-styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { Area, VictoryArea, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const DisableInlineStyles: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/events.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryArea, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const Events: Story = { args: {}, render: (props) => ( { return [ { eventKey: "all", target: "data", mutation: (eventProps) => { const fill = eventProps.style && eventProps.style.fill; return fill === "black" ? null : { style: { fill: "black" } }; }, }, ]; }, }, }, ]} data={getData(5)} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/interpolation.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { InterpolationPropType, VictoryArea, VictoryChart, VictoryLabel, VictoryTheme, } from "@/victory"; import { getData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const Interpolation: Story = { args: { data: getData(8), }, render: (props) => ( <> {[ "basis", "cardinal", "catmullRom", "linear", "monotoneX", "monotoneY", "natural", "step", "stepAfter", "stepBefore", ].map((interpolation) => ( ))} ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/labels.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryArea, VictoryChart } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const Labels: Story = { args: {}, render: (props) => ( <> `x: ${datum.x}`} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/log-scale.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryArea, VictoryChart, VictoryTheme } from "@/victory"; import { getLogData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const LogScale: Story = { args: {}, render: (props) => ( <> `x: ${datum.x}`} /> `x: ${datum.x}`} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/plotting-functions.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryArea, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const PlottingFunctions: Story = { args: {}, render: (props) => ( <> Math.sin(2 * Math.PI * d.x)} /> Math.sin(2 * Math.PI * d.x)} /> Math.sin(2 * Math.PI * d.x)} y0={(d) => Math.sin(2 * Math.PI * d.x) - 0.5} /> Math.sin(2 * Math.PI * d.x)} y0={(d) => Math.sin(2 * Math.PI * d.x) - 0.5} /> Math.sin(Math.PI * d.x)} /> Math.sin(Math.PI * d.x)} y0={(d) => Math.sin(Math.PI * d.x) - 0.5} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/polar-interpolation.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { InterpolationPropType, VictoryArea, VictoryChart, VictoryLabel, VictoryTheme, } from "@/victory"; import { getData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const PolarInterpolation: Story = { args: { data: getData(8), }, render: (props) => ( <> {["basis", "cardinal", "catmullRom", "linear"].map((interpolation) => ( ))} ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/polar.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryArea, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const Polar: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/stacked.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryArea, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData, getDataWithBaseline } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const Stacked: Story = { args: {}, render: (props) => ( <> datum.x}> datum.x}> datum.x}> datum.x}> datum.x}> datum.x}> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryArea, VictoryChart, VictoryLabelStyleObject, VictoryTheme, } from "@/victory"; import { getData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; const labelStyle: VictoryLabelStyleObject = { fill: ({ datum }) => (datum.x === "Dog" ? "red" : "black"), }; export const Styles: Story = { args: {}, render: (props) => ( <> datum.x} style={{ labels: { fontSize: 20, fill: "tomato", fontFamily: "monospace" }, data: { fill: "tomato", fillOpacity: 0.7, stroke: "tomato", strokeWidth: 2, }, }} /> datum.x} data={[ { x: "Cat", y: 62 }, { x: "Dog", y: 91 }, { x: "Fish", y: 55 }, { x: "Bird", y: 55 }, ]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/theme.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryArea, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { getData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const Theme: Story = { args: {}, render: (props) => ( <> datum.x}> datum.x}> datum.x}> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/time-scale.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryArea, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { getTimeData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const TimeScale: Story = { args: {}, render: (props) => ( <> datum.x.getFullYear()} /> datum.x.getFullYear()} /> datum.x.getFullYear()}> datum.x.getFullYear()}> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-area/tooltips.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryArea, VictoryChart, VictoryTheme, VictoryTooltip, } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData, getMixedData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryArea", }; export const Tooltips: Story = { args: { labelComponent: , }, render: (props) => ( <> `x: ${datum.x}`} /> `x: ${datum.x}`} /> `x: ${datum.x}`} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/axis-value.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryBar, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const AxisValue: Story = { args: {}, render: (props) => ( <> t.getFullYear()} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/brush-axis-grid-line-styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryBrushLine } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const BrushAxisGridLineStyles: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/brush-axis-grid-line-width.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryBrushLine } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const BrushAxisGridLineWidth: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/brush-axis-grid-line-with-domain.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryBrushLine } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const BrushAxisGridLineWithDomain: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/brush-axis-grid-line.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryBrushLine } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const BrushAxisGridLine: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/brush-axis-with-domain.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryBrushLine } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const BrushAxisWithDomain: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/brush-axis.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryBrushLine } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const BrushAxis: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryAxis, VictoryAxisProps } from "@/victory"; import { VictoryAxisCommonProps, VictoryCommonProps, VictoryDatableProps, VictorySingleLabelableProps, } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryAxisProps & { themeKey: string; }; export type Story = StoryObj; export const ComponentMeta: Meta> = { component: VictoryAxis, decorators: [componentContainer], argTypes: { ...VictoryAxisCommonProps, ...VictoryCommonProps, ...VictoryDatableProps, ...VictorySingleLabelableProps, crossAxis: { control: "boolean" }, fixLabelOverlap: { control: "boolean" }, offsetX: { control: "number" }, offsetY: { control: "number" }, orientation: { control: "select", options: ["top", "bottom", "left", "right"], }, }, }; ================================================ FILE: stories/victory-charts/victory-axis/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const Default: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/fix-label-overlap.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryTheme } from "@/victory"; import { getRandomValues, getTimeValues, getValues } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const FixLabelOverlap: Story = { args: { fixLabelOverlap: true, }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/offsets.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const Offsets: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/orientation.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryChart, VictoryTheme } from "@/victory"; import { getValues } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const Orientation: Story = { args: { tickValues: getValues(5), }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/style.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const Style: Story = { args: {}, render: (props) => ( <> (tick > 0.5 ? "red" : "grey") }, ticks: { stroke: "grey", size: 5 }, tickLabels: { fontSize: 15, padding: 5 }, }} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/tick-format.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryChart, VictoryTheme } from "@/victory"; import { getValues } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const TickFormat: Story = { args: { tickValues: getValues(5), }, render: (props) => ( <> `#${t}`} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/tick-values.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryChart, VictoryTheme } from "@/victory"; import { getRandomValues, getTimeValues, getValues } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const TickValues: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/with-domain.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryChart, VictoryScatter, VictoryTheme, } from "@/victory"; import { getValues } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const WithDomain: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-axis/with-multiline-labels.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryChart, VictoryTheme } from "@/victory"; import { getValues } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryAxis", }; export const WithMultilineLabels: Story = { args: { tickFormat: (tick) => (tick >= 0 ? tick : `minus\n${-tick}`), tickValues: getValues(5, -2), }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/alignment.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryTheme } from "@/victory"; import { getData, getMixedData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const Alignment: Story = { args: { data: getData(7), }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/bar-ratio.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const BarRatio: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/bar-width.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const BarWidth: Story = { args: { data: getData(7), }, render: (props) => ( <> datum.x * 4} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryBar, VictoryBarProps } from "@/victory"; import { VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryBarProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryBar, decorators: [componentContainer], argTypes: { ...VictoryCommonProps, ...VictoryDatableProps, ...VictoryMultiLabelableProps, alignment: { control: "select", options: ["start", "middle", "end"] }, barRatio: { control: "number" }, barWidth: { control: "number" }, cornerRadius: { control: "number" }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-bar/corner-radius.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryPolarAxis, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData, getMixedData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const CornerRadius: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/data.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const Data: Story = { args: {}, render: (props) => ( <> data.pet + data.wild} /> d.y - d.x} /> d.y - d.x} /> d.y - d.x} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const Default: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/disable-inline-styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { Bar, VictoryBar, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const DisableInlineStyles: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/domain.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const Domain: Story = { args: { data: getData(7), }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/focus-with-refs.stories.tsx ================================================ /* eslint-disable react-hooks/rules-of-hooks */ import React, { useEffect, useCallback, useRef } from "react"; import type { Meta } from "@storybook/react"; import { Bar, VictoryBar, VictoryChart, VictoryTheme, VictoryTooltip, } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const FocusWithRefs: Story = { args: {}, render: (props) => { const barsRef = useRef(new Map()); const focusOnBar = (id) => { const map = barsRef.current; const node = map.get(id); node.focus(); }; useEffect(() => { if (barsRef.current) { focusOnBar("1"); } }, []); const setRef = useCallback((node) => { const map = barsRef.current; if (node) { map.set(node.attributes.index.value, node); } }, []); return ( <> `x: ${datum.x}`} labelComponent={} dataComponent={} /> ); }, }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/get-path.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const GetPath: Story = { args: { data: getData(7), }, render: (props) => { const verticalPathFn = (callbackArgs) => { const { x0, x1, y0, y1 } = callbackArgs; return `M ${x0}, ${y0} L ${(x1 + x0) / 2}, ${y1} L ${x1}, ${y0} z`; }; const horizontalPathFn = (callbackArgs) => { const { x0, x1, y0, y1 } = callbackArgs; return `M ${x0}, ${y1} L ${x1}, ${(y0 + y1) / 2} L ${x0}, ${y0} z`; }; return ( <> ); }, }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/grouped-bars.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryGroup, VictoryStack, VictoryTheme, VictoryTooltip, } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData, getMixedData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const GroupedBars: Story = { args: {}, render: (props) => ( <> datum.x}> datum.x}> datum.x}> datum.x}> datum.x} labelComponent={} > datum.x} labelComponent={} > datum.x} style={{ data: { width: 15 } }} > datum.x} style={{ data: { width: 15 } }} > datum.x} labelComponent={} > datum.x} labelComponent={} > ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/labels.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryPolarAxis, VictoryTheme, } from "@/victory"; import { getData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const Labels: Story = { args: {}, render: (props) => ( <> `x: ${datum.x}`} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/polar-bars.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryGroup, VictoryPolarAxis, VictoryStack, VictoryTheme, } from "@/victory"; import { getData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const PolarBars: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/regressions.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryGroup, VictoryStack, VictoryTheme, } from "@/victory"; import { getData, getStackedData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const Regressions: Story = { args: {}, render: (props) => ( <> {getStackedData(5, 3, "useStrings").map((data, index) => { return ( ); })} {getStackedData(5, 3, "useStrings").map((data, index) => { return ( ); })} {getStackedData(5, 3, "useStrings").map((data, index) => { return ( ); })} datum.x} > ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/scale.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryGroup, VictoryStack, VictoryTheme, } from "@/victory"; import { getLogData, getTimeData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const Scale: Story = { args: {}, render: (props) => ( <> datum.y.toPrecision(1)} /> datum.y.toPrecision(1)} /> datum.x.getFullYear()} /> datum.x.getFullYear()} /> datum.x.getFullYear()}> datum.x.getFullYear()}> datum.x.getFullYear()}> datum.x.getFullYear()}> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/sorting.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const Sorting: Story = { args: { data: [ { x: "low", y: 1 }, { x: "med", y: 2 }, { x: "high", y: 3 }, ], sortKey: "sort", }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/stacked-bars.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryLabel, VictoryStack, VictoryTheme, VictoryTooltip, } from "@/victory"; import { getData, getDataWithBaseline, getMixedData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const StackedBars: Story = { args: {}, render: (props) => ( <> datum._y1.toPrecision(2)}> datum.y.toPrecision(2)} labelComponent={} /> datum.y.toPrecision(2)} labelComponent={} /> datum.y.toPrecision(2)} labelComponent={} /> datum._y1.toPrecision(2)}> datum._y1.toPrecision(2)}> datum._y1.toPrecision(2)}> datum._y1.toPrecision(2)} labelComponent={} > datum._y1.toPrecision(2)} labelComponent={} > ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/style.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const Style: Story = { args: {}, render: (props) => ( <> datum.y} style={{ labels: { fontSize: 20, fill: "tomato", fontFamily: "monospace" }, data: { fill: "tomato", fillOpacity: 0.7, stroke: "tomato", strokeWidth: 2, }, }} /> (datum.y > 75 ? "red" : "transparent"), strokeWidth: 3, opacity: ({ datum }) => (datum.y > 75 ? 1 : 0.4), }, }} labels={({ datum }) => datum.x} data={[ { x: "Cat", y: 62 }, { x: "Dog", y: 91 }, { x: "Fish", y: 55 }, { x: "Bird", y: 55 }, ]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/theme.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const Theme: Story = { args: {}, render: (props) => ( <> datum.x}> datum.x}> datum.x}> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-bar/tooltips.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryPolarAxis, VictoryTheme, VictoryTooltip, } from "@/victory"; import { getData, getMixedData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBar", }; export const Tooltips: Story = { args: {}, render: (props) => ( <> `x: ${datum.x}`} labelComponent={} /> `x: ${datum.x}`} labelComponent={} /> `x: ${datum.x}`} labelComponent={} /> `x: ${datum.x}`} labelComponent={} /> } /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-box-plot/box-width.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBoxPlot, VictoryChart, VictoryTheme } from "@/victory"; import { getBoxPlotData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBoxPlot", }; export const BoxWidth: Story = { args: { data: getBoxPlotData(5), domainPadding: 20, }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-box-plot/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryBoxPlot, VictoryBoxPlotProps } from "@/victory"; import { VictoryCommonProps, VictoryDatableProps } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryBoxPlotProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryBoxPlot, decorators: [componentContainer], argTypes: { ...VictoryCommonProps, ...VictoryDatableProps, boxWidth: { control: "number" }, labels: { control: "boolean" }, max: { control: "text" }, maxLabels: { control: "boolean" }, median: { control: "text" }, medianLabels: { control: "boolean" }, min: { control: "text" }, minLabels: { control: "boolean" }, q1: { control: "text" }, q1Labels: { control: "boolean" }, q3: { control: "text" }, q3Labels: { control: "boolean" }, whiskerWidth: { control: "number" }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-box-plot/data.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBoxPlot, VictoryChart, VictoryTheme } from "@/victory"; import { getArrayData, getBoxPlotRepeatData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBoxPlot", }; export const Data: Story = { args: { domainPadding: 20, }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-box-plot/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBoxPlot, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getBoxPlotData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBoxPlot", }; export const Default: Story = { args: { domainPadding: 20, data: getBoxPlotData(5), }, render: (props) => ( ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-box-plot/disable-inline-styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { Box, LineSegment, VictoryBoxPlot, VictoryChart, VictoryTheme, Whisker, } from "@/victory"; import { getBoxPlotData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBoxPlot", }; export const DisableInlineStyles: Story = { args: { domainPadding: 20, data: getBoxPlotData(8), }, render: (props) => ( <> } q3Component={} maxComponent={ } minComponent={ } medianComponent={ } {...props} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-box-plot/domain.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBoxPlot, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getArrayData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBoxPlot", }; export const Domain: Story = { args: { data: getArrayData(5, 10), domainPadding: 20, }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-box-plot/group.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBoxPlot, VictoryGroup } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBoxPlot", }; export const Group: Story = { args: { domainPadding: 20, }, render: (props) => ( ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-box-plot/labels.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBoxPlot, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getBoxPlotData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBoxPlot", }; export const Labels: Story = { args: { data: getBoxPlotData(3), domainPadding: 20, }, render: (props) => ( <> `min: ${datum.min}`} maxLabels={({ datum }) => `max: ${datum.max}`} medianLabels={({ datum }) => `med: ${datum.median}`} {...props} /> `q1: ${datum.q1}`} q3Labels={({ datum }) => `q3: ${datum.q3}`} {...props} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-box-plot/style.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBoxPlot, VictoryChart, VictoryTheme } from "@/victory"; import { getBoxPlotData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBoxPlot", }; export const Style: Story = { args: { data: getBoxPlotData(4), domainPadding: 20, labels: true, }, render: (props) => ( <> (datum.q1 < 10 ? 1 : 0.5), }, q3: { fill: "#2bbee0", fillOpacity: ({ datum }) => (datum.q3 > 15 ? 1 : 0.5), }, median: { stroke: "#fff", strokeWidth: 2 }, minLabels: { fill: "#FF530D", padding: 10 }, maxLabels: { fill: "#2bbee0", padding: 10 }, }} {...props} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-box-plot/theme.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBoxPlot, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getBoxPlotData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBoxPlot", }; export const Theme: Story = { args: { domainPadding: 20, }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-box-plot/tooltips.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBoxPlot, VictoryChart, VictoryTheme, VictoryTooltip, } from "@/victory"; import { getBoxPlotData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBoxPlot", }; export const Tooltips: Story = { args: { data: getBoxPlotData(3), domainPadding: 20, labels: true, }, render: (props) => ( <> } q3LabelComponent={} labelOrientation={{ q1: "left", q3: "left", min: "right", max: "right", median: "right", }} {...props} /> } q3LabelComponent={} labelOrientation={{ q1: "top", q3: "top", min: "bottom", max: "bottom", median: "bottom", }} {...props} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-box-plot/whisker-width.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBoxPlot, VictoryChart, VictoryTheme } from "@/victory"; import { getBoxPlotData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryBoxPlot", }; export const WhiskerWidth: Story = { args: { data: getBoxPlotData(5), domainPadding: 20, }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-candlestick/candle-colors.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryCandlestick, VictoryChart, VictoryTheme } from "@/victory"; import { getCandlestickData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryCandlestick", }; export const CandleColors: Story = { args: { candleColors: { positive: "#8BC34A", negative: "#C62828" }, data: getCandlestickData(7), domainPadding: 20, }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-candlestick/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryCandlestick, VictoryCandlestickProps } from "@/victory"; import { VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryCandlestickProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryCandlestick, decorators: [componentContainer], argTypes: { ...VictoryCommonProps, ...VictoryDatableProps, ...VictoryMultiLabelableProps, candleRatio: { control: "boolean" }, candleWidth: { control: "number" }, close: { control: "text" }, closeLabels: { control: "boolean" }, high: { control: "text" }, highLabels: { control: "boolean" }, low: { control: "text" }, lowLabels: { control: "boolean" }, open: { control: "text" }, openLabels: { control: "boolean" }, wickStrokeWidth: { control: "number" }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-candlestick/data.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryCandlestick, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryCandlestick", }; export const Data: Story = { args: { domainPadding: 20, }, render: (props) => ( <> data.big / 10} /> data.big / 10} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-candlestick/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryCandlestick, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getCandlestickData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryCandlestick", }; export const Default: Story = { args: { data: getCandlestickData(8), }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-candlestick/disable-inline-styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { Candle, VictoryCandlestick, VictoryChart } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getCandlestickData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryCandlestick", }; export const DisableInlineStyles: Story = { args: { data: getCandlestickData(8), }, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-candlestick/domain.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryCandlestick, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryCandlestick", }; export const Domain: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-candlestick/labels.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryCandlestick, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getCandlestickData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryCandlestick", }; const sampleData = [ { x: 1, open: 9, close: 30, high: 56, low: 7 }, { x: 2, open: 80, close: 40, high: 120, low: 10 }, { x: 3, open: 50, close: 80, high: 90, low: 20 }, { x: 4, open: 70, close: 22, high: 70, low: 5 }, ]; export const Labels: Story = { args: { domainPadding: 20, }, render: (props) => ( <> `x: ${datum.x}`} /> `x: ${datum.x}`} /> datum.open} closeLabels={({ datum }) => datum.close} /> datum.open} closeLabels={({ datum }) => datum.close} labelOrientation={{ open: "top", close: "bottom" }} /> datum.high} lowLabels={({ datum }) => datum.low} /> datum.high} lowLabels={({ datum }) => datum.low} labelOrientation={{ low: "left", high: "right" }} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-candlestick/scale.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryCandlestick, VictoryChart, VictoryTheme } from "@/victory"; import { getCandlestickTimeData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryCandlestick", }; export const Scale: Story = { args: {}, render: (props) => ( <> datum.x.getFullYear()} /> datum.x.getFullYear()} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-candlestick/style.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryCandlestick, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getCandlestickData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryCandlestick", }; export const Style: Story = { args: { data: getCandlestickData(7), }, render: (props) => ( <> datum.x} style={{ labels: { fontSize: 20, fill: "tomato", fontFamily: "monospace" }, data: { fill: "tomato", fillOpacity: 0.7, stroke: "tomato", strokeWidth: 2, }, }} /> datum.open > datum.close ? "red" : "black", }, }} labels={({ datum }) => datum.x} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-candlestick/tooltips.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryCandlestick, VictoryChart, VictoryTheme, VictoryTooltip, } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getCandlestickData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryCandlestick", }; const sampleData = [ { x: 1, open: 9, close: 30, high: 56, low: 7 }, { x: 2, open: 80, close: 40, high: 120, low: 10 }, { x: 3, open: 50, close: 80, high: 90, low: 20 }, { x: 4, open: 70, close: 22, high: 70, low: 5 }, ]; export const Tooltips: Story = { args: {}, render: (props) => ( <> `x: ${datum.x}`} labelComponent={} /> `x: ${datum.x}`} labelComponent={} /> datum.open} closeLabels={({ datum }) => datum.close} openLabelComponent={} closeLabelComponent={} /> datum.open} closeLabels={({ datum }) => datum.close} openLabelComponent={} closeLabelComponent={} labelOrientation={{ open: "top", close: "bottom" }} /> datum.high} lowLabels={({ datum }) => datum.low} highLabelComponent={} lowLabelComponent={} /> datum.high} lowLabels={({ datum }) => datum.low} highLabelComponent={} lowLabelComponent={} labelOrientation={{ low: "left", high: "right" }} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-candlestick/wick-stroke-width.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryCandlestick, VictoryChart, VictoryTheme } from "@/victory"; import { getCandlestickData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryCandlestick", }; export const WickStrokeWidth: Story = { args: { data: getCandlestickData(7), }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/axes.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const Axes: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryChart, VictoryChartProps } from "@/victory"; import { VictoryCommonProps } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryChartProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryChart, decorators: [componentContainer], argTypes: { ...VictoryCommonProps, desc: { control: "text" }, endAngle: { control: "number" }, innerRadius: { control: "number" }, prependDefaultAxes: { control: "boolean" }, startAngle: { control: "number" }, title: { control: "text" }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-chart/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const Default: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/domain-from-data.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryBoxPlot, VictoryChart, VictoryLine, VictoryScatter, VictoryTheme, } from "@/victory"; import { getArrayData, getData, getFourQuadrantData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const DomainFromData: Story = { args: {}, render: (props) => ( <> 10 * Math.sin(13 * Math.PI * d.x)} /> 10 * Math.sin(13 * Math.PI * d.x)} /> Math.sin(Math.PI * d.x)} /> Math.sin(Math.PI * d.x)} /> 5 + 3 * Math.sin(Math.PI * d.x)} /> 5 + 3 * Math.sin(Math.PI * d.x)} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/domain-padding.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryTheme } from "@/victory"; import { getData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const DomainPadding: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/domain.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryLine, VictoryTheme } from "@/victory"; import { getData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const Domain: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/orientations.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryBar, VictoryChart } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const Orientations: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/style.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryChart, VictoryGroup, VictoryPolarAxis, VictoryStack, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const Style: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/victory-brush-container-default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBrushContainer, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const VictoryBrushContainerDefault: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/victory-brush-container-with-brush-style.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBrushContainer, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const VictoryBrushContainerWithBrushStyle: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/victory-brush-container-with-domain-horizontal.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBrushContainer, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const VictoryBrushContainerWithDomainHorizontal: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/victory-brush-container-with-domain.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBrushContainer, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const VictoryBrushContainerWithDomain: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/victory-cursor-container-default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryChart, VictoryCursorContainer, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const VictoryCursorContainerDefault: Story = { args: {}, render: (props) => ( <> datum.x} defaultCursorValue={{ x: 0.25, y: 0.75 }} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/victory-cursor-container-horizontal.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryChart, VictoryCursorContainer, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const VictoryCursorContainerHorizontal: Story = { args: {}, render: (props) => ( <> datum.x} defaultCursorValue={{ x: 0.25, y: 0.75 }} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-chart/victory-zoom-container-default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryChart, VictoryLine, VictoryZoomContainer, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryChart", }; export const VictoryZoomContainerDefault: Story = { args: {}, render: (props) => ( <> } > ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-container/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryContainer, VictoryContainerProps } from "@/victory"; import { VictoryContainerProps as ContainerArgTypes } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryContainerProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryContainer, decorators: [componentContainer], argTypes: { ...ContainerArgTypes, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-container/preserve-aspect-ratio.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryContainer, VictoryChart, VictoryLabel, VictoryLine, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryContainer", }; export const PreserveAspectRatio: Story = { args: { height: 250, width: 500, }, render: (props) => ( <> } > } > } > ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-container/responsive.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryContainer, VictoryChart, VictoryLabel, VictoryLine, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryContainer", }; export const Responsive: Story = { args: {}, render: (props) => ( <> } > ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-errorbar/border-width.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryErrorBar, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getErrorBarData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryErrorBar", }; export const BorderWidth: Story = { args: { data: getErrorBarData(5), }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-errorbar/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryErrorBar, VictoryErrorBarProps } from "@/victory"; import { VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryErrorBarProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryErrorBar, decorators: [componentContainer], argTypes: { ...VictoryCommonProps, ...VictoryDatableProps, ...VictoryMultiLabelableProps, borderWidth: { control: "number" }, errorX: { control: "text" }, errorY: { control: "text" }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-errorbar/data.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryErrorBar, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryErrorBar", }; export const Data: Story = { args: {}, render: (props) => ( <> [d.error, d.error + 2]} /> [d.error, d.error + 2]} /> [d.error, d.error + 2]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-errorbar/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryErrorBar, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryErrorBar", }; export const Default: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-errorbar/disable-inline-styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { ErrorBar, VictoryErrorBar, VictoryChart, VictoryTheme, } from "@/victory"; import { getErrorBarData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryErrorbar", }; export const DisableInlineStyles: Story = { args: { data: getErrorBarData(4), }, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-errorbar/domain.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryErrorBar, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryErrorBar", }; export const Domain: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-errorbar/labels.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryErrorBar, VictoryChart, VictoryTheme, VictoryTooltip, } from "@/victory"; import { getErrorBarData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryErrorBar", }; export const Labels: Story = { args: {}, render: (props) => ( <> `x: ${datum.x}`} /> `x: ${datum.x}`} labelComponent={} /> } /> [d.error, d.error + 2]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-errorbar/style.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryErrorBar, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getErrorBarData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryErrorBar", }; export const Style: Story = { args: {}, render: (props) => ( <> datum.x} style={{ labels: { fontSize: 20, fill: "tomato", fontFamily: "monospace" }, data: { fill: "tomato", fillOpacity: 0.7, stroke: "tomato", strokeWidth: 2, }, }} /> datum.errorX > datum.errorY ? "red" : "black", }, }} labels={({ datum }) => datum.x} data={getErrorBarData(4, true)} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-errorbar/theme.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryErrorBar, VictoryChart, VictoryTheme } from "@/victory"; import { getErrorBarData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryErrorBar", }; export const Theme: Story = { args: { data: getErrorBarData(5), }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/bin-spacing.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import { data } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const BinSpacing: Story = { args: { data, }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryHistogram, VictoryHistogramProps } from "@/victory"; import { VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryHistogramProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryHistogram, decorators: [componentContainer], argTypes: { ...VictoryCommonProps, ...VictoryDatableProps, ...VictoryMultiLabelableProps, bins: { control: "number" }, binSpacing: { control: "number" }, cornerRadius: { control: "number" }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-histogram/corner-radius.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import { data } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const CornerRadius: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/data.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import { data, timeData } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const Data: Story = { args: {}, render: (props) => { const oneYear = timeData.map(({ x }) => { const newDate = new Date(x); newDate.setFullYear(2020); return { x: newDate }; }); const fourMonths = timeData.map(({ x }, index) => { const newDate = new Date(x); newDate.setFullYear(2020); newDate.setMonth(Math.ceil(index / 200)); return { x: newDate }; }); const oneMonth = timeData.map(({ x }) => { const newDate = new Date(x); newDate.setMonth(4); newDate.setFullYear(2020); return { x: newDate }; }); return ( <> ({ value: x }))} x={({ value }) => value} /> {/* ({ value: x }))} x={({ value }) => value} /> */} ); }, }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/data.ts ================================================ export const data = [ { x: 18 }, { x: 85 }, { x: 27 }, { x: 62 }, { x: 26 }, { x: 90 }, { x: 85 }, { x: 60 }, { x: 85 }, { x: 21 }, { x: 86 }, { x: 89 }, { x: 60 }, { x: 82 }, { x: 70 }, { x: 22 }, { x: 68 }, { x: 79 }, { x: 18 }, { x: 76 }, { x: 32 }, { x: 26 }, { x: 18 }, { x: 63 }, { x: 71 }, { x: 98 }, { x: 91 }, { x: 22 }, { x: 25 }, { x: 91 }, { x: 18 }, { x: 49 }, { x: 18 }, { x: 79 }, { x: 56 }, { x: 18 }, { x: 18 }, { x: 28 }, { x: 79 }, { x: 44 }, ]; export const timeData = [ { x: new Date("2018-09-24T07:00:00.000Z") }, { x: new Date("2010-09-14T07:00:00.000Z") }, { x: new Date("2011-03-14T07:00:00.000Z") }, { x: new Date("2011-01-09T08:00:00.000Z") }, { x: new Date("2014-05-06T07:00:00.000Z") }, { x: new Date("2012-02-05T08:00:00.000Z") }, { x: new Date("2013-08-09T07:00:00.000Z") }, { x: new Date("2015-05-21T07:00:00.000Z") }, { x: new Date("2020-11-10T08:00:00.000Z") }, { x: new Date("2013-01-27T08:00:00.000Z") }, { x: new Date("2013-03-03T08:00:00.000Z") }, { x: new Date("2015-10-21T07:00:00.000Z") }, { x: new Date("2021-09-10T07:00:00.000Z") }, { x: new Date("2011-01-05T08:00:00.000Z") }, { x: new Date("2020-03-18T07:00:00.000Z") }, { x: new Date("2011-02-04T08:00:00.000Z") }, { x: new Date("2019-03-19T07:00:00.000Z") }, { x: new Date("2019-01-03T08:00:00.000Z") }, { x: new Date("2016-07-07T07:00:00.000Z") }, { x: new Date("2011-05-18T07:00:00.000Z") }, { x: new Date("2021-06-12T07:00:00.000Z") }, { x: new Date("2016-09-12T07:00:00.000Z") }, { x: new Date("2018-02-24T08:00:00.000Z") }, { x: new Date("2016-01-05T08:00:00.000Z") }, { x: new Date("2019-08-07T07:00:00.000Z") }, { x: new Date("2018-05-19T07:00:00.000Z") }, { x: new Date("2020-06-25T07:00:00.000Z") }, { x: new Date("2018-09-17T07:00:00.000Z") }, { x: new Date("2012-04-27T07:00:00.000Z") }, { x: new Date("2016-01-18T08:00:00.000Z") }, { x: new Date("2020-01-07T08:00:00.000Z") }, { x: new Date("2017-09-24T07:00:00.000Z") }, { x: new Date("2014-09-19T07:00:00.000Z") }, { x: new Date("2021-09-11T07:00:00.000Z") }, { x: new Date("2013-06-10T07:00:00.000Z") }, { x: new Date("2020-08-18T07:00:00.000Z") }, { x: new Date("2015-06-16T07:00:00.000Z") }, { x: new Date("2021-07-26T07:00:00.000Z") }, { x: new Date("2014-02-10T08:00:00.000Z") }, { x: new Date("2020-07-13T07:00:00.000Z") }, { x: new Date("2021-07-06T07:00:00.000Z") }, { x: new Date("2012-08-06T07:00:00.000Z") }, { x: new Date("2013-11-14T08:00:00.000Z") }, { x: new Date("2011-07-01T07:00:00.000Z") }, { x: new Date("2017-08-12T07:00:00.000Z") }, { x: new Date("2014-08-12T07:00:00.000Z") }, { x: new Date("2015-05-20T07:00:00.000Z") }, { x: new Date("2010-09-16T07:00:00.000Z") }, { x: new Date("2013-09-21T07:00:00.000Z") }, { x: new Date("2012-05-19T07:00:00.000Z") }, { x: new Date("2010-06-25T07:00:00.000Z") }, { x: new Date("2014-02-25T08:00:00.000Z") }, { x: new Date("2012-10-14T07:00:00.000Z") }, { x: new Date("2014-03-10T07:00:00.000Z") }, { x: new Date("2010-06-17T07:00:00.000Z") }, { x: new Date("2010-11-19T08:00:00.000Z") }, { x: new Date("2016-11-14T08:00:00.000Z") }, { x: new Date("2020-03-20T07:00:00.000Z") }, { x: new Date("2016-05-21T07:00:00.000Z") }, { x: new Date("2018-03-16T07:00:00.000Z") }, { x: new Date("2017-07-15T07:00:00.000Z") }, { x: new Date("2018-01-11T08:00:00.000Z") }, { x: new Date("2010-01-01T08:00:00.000Z") }, { x: new Date("2014-10-13T07:00:00.000Z") }, { x: new Date("2013-10-08T07:00:00.000Z") }, { x: new Date("2020-03-01T08:00:00.000Z") }, { x: new Date("2018-09-14T07:00:00.000Z") }, { x: new Date("2021-02-17T08:00:00.000Z") }, { x: new Date("2020-08-08T07:00:00.000Z") }, { x: new Date("2019-04-09T07:00:00.000Z") }, { x: new Date("2011-01-05T08:00:00.000Z") }, { x: new Date("2013-02-26T08:00:00.000Z") }, { x: new Date("2020-03-04T08:00:00.000Z") }, { x: new Date("2018-08-04T07:00:00.000Z") }, { x: new Date("2011-09-14T07:00:00.000Z") }, { x: new Date("2018-02-19T08:00:00.000Z") }, { x: new Date("2016-02-08T08:00:00.000Z") }, { x: new Date("2020-10-03T07:00:00.000Z") }, { x: new Date("2021-05-24T07:00:00.000Z") }, { x: new Date("2019-10-04T07:00:00.000Z") }, { x: new Date("2019-09-21T07:00:00.000Z") }, { x: new Date("2020-06-20T07:00:00.000Z") }, { x: new Date("2017-10-10T07:00:00.000Z") }, { x: new Date("2017-09-16T07:00:00.000Z") }, { x: new Date("2010-05-20T07:00:00.000Z") }, { x: new Date("2020-04-05T07:00:00.000Z") }, { x: new Date("2021-01-07T08:00:00.000Z") }, { x: new Date("2012-09-05T07:00:00.000Z") }, { x: new Date("2010-06-11T07:00:00.000Z") }, { x: new Date("2010-01-15T08:00:00.000Z") }, { x: new Date("2015-06-23T07:00:00.000Z") }, { x: new Date("2016-05-07T07:00:00.000Z") }, { x: new Date("2012-06-25T07:00:00.000Z") }, { x: new Date("2016-05-03T07:00:00.000Z") }, { x: new Date("2011-10-12T07:00:00.000Z") }, { x: new Date("2019-01-23T08:00:00.000Z") }, { x: new Date("2013-01-15T08:00:00.000Z") }, { x: new Date("2018-02-05T08:00:00.000Z") }, { x: new Date("2011-07-24T07:00:00.000Z") }, { x: new Date("2010-11-14T08:00:00.000Z") }, { x: new Date("2020-10-16T07:00:00.000Z") }, { x: new Date("2016-03-07T08:00:00.000Z") }, { x: new Date("2014-02-12T08:00:00.000Z") }, { x: new Date("2019-11-20T08:00:00.000Z") }, { x: new Date("2019-09-27T07:00:00.000Z") }, { x: new Date("2019-03-16T07:00:00.000Z") }, { x: new Date("2018-08-15T07:00:00.000Z") }, { x: new Date("2020-02-21T08:00:00.000Z") }, { x: new Date("2012-05-03T07:00:00.000Z") }, { x: new Date("2015-03-23T07:00:00.000Z") }, { x: new Date("2021-06-27T07:00:00.000Z") }, { x: new Date("2015-03-16T07:00:00.000Z") }, { x: new Date("2014-05-04T07:00:00.000Z") }, { x: new Date("2020-03-08T08:00:00.000Z") }, { x: new Date("2021-07-11T07:00:00.000Z") }, { x: new Date("2010-08-15T07:00:00.000Z") }, { x: new Date("2016-10-09T07:00:00.000Z") }, { x: new Date("2012-07-13T07:00:00.000Z") }, { x: new Date("2020-07-22T07:00:00.000Z") }, { x: new Date("2019-08-22T07:00:00.000Z") }, { x: new Date("2014-03-05T08:00:00.000Z") }, { x: new Date("2014-03-05T08:00:00.000Z") }, { x: new Date("2021-09-05T07:00:00.000Z") }, { x: new Date("2021-02-11T08:00:00.000Z") }, { x: new Date("2018-11-07T08:00:00.000Z") }, { x: new Date("2017-10-25T07:00:00.000Z") }, { x: new Date("2018-03-07T08:00:00.000Z") }, { x: new Date("2012-10-17T07:00:00.000Z") }, { x: new Date("2021-03-27T07:00:00.000Z") }, { x: new Date("2017-01-01T08:00:00.000Z") }, { x: new Date("2010-03-21T07:00:00.000Z") }, { x: new Date("2021-07-23T07:00:00.000Z") }, { x: new Date("2015-11-26T08:00:00.000Z") }, { x: new Date("2021-02-20T08:00:00.000Z") }, { x: new Date("2018-09-18T07:00:00.000Z") }, { x: new Date("2018-09-27T07:00:00.000Z") }, { x: new Date("2021-05-24T07:00:00.000Z") }, { x: new Date("2019-05-02T07:00:00.000Z") }, { x: new Date("2018-06-12T07:00:00.000Z") }, { x: new Date("2016-05-13T07:00:00.000Z") }, { x: new Date("2020-07-22T07:00:00.000Z") }, { x: new Date("2017-04-19T07:00:00.000Z") }, { x: new Date("2010-10-14T07:00:00.000Z") }, { x: new Date("2021-11-01T07:00:00.000Z") }, { x: new Date("2015-09-03T07:00:00.000Z") }, { x: new Date("2015-01-18T08:00:00.000Z") }, { x: new Date("2020-05-22T07:00:00.000Z") }, { x: new Date("2020-05-21T07:00:00.000Z") }, { x: new Date("2017-01-09T08:00:00.000Z") }, { x: new Date("2017-01-01T08:00:00.000Z") }, { x: new Date("2020-05-13T07:00:00.000Z") }, { x: new Date("2013-01-24T08:00:00.000Z") }, { x: new Date("2013-04-05T07:00:00.000Z") }, { x: new Date("2018-04-15T07:00:00.000Z") }, { x: new Date("2014-02-11T08:00:00.000Z") }, { x: new Date("2014-11-21T08:00:00.000Z") }, { x: new Date("2017-10-12T07:00:00.000Z") }, { x: new Date("2020-06-15T07:00:00.000Z") }, { x: new Date("2015-01-20T08:00:00.000Z") }, { x: new Date("2021-08-26T07:00:00.000Z") }, { x: new Date("2010-10-20T07:00:00.000Z") }, { x: new Date("2012-09-02T07:00:00.000Z") }, { x: new Date("2018-09-20T07:00:00.000Z") }, { x: new Date("2015-04-01T07:00:00.000Z") }, { x: new Date("2012-02-22T08:00:00.000Z") }, { x: new Date("2017-03-03T08:00:00.000Z") }, { x: new Date("2011-06-08T07:00:00.000Z") }, { x: new Date("2015-08-11T07:00:00.000Z") }, { x: new Date("2010-01-10T08:00:00.000Z") }, { x: new Date("2019-05-18T07:00:00.000Z") }, { x: new Date("2020-09-03T07:00:00.000Z") }, { x: new Date("2011-09-04T07:00:00.000Z") }, { x: new Date("2017-10-01T07:00:00.000Z") }, { x: new Date("2010-07-02T07:00:00.000Z") }, { x: new Date("2010-05-15T07:00:00.000Z") }, { x: new Date("2021-08-23T07:00:00.000Z") }, { x: new Date("2018-05-19T07:00:00.000Z") }, { x: new Date("2015-11-21T08:00:00.000Z") }, { x: new Date("2014-11-15T08:00:00.000Z") }, { x: new Date("2014-06-10T07:00:00.000Z") }, { x: new Date("2013-11-21T08:00:00.000Z") }, { x: new Date("2021-04-06T07:00:00.000Z") }, { x: new Date("2014-03-26T07:00:00.000Z") }, { x: new Date("2020-05-07T07:00:00.000Z") }, { x: new Date("2016-06-03T07:00:00.000Z") }, { x: new Date("2018-04-07T07:00:00.000Z") }, { x: new Date("2020-04-14T07:00:00.000Z") }, { x: new Date("2010-07-27T07:00:00.000Z") }, { x: new Date("2020-03-03T08:00:00.000Z") }, { x: new Date("2012-02-24T08:00:00.000Z") }, { x: new Date("2018-06-10T07:00:00.000Z") }, { x: new Date("2010-05-16T07:00:00.000Z") }, { x: new Date("2017-09-15T07:00:00.000Z") }, { x: new Date("2017-11-16T08:00:00.000Z") }, { x: new Date("2013-04-03T07:00:00.000Z") }, { x: new Date("2013-04-05T07:00:00.000Z") }, { x: new Date("2012-07-21T07:00:00.000Z") }, { x: new Date("2020-03-09T07:00:00.000Z") }, { x: new Date("2019-11-07T08:00:00.000Z") }, { x: new Date("2017-07-17T07:00:00.000Z") }, { x: new Date("2011-03-02T08:00:00.000Z") }, { x: new Date("2012-11-05T08:00:00.000Z") }, { x: new Date("2020-03-12T07:00:00.000Z") }, { x: new Date("2021-08-27T07:00:00.000Z") }, { x: new Date("2013-04-04T07:00:00.000Z") }, { x: new Date("2013-09-24T07:00:00.000Z") }, { x: new Date("2021-08-08T07:00:00.000Z") }, { x: new Date("2020-11-24T08:00:00.000Z") }, { x: new Date("2018-07-13T07:00:00.000Z") }, { x: new Date("2010-05-17T07:00:00.000Z") }, { x: new Date("2020-09-06T07:00:00.000Z") }, { x: new Date("2020-08-13T07:00:00.000Z") }, { x: new Date("2015-05-22T07:00:00.000Z") }, { x: new Date("2015-11-22T08:00:00.000Z") }, { x: new Date("2011-07-26T07:00:00.000Z") }, { x: new Date("2017-10-20T07:00:00.000Z") }, { x: new Date("2010-01-18T08:00:00.000Z") }, { x: new Date("2018-02-21T08:00:00.000Z") }, { x: new Date("2018-09-26T07:00:00.000Z") }, { x: new Date("2016-03-14T07:00:00.000Z") }, { x: new Date("2011-07-08T07:00:00.000Z") }, { x: new Date("2015-06-18T07:00:00.000Z") }, { x: new Date("2017-03-19T07:00:00.000Z") }, { x: new Date("2019-06-12T07:00:00.000Z") }, { x: new Date("2021-05-08T07:00:00.000Z") }, { x: new Date("2010-07-23T07:00:00.000Z") }, { x: new Date("2017-11-12T08:00:00.000Z") }, { x: new Date("2021-04-19T07:00:00.000Z") }, { x: new Date("2013-07-22T07:00:00.000Z") }, { x: new Date("2012-02-26T08:00:00.000Z") }, { x: new Date("2019-10-08T07:00:00.000Z") }, { x: new Date("2010-11-13T08:00:00.000Z") }, { x: new Date("2016-02-09T08:00:00.000Z") }, { x: new Date("2020-09-19T07:00:00.000Z") }, { x: new Date("2020-06-11T07:00:00.000Z") }, { x: new Date("2021-05-12T07:00:00.000Z") }, { x: new Date("2013-03-26T07:00:00.000Z") }, { x: new Date("2015-11-12T08:00:00.000Z") }, { x: new Date("2016-03-15T07:00:00.000Z") }, { x: new Date("2021-04-09T07:00:00.000Z") }, { x: new Date("2010-07-26T07:00:00.000Z") }, { x: new Date("2021-01-11T08:00:00.000Z") }, { x: new Date("2017-04-22T07:00:00.000Z") }, { x: new Date("2019-01-13T08:00:00.000Z") }, { x: new Date("2020-06-08T07:00:00.000Z") }, { x: new Date("2013-03-08T08:00:00.000Z") }, { x: new Date("2012-10-19T07:00:00.000Z") }, { x: new Date("2018-06-12T07:00:00.000Z") }, { x: new Date("2021-01-27T08:00:00.000Z") }, { x: new Date("2010-06-23T07:00:00.000Z") }, { x: new Date("2013-07-06T07:00:00.000Z") }, { x: new Date("2014-09-09T07:00:00.000Z") }, { x: new Date("2012-06-17T07:00:00.000Z") }, { x: new Date("2013-05-24T07:00:00.000Z") }, { x: new Date("2011-03-10T08:00:00.000Z") }, { x: new Date("2021-05-27T07:00:00.000Z") }, { x: new Date("2017-11-06T08:00:00.000Z") }, { x: new Date("2019-10-21T07:00:00.000Z") }, { x: new Date("2014-09-20T07:00:00.000Z") }, { x: new Date("2018-02-17T08:00:00.000Z") }, { x: new Date("2019-02-09T08:00:00.000Z") }, { x: new Date("2015-04-11T07:00:00.000Z") }, { x: new Date("2021-08-16T07:00:00.000Z") }, { x: new Date("2011-10-11T07:00:00.000Z") }, { x: new Date("2010-01-13T08:00:00.000Z") }, { x: new Date("2021-01-19T08:00:00.000Z") }, { x: new Date("2016-11-12T08:00:00.000Z") }, { x: new Date("2011-08-25T07:00:00.000Z") }, { x: new Date("2012-02-09T08:00:00.000Z") }, { x: new Date("2016-01-08T08:00:00.000Z") }, { x: new Date("2016-01-01T08:00:00.000Z") }, { x: new Date("2017-09-15T07:00:00.000Z") }, { x: new Date("2012-05-08T07:00:00.000Z") }, { x: new Date("2016-06-12T07:00:00.000Z") }, { x: new Date("2019-06-26T07:00:00.000Z") }, { x: new Date("2011-09-24T07:00:00.000Z") }, { x: new Date("2020-07-07T07:00:00.000Z") }, { x: new Date("2013-08-04T07:00:00.000Z") }, { x: new Date("2020-10-14T07:00:00.000Z") }, { x: new Date("2021-02-13T08:00:00.000Z") }, { x: new Date("2021-03-13T08:00:00.000Z") }, { x: new Date("2020-07-15T07:00:00.000Z") }, { x: new Date("2011-05-27T07:00:00.000Z") }, { x: new Date("2020-10-05T07:00:00.000Z") }, { x: new Date("2021-03-01T08:00:00.000Z") }, { x: new Date("2012-10-24T07:00:00.000Z") }, { x: new Date("2020-07-11T07:00:00.000Z") }, { x: new Date("2011-07-25T07:00:00.000Z") }, { x: new Date("2021-08-20T07:00:00.000Z") }, { x: new Date("2016-11-07T08:00:00.000Z") }, { x: new Date("2016-01-11T08:00:00.000Z") }, { x: new Date("2018-08-02T07:00:00.000Z") }, { x: new Date("2011-07-13T07:00:00.000Z") }, { x: new Date("2013-07-10T07:00:00.000Z") }, { x: new Date("2010-10-23T07:00:00.000Z") }, { x: new Date("2015-03-06T08:00:00.000Z") }, { x: new Date("2012-01-18T08:00:00.000Z") }, { x: new Date("2013-11-02T07:00:00.000Z") }, { x: new Date("2017-09-17T07:00:00.000Z") }, { x: new Date("2019-02-17T08:00:00.000Z") }, { x: new Date("2019-09-01T07:00:00.000Z") }, { x: new Date("2013-01-16T08:00:00.000Z") }, { x: new Date("2012-09-08T07:00:00.000Z") }, { x: new Date("2021-02-24T08:00:00.000Z") }, { x: new Date("2019-05-22T07:00:00.000Z") }, { x: new Date("2010-06-07T07:00:00.000Z") }, { x: new Date("2013-04-21T07:00:00.000Z") }, { x: new Date("2021-06-22T07:00:00.000Z") }, { x: new Date("2015-03-11T07:00:00.000Z") }, { x: new Date("2019-01-01T08:00:00.000Z") }, { x: new Date("2015-03-03T08:00:00.000Z") }, { x: new Date("2014-08-02T07:00:00.000Z") }, { x: new Date("2012-11-05T08:00:00.000Z") }, { x: new Date("2018-06-03T07:00:00.000Z") }, { x: new Date("2017-02-20T08:00:00.000Z") }, { x: new Date("2010-04-25T07:00:00.000Z") }, { x: new Date("2011-11-25T08:00:00.000Z") }, { x: new Date("2020-08-19T07:00:00.000Z") }, { x: new Date("2016-01-24T08:00:00.000Z") }, { x: new Date("2019-02-14T08:00:00.000Z") }, { x: new Date("2020-06-02T07:00:00.000Z") }, { x: new Date("2015-11-01T07:00:00.000Z") }, { x: new Date("2016-09-01T07:00:00.000Z") }, { x: new Date("2016-02-08T08:00:00.000Z") }, { x: new Date("2011-11-20T08:00:00.000Z") }, { x: new Date("2021-04-25T07:00:00.000Z") }, { x: new Date("2010-01-05T08:00:00.000Z") }, { x: new Date("2015-07-25T07:00:00.000Z") }, { x: new Date("2017-10-07T07:00:00.000Z") }, { x: new Date("2019-07-11T07:00:00.000Z") }, { x: new Date("2017-02-23T08:00:00.000Z") }, { x: new Date("2011-05-12T07:00:00.000Z") }, { x: new Date("2013-03-01T08:00:00.000Z") }, { x: new Date("2015-06-02T07:00:00.000Z") }, { x: new Date("2015-07-21T07:00:00.000Z") }, { x: new Date("2017-06-23T07:00:00.000Z") }, { x: new Date("2021-08-13T07:00:00.000Z") }, { x: new Date("2016-01-04T08:00:00.000Z") }, { x: new Date("2010-06-08T07:00:00.000Z") }, { x: new Date("2019-08-11T07:00:00.000Z") }, { x: new Date("2011-03-15T07:00:00.000Z") }, { x: new Date("2016-11-26T08:00:00.000Z") }, { x: new Date("2014-07-24T07:00:00.000Z") }, { x: new Date("2013-03-10T08:00:00.000Z") }, { x: new Date("2011-01-16T08:00:00.000Z") }, { x: new Date("2017-02-27T08:00:00.000Z") }, { x: new Date("2010-02-24T08:00:00.000Z") }, { x: new Date("2011-03-22T07:00:00.000Z") }, { x: new Date("2012-06-15T07:00:00.000Z") }, { x: new Date("2014-02-10T08:00:00.000Z") }, { x: new Date("2012-11-22T08:00:00.000Z") }, { x: new Date("2019-09-19T07:00:00.000Z") }, { x: new Date("2011-02-18T08:00:00.000Z") }, { x: new Date("2021-01-08T08:00:00.000Z") }, { x: new Date("2013-11-08T08:00:00.000Z") }, { x: new Date("2020-08-01T07:00:00.000Z") }, { x: new Date("2019-04-20T07:00:00.000Z") }, { x: new Date("2016-03-18T07:00:00.000Z") }, { x: new Date("2020-05-22T07:00:00.000Z") }, { x: new Date("2018-07-20T07:00:00.000Z") }, { x: new Date("2021-06-07T07:00:00.000Z") }, { x: new Date("2012-07-24T07:00:00.000Z") }, { x: new Date("2010-09-20T07:00:00.000Z") }, { x: new Date("2016-11-10T08:00:00.000Z") }, { x: new Date("2014-09-07T07:00:00.000Z") }, { x: new Date("2011-01-16T08:00:00.000Z") }, { x: new Date("2013-11-04T08:00:00.000Z") }, { x: new Date("2015-04-07T07:00:00.000Z") }, { x: new Date("2018-02-14T08:00:00.000Z") }, { x: new Date("2016-05-13T07:00:00.000Z") }, { x: new Date("2013-03-02T08:00:00.000Z") }, { x: new Date("2020-05-03T07:00:00.000Z") }, { x: new Date("2010-11-07T07:00:00.000Z") }, { x: new Date("2012-02-25T08:00:00.000Z") }, { x: new Date("2016-03-04T08:00:00.000Z") }, { x: new Date("2016-08-19T07:00:00.000Z") }, { x: new Date("2012-11-24T08:00:00.000Z") }, { x: new Date("2015-01-10T08:00:00.000Z") }, { x: new Date("2016-11-02T07:00:00.000Z") }, { x: new Date("2012-09-03T07:00:00.000Z") }, { x: new Date("2016-07-02T07:00:00.000Z") }, { x: new Date("2018-07-22T07:00:00.000Z") }, { x: new Date("2021-01-04T08:00:00.000Z") }, { x: new Date("2013-06-06T07:00:00.000Z") }, { x: new Date("2020-02-04T08:00:00.000Z") }, { x: new Date("2013-04-26T07:00:00.000Z") }, { x: new Date("2015-06-24T07:00:00.000Z") }, { x: new Date("2012-05-20T07:00:00.000Z") }, { x: new Date("2018-07-14T07:00:00.000Z") }, { x: new Date("2010-05-07T07:00:00.000Z") }, { x: new Date("2010-10-11T07:00:00.000Z") }, { x: new Date("2014-05-05T07:00:00.000Z") }, { x: new Date("2021-05-08T07:00:00.000Z") }, { x: new Date("2012-04-05T07:00:00.000Z") }, { x: new Date("2012-02-23T08:00:00.000Z") }, { x: new Date("2012-09-05T07:00:00.000Z") }, { x: new Date("2012-05-22T07:00:00.000Z") }, { x: new Date("2011-09-05T07:00:00.000Z") }, { x: new Date("2017-08-19T07:00:00.000Z") }, { x: new Date("2011-08-16T07:00:00.000Z") }, { x: new Date("2010-06-15T07:00:00.000Z") }, { x: new Date("2018-09-26T07:00:00.000Z") }, { x: new Date("2015-11-18T08:00:00.000Z") }, { x: new Date("2020-01-23T08:00:00.000Z") }, { x: new Date("2016-04-08T07:00:00.000Z") }, { x: new Date("2016-11-24T08:00:00.000Z") }, { x: new Date("2016-10-10T07:00:00.000Z") }, { x: new Date("2019-01-18T08:00:00.000Z") }, { x: new Date("2018-09-11T07:00:00.000Z") }, { x: new Date("2013-10-09T07:00:00.000Z") }, { x: new Date("2014-06-12T07:00:00.000Z") }, { x: new Date("2010-07-17T07:00:00.000Z") }, { x: new Date("2021-09-15T07:00:00.000Z") }, { x: new Date("2010-06-16T07:00:00.000Z") }, { x: new Date("2016-04-27T07:00:00.000Z") }, { x: new Date("2014-06-17T07:00:00.000Z") }, { x: new Date("2012-09-03T07:00:00.000Z") }, { x: new Date("2017-11-10T08:00:00.000Z") }, { x: new Date("2021-10-20T07:00:00.000Z") }, { x: new Date("2011-02-19T08:00:00.000Z") }, { x: new Date("2018-07-02T07:00:00.000Z") }, { x: new Date("2017-03-14T07:00:00.000Z") }, { x: new Date("2020-07-07T07:00:00.000Z") }, { x: new Date("2017-09-09T07:00:00.000Z") }, { x: new Date("2021-10-19T07:00:00.000Z") }, { x: new Date("2017-02-03T08:00:00.000Z") }, { x: new Date("2015-07-05T07:00:00.000Z") }, { x: new Date("2012-11-27T08:00:00.000Z") }, { x: new Date("2021-06-03T07:00:00.000Z") }, { x: new Date("2010-01-23T08:00:00.000Z") }, { x: new Date("2021-05-23T07:00:00.000Z") }, { x: new Date("2015-05-12T07:00:00.000Z") }, { x: new Date("2018-03-03T08:00:00.000Z") }, { x: new Date("2021-05-07T07:00:00.000Z") }, { x: new Date("2012-06-22T07:00:00.000Z") }, { x: new Date("2019-03-03T08:00:00.000Z") }, { x: new Date("2014-10-03T07:00:00.000Z") }, { x: new Date("2014-04-02T07:00:00.000Z") }, { x: new Date("2020-11-04T08:00:00.000Z") }, { x: new Date("2012-02-16T08:00:00.000Z") }, { x: new Date("2014-02-18T08:00:00.000Z") }, { x: new Date("2011-07-25T07:00:00.000Z") }, { x: new Date("2020-01-11T08:00:00.000Z") }, { x: new Date("2013-01-13T08:00:00.000Z") }, { x: new Date("2012-04-06T07:00:00.000Z") }, { x: new Date("2021-10-22T07:00:00.000Z") }, { x: new Date("2013-05-04T07:00:00.000Z") }, { x: new Date("2011-03-14T07:00:00.000Z") }, { x: new Date("2016-10-07T07:00:00.000Z") }, { x: new Date("2013-04-23T07:00:00.000Z") }, { x: new Date("2013-07-05T07:00:00.000Z") }, { x: new Date("2014-03-09T08:00:00.000Z") }, { x: new Date("2011-09-24T07:00:00.000Z") }, { x: new Date("2019-10-02T07:00:00.000Z") }, { x: new Date("2013-08-27T07:00:00.000Z") }, { x: new Date("2011-11-16T08:00:00.000Z") }, { x: new Date("2018-09-14T07:00:00.000Z") }, { x: new Date("2018-05-08T07:00:00.000Z") }, { x: new Date("2014-03-08T08:00:00.000Z") }, { x: new Date("2011-09-05T07:00:00.000Z") }, { x: new Date("2021-07-10T07:00:00.000Z") }, { x: new Date("2012-04-26T07:00:00.000Z") }, { x: new Date("2015-03-10T07:00:00.000Z") }, { x: new Date("2017-06-25T07:00:00.000Z") }, { x: new Date("2012-06-06T07:00:00.000Z") }, { x: new Date("2016-03-17T07:00:00.000Z") }, { x: new Date("2019-10-13T07:00:00.000Z") }, { x: new Date("2017-04-08T07:00:00.000Z") }, { x: new Date("2019-09-03T07:00:00.000Z") }, { x: new Date("2013-02-09T08:00:00.000Z") }, { x: new Date("2011-11-07T08:00:00.000Z") }, { x: new Date("2021-07-07T07:00:00.000Z") }, { x: new Date("2013-03-15T07:00:00.000Z") }, { x: new Date("2016-07-10T07:00:00.000Z") }, { x: new Date("2011-01-03T08:00:00.000Z") }, { x: new Date("2020-03-20T07:00:00.000Z") }, { x: new Date("2010-04-25T07:00:00.000Z") }, { x: new Date("2020-11-23T08:00:00.000Z") }, { x: new Date("2016-09-05T07:00:00.000Z") }, { x: new Date("2013-02-24T08:00:00.000Z") }, { x: new Date("2016-04-11T07:00:00.000Z") }, { x: new Date("2015-07-07T07:00:00.000Z") }, { x: new Date("2018-11-19T08:00:00.000Z") }, { x: new Date("2014-01-09T08:00:00.000Z") }, { x: new Date("2016-03-05T08:00:00.000Z") }, { x: new Date("2011-02-07T08:00:00.000Z") }, { x: new Date("2016-02-17T08:00:00.000Z") }, { x: new Date("2017-06-21T07:00:00.000Z") }, { x: new Date("2013-10-23T07:00:00.000Z") }, { x: new Date("2017-08-24T07:00:00.000Z") }, { x: new Date("2018-01-17T08:00:00.000Z") }, { x: new Date("2015-04-19T07:00:00.000Z") }, { x: new Date("2016-05-10T07:00:00.000Z") }, { x: new Date("2018-01-01T08:00:00.000Z") }, { x: new Date("2017-01-01T08:00:00.000Z") }, { x: new Date("2021-10-02T07:00:00.000Z") }, { x: new Date("2012-02-20T08:00:00.000Z") }, { x: new Date("2010-04-07T07:00:00.000Z") }, { x: new Date("2019-07-21T07:00:00.000Z") }, { x: new Date("2014-06-15T07:00:00.000Z") }, ]; ================================================ FILE: stories/victory-charts/victory-histogram/date-bins.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import * as d3Array from "@/victory-vendor/d3-array"; import * as d3Scale from "@/victory-vendor/d3-scale"; import * as d3Time from "@/victory-vendor/d3-time"; import { timeData } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const DateBins: Story = { args: {}, render: (props) => { // HACK: d3scale has a scaleTime function but the types // are whack coming through the build const niceTimeScale = (d3Scale as any) .scaleTime() .domain(d3Array.extent(timeData, ({ x }) => x)) .nice(); return ( <> ); }, }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import { data } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const Default: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/disable-inline-styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { Bar, VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import { data } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const DisableInlineStyles: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/domain.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import { data } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const Domain: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/empty-data.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const EmptyData: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/get-path.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import { data } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const GetPath: Story = { args: {}, render: (props) => { // const verticalPathFn = (callbackArgs) => { // const { x0, x1, y0, y1 } = callbackArgs; // return `M ${x0}, ${y0} // L ${(x1 + x0) / 2}, ${y1} // L ${x1}, ${y0} // z`; // }; // const horizontalPathFn = (callbackArgs) => { // const { x0, x1, y0, y1 } = callbackArgs; // return `M ${x0}, ${y1} // L ${x1}, ${(y0 + y1) / 2} // L ${x0}, ${y0} // z`; // }; // TODO: revert this when the getPath types are fixed. return ( <> {/* */} {/* */} ); }, }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/labels.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme, VictoryTooltip, } from "@/victory"; import { data } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const Labels: Story = { args: {}, render: (props) => ( <> `${datum.x0} - ${datum.x1}`} /> `${datum.x0} - ${datum.x1}`} /> `${datum.x0} - ${datum.x1}`} labelComponent={} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/mixed-charts.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryLine, VictoryChart, VictoryScatter, VictoryTheme, } from "@/victory"; import { data } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const MixedCharts: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/numeric-bins.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import { data } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const NumericBins: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/scale.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import { data, timeData } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const Scale: Story = { args: {}, render: (props) => ( <> `${datum.x0.getFullYear()}\n|\n${datum.x1.getFullYear()}` } /> `${datum.x0.getFullYear()} - ${datum.x1.getFullYear()}` } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/stacked.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { getData } from "../../utils/data"; import { data } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const Stacked: Story = { args: {}, render: (props) => { const stackedData = [ ...[50, 30, 100, 32, 50, 10, 49, 78, 20].map((count) => getData(count, count.toString(), 100), ), [{ x: 1 }, { x: 3 }, { x: 1 }, { x: 2 }], ]; return ( <> {stackedData.map((d, index) => ( ))} {stackedData.map((d, index) => ( ))} {stackedData.map((d, index) => ( ))} {stackedData.map((d, index) => ( ))} ({ a: { b: { c: x } } }))} x="a.b.c" /> {stackedData.map((d, index) => ( ))} ({ a: { b: { c: x } } }))} x="a.b.c" /> {stackedData.map((d, index) => ( ))} ); }, }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import { data } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const Styles: Story = { args: {}, render: (props) => ( <> (datum.y > 3 ? "red" : "transparent"), strokeWidth: 3, opacity: ({ datum }) => (datum.y > 3 ? 1 : 0.4), }, }} labels={["one", "two", "three", "four", "five"]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-histogram/theme.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryHistogram, VictoryChart, VictoryTheme } from "@/victory"; import { data } from "./data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryHistogram", }; export const Theme: Story = { args: { data, }, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-label/anchors.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLabel, VictoryScatter } from "@/victory"; import { defaultScatterProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLabel", }; export const Anchors: Story = { args: {}, render: (props) => ( <> } /> } /> } /> } /> } /> } /> } /> } /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-label/angles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLabel, VictoryScatter } from "@/victory"; import { defaultScatterProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLabel", }; export const Angles: Story = { args: {}, render: (props) => ( <> } /> } /> } /> } /> } /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-label/background-padding.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLabel, VictoryScatter } from "@/victory"; import { defaultScatterProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLabel", }; export const BackgroundPadding: Story = { args: {}, render: (props) => ( <> } /> } /> } /> } /> } /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-label/background-styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLabel, VictoryScatter } from "@/victory"; import { defaultScatterProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLabel", }; export const BackgroundStyles: Story = { args: {}, render: (props) => ( <> } /> } /> } /> (datum.y > 0 ? -5 : 8)} verticalAnchor="end" backgroundPadding={{ top: 5, right: 5, bottom: 5, left: 5 }} backgroundStyle={{ fill: "plum", stroke: "#000000" }} text={[ "Victory is awesome.", "background styles", "work with dy functions", ]} /> } /> (datum.y > 0 ? -5 : 8)} verticalAnchor="end" backgroundPadding={{ top: 5, right: 5, bottom: 5, left: 5 }} backgroundStyle={{ fill: "thistle", stroke: "#000000" }} text={[ "Victory is awesome.", "background styles", "work with dx functions", ]} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-label/config.ts ================================================ import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { VictoryLabel, VictoryScatterProps } from "@/victory"; import { componentContainer } from "../../utils/decorators"; type StoryProps = React.ComponentProps; export const ComponentMeta: Meta> = { component: VictoryLabel, decorators: [componentContainer], argTypes: { angle: { control: "number" }, capHeight: { control: "number" }, className: { control: "text" }, desc: { control: "text" }, direction: { control: "select", options: ["inherit", "rtl", "ltr"] }, id: { control: "text" }, inline: { control: "boolean" }, labelPlacement: { control: "select", options: ["parallel", "perpendicular", "vertical"], }, lineHeight: { control: "number" }, polar: { control: "boolean" }, renderInPortal: { control: "boolean" }, tabIndex: { control: "number" }, text: { control: "text" }, textAnchor: { control: "select", options: ["inherit", "start", "middle", "end"], }, transform: { control: "text" }, verticalAnchor: { control: "select", options: ["inherit", "start", "middle", "end"], }, }, }; export type Story = StoryObj; export const defaultScatterProps: VictoryScatterProps = { style: { labels: { padding: 0, fontFamily: "arial" }, data: { fill: "gold" }, }, width: 300, height: 300, domain: [-10, 10], data: [{ x: 0, y: 0 }], labels: () => "Label", size: 5, }; ================================================ FILE: stories/victory-charts/victory-label/default-rendering.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLabel, VictoryScatter } from "@/victory"; import { defaultScatterProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLabel", }; export const Default: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-label/inline.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLabel, VictoryScatter } from "@/victory"; import { defaultScatterProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLabel", }; export const Inline: Story = { args: {}, render: (props) => ( <> } /> } /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-label/line-height.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLabel, VictoryScatter } from "@/victory"; import { defaultScatterProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLabel", }; export const LineHeight: Story = { args: {}, render: (props) => ( <> } /> } /> } /> } /> } /> } /> } /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-label/positioning.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLabel, VictoryScatter } from "@/victory"; import { defaultScatterProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLabel", }; export const Positioning: Story = { args: {}, render: (props) => ( <> } /> } /> } /> } /> datum.x + 50} text="dx function" /> } /> datum.x - 20} text="dy function" /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-label/styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLabel, VictoryScatter } from "@/victory"; import { defaultScatterProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLabel", }; export const Styles: Story = { args: {}, render: (props) => ( <> } /> } /> } /> } /> } /> } /> (datum.y === 0 ? 12 : 15), fontFamily: "arial", }} text={["object", "with", "functions"]} backgroundStyle={{ stroke: "blue", fill: "none" }} /> } /> (datum.y === 0 ? 12 : 15), fontFamily: "arial", }, ]} text={["single array", "with", "functions"]} backgroundStyle={{ stroke: "blue", fill: "none" }} /> } /> (datum.y === 0 ? 12 : 15), fontFamily: "arial", }, { fill: ({ datum }) => (datum.y === 0 ? "red" : "blue"), fontFamily: "arial", }, ]} text={["multi array", "with", "functions"]} backgroundStyle={{ stroke: "blue", fill: "none" }} /> } /> (datum.y === 0 ? 12 : 15), fontFamily: "arial", }} text={["object", "with", "functions"]} backgroundStyle={[{ stroke: "blue", fill: "none" }]} /> } /> (datum.y === 0 ? 12 : 15), fontFamily: "arial", }, ]} text={["single array", "with", "functions"]} backgroundStyle={[{ stroke: "blue", fill: "none" }]} /> } /> (datum.y === 0 ? 12 : 15), fontFamily: "arial", }, { fill: ({ datum }) => (datum.y === 0 ? "red" : "blue"), fontFamily: "arial", }, ]} text={["multi array", "with", "functions"]} backgroundStyle={[{ stroke: "blue", fill: "none" }]} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-legend/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryLegend, VictoryLegendProps } from "@/victory"; import { VictoryCommonProps, VictoryDatableProps, VictorySingleLabelableProps, } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryLegendProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryLegend, decorators: [componentContainer], argTypes: { ...VictoryCommonProps, ...VictoryDatableProps, ...VictorySingleLabelableProps, centerTitle: { control: "boolean" }, itemsPerRow: { control: "number" }, orientation: { control: "select", options: ["horizontal", "vertical"] }, rowGutter: { control: "number" }, symbolSpacer: { control: "number" }, title: { control: "text" }, titleOrientation: { control: "select", options: ["top", "bottom"] }, x: { control: "number" }, y: { control: "number" }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-legend/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryLegend, VictoryChart, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLegend", }; export const Default: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-legend/line-height.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryAxis, VictoryLegend, VictoryChart, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLegend", }; export const LineHeight: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-legend/title.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import range from "lodash/range"; import { VictoryAxis, VictoryLegend, VictoryChart, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLegend", }; const getData = (num) => { return range(num).map((v) => ({ name: `Series ${v + 1}`, symbol: { size: 5, type: "circle", fill: undefined, }, })); }; export const Title: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryLine, VictoryLineProps } from "@/victory"; import { VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryLineProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryLine, decorators: [componentContainer], argTypes: { ...VictoryCommonProps, ...VictoryDatableProps, ...VictoryMultiLabelableProps, interpolation: { control: "select", options: [ "basis", "basisClosed", "basisOpen", "bundle", "cardinal", "cardinalClosed", "cardinalOpen", "catmullRom", "catmullRomClosed", "catmullRomOpen", "linear", "linearClosed", "monotoneX", "monotoneY", "natural", "radial", "step", "stepAfter", "stepBefore", ], }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-line/data-accessors.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLine, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const DataAccessors: Story = { args: {}, render: (props) => ( <> data.pet + data.wild} /> data.pet + data.wild} /> data.pet + data.wild} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLine, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const Default: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/disable-inline-styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { Curve, VictoryLine, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const DisableInlineStyles: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/events.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLine, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const Events: Story = { args: {}, render: (props) => ( { return [ { target: "data", eventKey: "all", mutation: ({ style }) => { return style.stroke === "black" ? null : { style: { stroke: "black", strokeWidth: 5 } }; }, }, ]; }, }, }, ]} data={getData(5, "seed-1")} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/interpolation.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { InterpolationPropType, VictoryLine, VictoryChart, VictoryLabel, VictoryTheme, } from "@/victory"; import { getData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const Interpolation: Story = { args: {}, render: (props) => { const makeInterpolationChart = (interpolation) => ( ); return ( <> {[ "basis", "cardinal", "catmullRom", "linear", "monotoneX", "monotoneY", "natural", "step", "stepAfter", "stepBefore", ].map((interpolation) => makeInterpolationChart(interpolation))} ); }, }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/labels.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLine, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const Labels: Story = { args: {}, render: (props) => ( <> `x: ${datum.x}`} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/log-scale.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLine, VictoryChart, VictoryTheme } from "@/victory"; import { getLogData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const LogScale: Story = { args: {}, render: (props) => ( <> `x: ${datum.x}`} /> `x: ${datum.x}`} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/plotting-functions.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLine, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const PlottingFunctions: Story = { args: {}, render: (props) => ( <> Math.sin(2 * Math.PI * d.x)} /> Math.sin(2 * Math.PI * d.x)} /> Math.sin(2 * Math.PI * d.x)} y0={(d) => Math.sin(2 * Math.PI * d.x) - 0.5} /> Math.sin(2 * Math.PI * d.x)} y0={(d) => Math.sin(2 * Math.PI * d.x) - 0.5} /> Math.sin(Math.PI * d.x)} /> Math.sin(Math.PI * d.x)} y0={(d) => Math.sin(Math.PI * d.x) - 0.5} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/polar-interpolation.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { InterpolationPropType, VictoryLine, VictoryChart, VictoryLabel, VictoryTheme, } from "@/victory"; import { getData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const PolarInterpolation: Story = { args: {}, render: (props) => { const makeInterpolationChart = (interpolation) => ( ); return ( <> {["basis", "cardinal", "catmullRom", "linear"].map((interpolation) => makeInterpolationChart(interpolation), )} ); }, }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/polar.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLine, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { getData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const Polar: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/stacked.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLine, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { getData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const Stacked: Story = { args: {}, render: (props) => ( <> datum.x}> datum.x}> datum.x}> datum.x}> datum.x}> datum.x}> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLine, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const Styles: Story = { args: {}, render: (props) => ( <> datum.x} style={{ labels: { fontSize: 20, fill: "tomato", fontFamily: "monospace" }, data: { stroke: "tomato", strokeWidth: 2 }, }} /> datum.x} data={[ { x: "Cat", y: 62 }, { x: "Dog", y: 91 }, { x: "Fish", y: 55 }, { x: "Bird", y: 55 }, ]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/theme.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLine, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { getData, getMixedData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const Theme: Story = { args: {}, render: (props) => ( <> datum.x} /> datum.x}> datum.x} /> datum.x}> datum.x} /> datum.x}> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/time-scale.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLine, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { getTimeData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const TimeScale: Story = { args: {}, render: (props) => ( <> datum.x.getFullYear()} /> datum.x.getFullYear()} /> datum.x.getFullYear()}> datum.x.getFullYear()}> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-line/tooltips.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryLine, VictoryChart, VictoryTheme, VictoryTooltip, } from "@/victory"; import { getData, getMixedData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryLine", }; export const Tooltips: Story = { args: {}, render: (props) => ( <> `x: ${datum.x}`} labelComponent={} /> `x: ${datum.x}`} labelComponent={} /> `x: ${datum.x}`} labelComponent={} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/categories.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const Categories: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryPie, VictoryPieProps } from "@/victory"; import { VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryPieProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryPie, decorators: [componentContainer], argTypes: { ...VictoryCommonProps, ...VictoryDatableProps, ...VictoryMultiLabelableProps, cornerRadius: { control: "number" }, endAngle: { control: "number" }, innerRadius: { control: "number" }, labelIndicator: { control: "boolean" }, labelIndicatorInnerOffset: { control: "number" }, labelIndicatorOuterOffset: { control: "number" }, labelPlacement: { control: "select", options: ["parallel", "perpendicular", "vertical"], }, labelPosition: { control: "select", options: ["startAngle", "endAngle", "centroid"], }, labelRadius: { control: "number" }, padAngle: { control: "number" }, startAngle: { control: "number" }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-pie/corner-radius.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const CornerRadius: Story = { args: {}, render: (props) => ( <> datum.x * 5} innerRadius={100} data={[ { x: 1, y: 1 }, { x: 2, y: 3 }, { x: 3, y: 5 }, { x: 4, y: 2 }, { x: 5, y: 3 }, ]} /> datum.r} innerRadius={100} data={[ { x: 1, y: 1 }, { x: 2, y: 3 }, { x: 3, y: 5, r: 15 }, { x: 4, y: 2 }, { x: 5, y: 3 }, ]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/data.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const Data: Story = { args: {}, render: (props) => ( <> data.pet + data.wild} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const Default: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/disable-inline-styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; // TODO: uncomment when Slice props are fixed to include className export const DisableInlineStyles: Story = { args: {}, render: (props) => ( <> {/* } /> */} ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/inner-radius.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const InnerRadius: Story = { args: {}, render: (props) => ( <> datum.radius} innerRadius={({ datum }) => datum.radius - 20} data={[ { x: 1, y: 1, radius: 110 }, { x: 2, y: 3, radius: 120 }, { x: 3, y: 5, radius: 140 }, { x: 4, y: 2, radius: 150 }, { x: 5, y: 3, radius: 130 }, ]} /> datum.y + 10} labelRadius={({ datum }) => datum.y - 20} data={[ { x: "Cat", y: 62 }, { x: "Dog", y: 91 }, { x: "Fish", y: 55 }, { x: "Bird", y: 25 }, ]} /> datum.radius} data={[ { x: 1, y: 1, radius: 50 }, { x: 2, y: 3 }, { x: 3, y: 5 }, { x: 4, y: 2 }, { x: 5, y: 3 }, ]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/label-indicator.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { LineSegment, VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const LabelIndicator: Story = { args: {}, render: (props) => ( <> } /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/label-placement.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const LabelPlacement: Story = { args: { data: [ { x: 1, y: 1, l: 0 }, { x: 2, y: 1, l: 45 }, { x: 3, y: 1, l: 90 }, { x: 4, y: 1, l: 135 }, { x: 5, y: 1, l: 180 }, { x: 6, y: 1, l: 225 }, { x: 7, y: 1, l: 270 }, { x: 8, y: 1, l: 315 }, ], }, render: (props) => ( <> `${datum.l} degrees`} /> `${datum.l}\ndegrees`} /> `${datum.l} degrees`} /> `${datum.l}\ndegrees`} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/label-radius.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const LabelRadius: Story = { args: {}, render: (props) => ( <> datum.y} radius={80} innerRadius={100} data={[ { x: 1, y: 100 }, { x: 2, y: 130 }, { x: 3, y: 150 }, { x: 4, y: 120 }, { x: 5, y: 130 }, ]} /> datum.r} innerRadius={100} data={[ { x: 1, y: 100 }, { x: 2, y: 130 }, { x: 3, y: 150, r: 80 }, { x: 4, y: 120 }, { x: 5, y: 130 }, ]} /> datum.r} innerRadius={100} labelPosition="startAngle" data={[ { x: 1, y: 100 }, { x: 2, y: 130 }, { x: 3, y: 150, r: 80 }, { x: 4, y: 120 }, { x: 5, y: 130 }, ]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/labels.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const Labels: Story = { args: {}, render: (props) => ( <> `#${index}`} /> `#${index}`} labelPosition="startAngle" /> `#${index}`} labelPosition="endAngle" /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/origin.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const Origin: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/pad-angle.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const PadAngle: Story = { args: {}, render: (props) => ( <> datum.x * 2} innerRadius={100} data={[ { x: 1, y: 1 }, { x: 2, y: 3 }, { x: 3, y: 5 }, { x: 4, y: 2 }, { x: 5, y: 3 }, ]} /> datum.r} innerRadius={100} data={[ { x: 1, y: 1 }, { x: 2, y: 3 }, { x: 3, y: 5, r: 8 }, { x: 4, y: 2 }, { x: 5, y: 3 }, ]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/radius.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const Radius: Story = { args: {}, render: (props) => ( <> datum.radius} data={[ { x: 1, y: 1, radius: 110 }, { x: 2, y: 3, radius: 120 }, { x: 3, y: 5, radius: 140 }, { x: 4, y: 2, radius: 150 }, { x: 5, y: 3, radius: 130 }, ]} /> datum.y + 100} labelRadius={({ datum }) => datum.y + 50} style={{ labels: { fill: "white" }, }} data={[ { x: "Cat", y: 62 }, { x: "Dog", y: 91 }, { x: "Fish", y: 55 }, { x: "Bird", y: 25 }, ]} /> datum.y + 100} labelRadius={({ datum }) => datum.y + 65} style={{ labels: { fill: "white" }, }} data={[ { x: "Cat", y: 62 }, { x: "Dog", y: 91 }, { x: "Fish", y: 55 }, { x: "Bird", y: 25 }, ]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/start-and-end-angles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { Helpers, Slice, VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const StartAndEndAngles: Story = { args: {}, render: (props) => ( <> datum.endAngle} /> } labels={() => null} cornerRadius={5} radius={({ datum }) => datum.radius} innerRadius={({ datum }) => datum.innerRadius} data={[ { x: "Cat", y: 62, innerRadius: 0, radius: 30 }, { x: "Dog", y: 91, innerRadius: 35, radius: 65 }, { x: "Fish", y: 55, innerRadius: 70, radius: 100 }, { x: "Bird", y: 55, innerRadius: 105, radius: 135, endAngle: 360 }, ]} /> Helpers.radiansToDegrees(slice?.endAngle) - 90 } /> } labels={() => null} cornerRadius={5} radius={({ datum }) => datum.radius} innerRadius={({ datum }) => datum.innerRadius} data={[ { x: "Cat", y: 62, innerRadius: 0, radius: 30 }, { x: "Dog", y: 91, innerRadius: 35, radius: 65 }, { x: "Fish", y: 55, innerRadius: 70, radius: 100 }, { x: "Bird", y: 55, innerRadius: 105, radius: 135 }, ]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const Styles: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/theme.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const Theme: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-pie/tooltips.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPie, VictoryTheme, VictoryTooltip } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPie", }; export const Tooltips: Story = { args: {}, render: (props) => ( <> } /> } /> `#${index}`} labelPosition="startAngle" labelPlacement="perpendicular" labelComponent={} /> `#${index}`} labelPosition="endAngle" labelPlacement="perpendicular" labelComponent={} /> `#${index}`} labelPosition="startAngle" labelPlacement="parallel" labelComponent={} /> `#${index}`} labelPosition="endAngle" labelPlacement="parallel" labelComponent={} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-polar-axis/axis-angle.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPolarAxis, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPolarAxis", }; export const AxisAngle: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-polar-axis/axis-value.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPolarAxis, VictoryChart, VictoryTheme } from "@/victory"; import { getValues } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPolarAxis", }; export const AxisValue: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-polar-axis/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryPolarAxis, VictoryPolarAxisProps } from "@/victory"; import { VictoryAxisCommonProps, VictoryCommonProps, VictoryDatableProps, } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryPolarAxisProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryPolarAxis, decorators: [componentContainer], argTypes: { ...VictoryAxisCommonProps, ...VictoryCommonProps, ...VictoryDatableProps, axisAngle: { control: "number" }, endAngle: { control: "number" }, innerRadius: { control: "number" }, labelPlacement: { control: "select", options: ["parallel", "perpendicular", "vertical"], }, startAngle: { control: "number" }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-polar-axis/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPolarAxis, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPolarAxis", }; export const Default: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-polar-axis/domain.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPolarAxis, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getValues } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPolarAxis", }; export const Domain: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-polar-axis/inner-radius.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPolarAxis, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getValues } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPolarAxis", }; export const InnerRadius: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-polar-axis/label-placement.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPolarAxis, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPolarAxis", }; export const LabelPlacement: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-polar-axis/scale.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPolarAxis, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getTimeValues } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPolarAxis", }; export const Scale: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-polar-axis/start-and-end-angle.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPolarAxis, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPolarAxis", }; export const StartAndEndAngle: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-polar-axis/style.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPolarAxis, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPolarAxis", }; export const Style: Story = { args: {}, render: (props) => ( <> (tick > 0.5 ? "red" : "grey") }, ticks: { stroke: "grey", size: 5 }, tickLabels: { fontSize: 15, padding: 5 }, }} /> (tick > 0.5 ? "red" : "grey") }, ticks: { stroke: "grey", size: 5 }, tickLabels: { fontSize: 15, padding: 5 }, }} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-polar-axis/theme.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPolarAxis, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPolarAxis", }; export const Theme: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-polar-axis/tick-format.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPolarAxis, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getValues } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPolarAxis", }; export const TickFormat: Story = { args: {}, render: (props) => ( <> `#${t}`} /> `#${t}`} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-polar-axis/tick-values.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryPolarAxis, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getRandomValues, getValues } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPolarAxis", }; export const TickValues: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-portal/config.ts ================================================ import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { VictoryPortal } from "@/victory"; import { componentContainer } from "../../utils/decorators"; type StoryProps = React.ComponentProps; export const ComponentMeta: Meta = { component: VictoryPortal, decorators: [componentContainer], }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-portal/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryBar, VictoryPortal, VictoryChart, VictoryGroup, VictoryLabel, } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryPortal", }; export const Default: Story = { args: {}, render: (props) => (
} data={[ { x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 5 }, ]} />
), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/bubble-charts.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryScatter, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const BubbleCharts: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryScatter, VictoryScatterProps } from "@/victory"; import { VictoryCommonProps, VictoryDatableProps, VictoryMultiLabelableProps, } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; type StoryProps = VictoryScatterProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryScatter, decorators: [componentContainer], argTypes: { ...VictoryCommonProps, ...VictoryDatableProps, ...VictoryMultiLabelableProps, bubbleProperty: { control: "text" }, maxBubbleSize: { control: "number" }, size: { control: "number" }, }, }; export type Story = StoryObj; ================================================ FILE: stories/victory-charts/victory-scatter/data-accessors.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryScatter, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const DataAccessors: Story = { args: { data: [ { animal: "Cat", pet: 45, wild: 17 }, { animal: "Dog", pet: 85, wild: 6 }, { animal: "Fish", pet: 55, wild: 0 }, { animal: "Bird", pet: 15, wild: 40 }, { animal: "Monkey", pet: 5, wild: 40 }, ], }, render: (props) => ( <> datum.animal} x={"animal"} y={(data) => data.pet + data.wild} /> datum.animal} x={"animal"} y={(data) => data.pet + data.wild} /> datum.animal} x={"animal"} y={(data) => data.pet + data.wild} /> Math.sin(2 * Math.PI * d.x)} /> Math.sin(2 * Math.PI * d.x)} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/default-rendering.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryScatter, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const Default: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/disable-inline-styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { Point, VictoryScatter, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const DisableInlineStyles: Story = { args: {}, render: (props) => ( <> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/domain.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryScatter, VictoryChart } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const Domain: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/functional-symbols.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { ScatterSymbolType, VictoryScatter, VictoryChart, VictoryTheme, } from "@/victory"; import { getData, getMixedData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; const SYMBOLS: ScatterSymbolType[] = [ "circle", "cross", "diamond", "plus", "minus", "square", "star", "triangleDown", "triangleUp", ]; export const FunctionalSymbols: Story = { args: {}, render: (props) => ( <> SYMBOLS[index]} labels={({ index }) => SYMBOLS[index]} size={8} /> SYMBOLS[index]} labels={({ index }) => SYMBOLS[index]} size={8} /> SYMBOLS[index]} labels={({ index }) => SYMBOLS[index]} size={8} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/labels.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryScatter, VictoryChart } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const Labels: Story = { args: {}, render: (props) => ( <> `x: ${datum.x}`} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/log-scale.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryScatter, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getLogData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const LogScale: Story = { args: {}, render: (props) => ( <> `x: ${datum.x}`} /> `x: ${datum.x}`} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/polar.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryScatter, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const Polar: Story = { args: {}, render: (props) => ( <> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/stacked.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryScatter, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const Stacked: Story = { args: {}, render: (props) => ( <> datum.x}> datum.x}> datum.x}> datum.x}> datum.x}> datum.x}> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryScatter, VictoryChart, VictoryTheme } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const Styles: Story = { args: {}, render: (props) => ( <> datum.x} style={{ labels: { fontSize: 20, fill: "tomato", fontFamily: "monospace" }, data: { fill: "tomato" }, }} /> (datum.x === "Dog" ? "red" : "black"), }, }} labels={({ datum }) => datum.x} data={[ { x: "Cat", y: 62 }, { x: "Dog", y: 91 }, { x: "Fish", y: 55 }, { x: "Bird", y: 55 }, ]} /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/symbols.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { ScatterSymbolType, VictoryScatter, VictoryChart, VictoryTheme, } from "@/victory"; import { getMixedData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; const SYMBOLS: ScatterSymbolType[] = [ "circle", "cross", "diamond", "plus", "minus", "square", "star", "triangleDown", "triangleUp", ]; export const Symbols: Story = { args: {}, render: (props) => { return ( <> {SYMBOLS.map((symbol) => (
symbol} /> symbol} />
))} ); }, }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/theme.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryScatter, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { getData, getMixedData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const Theme: Story = { args: {}, render: (props) => ( <> datum.x} /> datum.x}> datum.x} /> datum.x}> datum.x} /> datum.x}> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/time-scale.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryScatter, VictoryChart, VictoryStack, VictoryTheme, } from "@/victory"; import { Story, ComponentMeta } from "./config"; import { getTimeData } from "../../utils/data"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const TimeScale: Story = { args: {}, render: (props) => ( <> datum.x.getFullYear()} /> datum.x.getFullYear()} /> datum.x.getFullYear()}> datum.x.getFullYear()}> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-scatter/tooltips.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryScatter, VictoryChart, VictoryTheme, VictoryTooltip, } from "@/victory"; import { getData, getMixedData } from "../../utils/data"; import { Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryScatter", }; export const Tooltips: Story = { args: {}, render: (props) => ( <> `x: ${datum.x}`} labelComponent={} /> `x: ${datum.x}`} labelComponent={} /> `x: ${datum.x}`} labelComponent={} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-tooltip/center-offset.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryTooltip, VictoryBar } from "@/victory"; import { defaultBarProps, polarBarProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryTooltip", }; export const CenterOffset: Story = { args: {}, render: (props) => ( <> } /> } /> } /> } /> } /> } /> } /> } /> } /> (datum.y < 0 ? 10 : -10), x: 10 }} text={`function\noffset`} /> } /> (datum.y < 0 ? -10 : 10), y: -10, }} text={`function\noffset`} /> } /> (Number(index) < 3 ? -10 : 10), x: 10, }} text={`function\noffset`} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-tooltip/config.ts ================================================ import type { Meta, StoryObj } from "@storybook/react"; import { VictoryTooltip, VictoryTooltipProps } from "@/victory"; import { VictoryLabelableProps } from "../../utils/arg-types"; import { componentContainer } from "../../utils/decorators"; import { getData, getMixedData } from "../../utils/data"; type StoryProps = VictoryTooltipProps & { themeKey: string; }; export const ComponentMeta: Meta> = { component: VictoryTooltip, decorators: [componentContainer], argTypes: { ...VictoryLabelableProps, active: { control: "boolean" }, activateData: { control: "boolean" }, angle: { control: "number" }, cornerRadius: { control: "number" }, dx: { control: "number" }, dy: { control: "number" }, flyoutHeight: { control: "number" }, flyoutPadding: { control: "number" }, flyoutWidth: { control: "number" }, height: { control: "number" }, horizontal: { control: "boolean" }, index: { control: "number" }, pointerOrientation: { control: "select", options: ["top", "bottom", "left", "right"], }, pointerWidth: { control: "number" }, renderInPortal: { control: "boolean" }, text: { control: "text" }, width: { control: "number" }, x: { control: "number" }, y: { control: "number" }, }, }; export type Story = StoryObj; export const defaultBarProps = { style: { labels: { fontFamily: "arial" }, data: { fill: "gold", width: 20 }, }, width: 300, height: 300, domainPadding: { y: 25 }, data: getMixedData(5), labels: () => "Label", size: 5, }; export const polarBarProps = { style: { labels: { fontFamily: "arial" }, data: { fill: "gold", width: 20 }, }, polar: true, width: 300, height: 300, domainPadding: { y: 25 }, data: getData(5), labels: () => "Label", size: 5, }; ================================================ FILE: stories/victory-charts/victory-tooltip/constrain-to-visible-area.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryTooltip, VictoryBar } from "@/victory"; import { defaultBarProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryTooltip", }; export const ConstrainToVisibleArea: Story = { args: {}, render: (props) => ( <> (datum.y > 0 ? -40 : 40), x: ({ datum }) => (datum.y > 0 ? -20 : 20), }} pointerOrientation={({ datum }) => (datum.y > 0 ? "bottom" : "top")} text={`constrain\nto\nvisible\narea`} /> } /> (datum.y > 0 ? -60 : 60), x: ({ datum }) => (datum.y > 0 ? -10 : 10), }} pointerOrientation={({ datum }) => (datum.y > 0 ? "bottom" : "top")} text={`constrain to\nvisible area`} /> } /> (datum.y > 0 ? 70 : -70), y: ({ datum }) => (datum.y > 0 ? -10 : 10), }} pointerOrientation={({ datum }) => (datum.y > 0 ? "left" : "right")} text={`constrain to\nvisible area`} /> } /> (datum.y > 0 ? 70 : -70), y: ({ datum }) => (datum.y > 0 ? -10 : 10), }} pointerOrientation={({ datum }) => (datum.y > 0 ? "left" : "right")} text={`constrain\nto\nvisible\narea`} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-tooltip/corner-radius.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryTooltip, VictoryBar } from "@/victory"; import { defaultBarProps, polarBarProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryTooltip", }; export const CornerRadius: Story = { args: {}, render: (props) => ( <> } /> } /> } /> (text === "square" ? 0 : 5)} text={({ datum }) => (datum.y < 0 ? "square" : "rounded ")} /> } /> (text === "square" ? 0 : 5)} text={({ datum }) => (datum.y < 0 ? "square" : "rounded ")} /> } /> (text === "square" ? 0 : 5)} text={({ index }) => (Number(index) > 2 ? "square" : "rounded ")} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-tooltip/default.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryTooltip, VictoryBar } from "@/victory"; import { defaultBarProps, polarBarProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryTooltip", }; export const Default: Story = { args: {}, render: (props) => ( <> } /> } /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-tooltip/disable-inline-styles.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { Flyout, VictoryLabel, VictoryTooltip, VictoryBar } from "@/victory"; import { defaultBarProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryTooltip", }; export const DisableInlineStyles: Story = { args: {}, render: (props) => ( <> } /> } labelComponent={ } /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-tooltip/flyout-height.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryTooltip, VictoryBar } from "@/victory"; import { defaultBarProps, polarBarProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryTooltip", }; export const FlyoutHeight: Story = { args: {}, render: (props) => ( <> } /> } /> } /> (text === "short" ? 20 : 50)} text={({ datum }) => (datum.y < 0 ? "short" : "tall")} /> } /> (text === "short" ? 20 : 50)} text={({ datum }) => (datum.y < 0 ? "short" : "tall")} /> } /> (text === "short" ? 20 : 50)} text={({ index }) => (Number(index) > 2 ? "short" : "tall")} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-tooltip/flyout-padding.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryTooltip, VictoryBar } from "@/victory"; import { defaultBarProps, polarBarProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryTooltip", }; export const FlyoutPadding: Story = { args: {}, render: (props) => ( <> } /> } /> datum.y > 0 ? { top: 20, left: 15, right: 5 } : 2 } text={`flyoutPadding\nfunction`} /> } /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-tooltip/flyout-style.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryTooltip, VictoryBar } from "@/victory"; import { defaultBarProps, polarBarProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryTooltip", }; export const FlyoutStyle: Story = { args: {}, render: (props) => ( <> } /> } /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-tooltip/flyout-width.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryTooltip, VictoryBar } from "@/victory"; import { defaultBarProps, polarBarProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryTooltip", }; export const FlyoutWidth: Story = { args: {}, render: (props) => ( <> } /> } /> } /> (text === "short" ? 35 : 100)} text={({ datum }) => (datum.y < 0 ? "short" : "long")} /> } /> (text === "short" ? 35 : 100)} text={({ datum }) => (datum.y < 0 ? "short" : "long")} /> } /> (text === "short" ? 35 : 100)} text={({ index }) => (Number(index) > 2 ? "short" : "long")} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-tooltip/pointer-length.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryTooltip, VictoryBar } from "@/victory"; import { defaultBarProps, polarBarProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryTooltip", }; export const PointerLength: Story = { args: {}, render: (props) => ( <> (index === 0 ? -20 : 0) }} pointerLength={30} text={`pointerLength\n30`} /> } /> (index === 0 ? 20 : 0) }} text={`pointerLength\n30`} /> } /> (index === 0 ? 20 : 0) }} text={`pointerLength\n30`} /> } /> (Number(index) < 2 ? 20 : 0) }} pointerLength={({ text }) => (text === "short" ? 1 : 30)} text={({ datum }) => (datum.y < 0 ? "short" : "long ")} /> } /> (Number(index) < 2 ? 20 : 0) }} pointerLength={({ text }) => (text === "short" ? 1 : 30)} text={({ datum }) => (datum.y < 0 ? "short" : "long ")} /> } /> (index === 0 || index === 4 ? 20 : 0), }} pointerLength={({ text }) => (text === "short" ? 1 : 30)} text={({ index }) => (Number(index) > 2 ? "short" : "long ")} /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-tooltip/pointer-orientation.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryTooltip, VictoryBar } from "@/victory"; import { defaultBarProps, polarBarProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryTooltip", }; export const PointerOrientation: Story = { args: {}, render: (props) => ( <> (datum.y > 0 ? "bottom" : "top")} text={({ datum }) => datum.y > 0 ? `orientation\nbottom` : `orientation\ntop` } /> } /> (datum.y > 0 ? -40 : 40), x: ({ datum }) => (datum.y > 0 ? -20 : 20), }} pointerOrientation={({ datum }) => (datum.y > 0 ? "bottom" : "top")} text={({ datum }) => datum.y > 0 ? `orientation\nbottom` : `orientation\ntop` } /> } /> (index < 3 ? "bottom" : "top")} text={({ index }) => Number(index) < 3 ? `orientation\nbottom` : `orientation\ntop` } /> } /> (datum.y > 0 ? 55 : -55), }} pointerOrientation={({ datum }) => (datum.y > 0 ? "left" : "right")} text={({ datum }) => datum.y > 0 ? `orientation\nleft` : `orientation\nright` } /> } /> (datum.y > 0 ? "left" : "right")} text={({ datum }) => datum.y > 0 ? `orientation\nleft` : `orientation\nright` } /> } /> index === 2 || index === 3 ? "right" : "left" } text={({ index }) => index === 2 || index === 3 ? `orientation\nleft` : `orientation\nright` } /> } /> ), }; export default meta; ================================================ FILE: stories/victory-charts/victory-tooltip/pointer-width.stories.tsx ================================================ import React from "react"; import type { Meta } from "@storybook/react"; import { VictoryTooltip, VictoryBar } from "@/victory"; import { defaultBarProps, polarBarProps, Story, ComponentMeta } from "./config"; const meta: Meta = { ...ComponentMeta, title: "Victory Charts/VictoryTooltip", }; export const PointerWidth: Story = { args: {}, render: (props) => ( <> (index === 0 ? -20 : 0) }} text={`pointerWidth\n20`} /> } /> (index === 0 ? 20 : 0) }} text={`pointerWidth\n20`} /> } /> (index === 0 ? 20 : 0) }} text={`pointerWidth\n20`} /> } /> (Number(index) < 2 ? -20 : 0) }} pointerWidth={({ text }) => (text === "skinny" ? 0 : 20)} text={({ datum }) => (datum.y < 0 ? "skinny" : "wide ")} /> } /> (Number(index) < 2 ? 20 : 0) }} pointerWidth={({ text }) => (text === "skinny" ? 0 : 20)} text={({ datum }) => (datum.y < 0 ? "skinny" : "wide ")} /> } /> (index === 0 || index === 4 ? 20 : 0), }} pointerWidth={({ text }) => (text === "skinny" ? 0 : 20)} text={({ index }) => (Number(index) > 2 ? "skinny" : "wide ")} /> } /> ), }; export default meta; ================================================ FILE: test/helpers/index.ts ================================================ export * from "./svg"; export * from "./wrappers"; ================================================ FILE: test/helpers/svg.ts ================================================ import * as d3Scale from "victory-vendor/d3-scale"; import * as d3Shape from "victory-vendor/d3-shape"; import { _internalD3Voronoi as d3Voronoi } from "victory-voronoi/lib/helper-methods"; import without from "lodash/without"; import min from "lodash/min"; import max from "lodash/max"; import property from "lodash/property"; const RECTANGULAR_SEQUENCE = ["M", "A", "L", "A", "L", "A", "L", "A", "z"]; const CIRCULAR_SEQUENCE = ["M", "m", "a", "a"]; const TRIANGULAR_SEQUENCE = ["M", "L", "L", "z"]; const CIRCULAR_SECTOR_SEQUENCE = ["M", "A", "L", "Z"]; export const calculateD3Path = (props, pathType, index = 0) => { const { width, height, padding, scale, interpolation, data, domain } = props; const scaleType = scale ? `scale${scale[0].toUpperCase() + scale.slice(1)}` : "scaleLinear"; const curveType = typeof interpolation === "string" ? `curve${interpolation[0].toUpperCase() + interpolation.slice(1)}` : undefined; const curveFunction = typeof interpolation === "function" ? interpolation : d3Shape[curveType!]; const dataDomain = data.reduce( (prev, datum) => { if (datum.x < prev.x[0]) { prev.x[0] = datum.x; } else if (datum.x > prev.x[1]) { prev.x[1] = datum.x; } if (datum.y < prev.y[0]) { prev.y[0] = datum.y; } else if (datum.y > prev.y[1]) { prev.y[1] = datum.y; } return prev; }, { x: [0, 0], y: [0, 0] }, ); const range = { x: [padding, width - padding], y: [height - padding, padding], }; const scaleX = d3Scale[scaleType]() .domain((domain && domain.x) || dataDomain.x) .range(range.x); const scaleY = d3Scale[scaleType]() .domain((domain && domain.y) || dataDomain.y) .range(range.y); switch (pathType) { case "line": { return ( d3Shape .line() .curve(curveFunction) // @ts-expect-error property x does not exist .x((d) => scaleX(d.x)) // @ts-expect-error property y does not exist .y((d) => scaleY(d.y))(data) ); } case "area": { const modifiedData = props.data.map((datum) => { return { x: datum.x, y: datum.y, y1: datum.y, y0: datum.y0 }; }); return ( d3Shape .area() .curve(curveFunction) // @ts-expect-error property x does not exist .x((d) => scaleX(d.x)) // @ts-expect-error property y1 does not exist .y1((d) => scaleY(d.y1)) // @ts-expect-error property y0 does not exist .y0((d) => scaleY(d.y0))(modifiedData) ); } case "voronoi": { const minRange = [Math.min(...range.x), Math.min(...range.y)]; const maxRange = [Math.max(...range.x), Math.max(...range.y)]; const voronoi = d3Voronoi() .x((d) => scaleX(d.x)) .y((d) => scaleY(d.y)) .extent([minRange, maxRange]); const polygons = voronoi.polygons(data); const polygon = without(polygons[index], "data"); return `M ${polygon.join("L")} Z`; } } return undefined; }; export const parseSvgPathCommands = (commandStr) => { const matches = commandStr.match( /[MmLlHhVvCcSsQqTtAaZz]+[^MmLlHhVvCcSsQqTtAaZz]*/g, ); return matches.map((match) => { const name = match.charAt(0); const args = match .substring(1) .split(",") .map((arg) => { return parseFloat(arg); }); return { raw: match, name, args, }; }); }; export const getPathCommandsFromContainer = (container) => { const commandStr = container.getAttribute("d"); return parseSvgPathCommands(commandStr); }; export const exhibitsShapeSequence = (commandString, shapeSeqeuence) => { const commands = parseSvgPathCommands(commandString); return commands.every( (command, index) => command.name === shapeSeqeuence[index], ); }; /** * Retrieve the raw svg height of a bar. * * @param {string} commandString - The "d" attribute of a `path` element. * @returns {Number} - The height of the bar in svg units. */ export const getBarHeight = (commandString) => { const commands = parseSvgPathCommands(commandString); return Math.abs(commands[0].args[1] - commands[2].args[1]); }; /** * Assert the provided element renders a 4-sided shape and return dimensions. * * @param {HTMLElement} path - An HTML path element. * @returns {Object} - Dimensions of the shape */ export const getBarShape = (path) => { const commandstring = path.getAttribute("d"); const commands = parseSvgPathCommands(commandstring); const points = commands.filter((command) => { return command.name !== "z"; }); const verticalPoints: any[] = points.map(property("args.1")); const horizontalPoints: any[] = points.map(property("args.0")); const height = max(verticalPoints) - min(verticalPoints); const width = max(horizontalPoints) - min(horizontalPoints); return { height, width, }; }; export const getDistanceFromOrigin = (coord) => { return Math.sqrt(Math.pow(coord.x, 2) + Math.pow(coord.y, 2)); }; /** * Get the angle between 2 arbitrary SVG coordinates, * using 0, 0 as origin * * @param {{x: number, y: number}} coord1 SVG coordinates for point 1 * @param {{x: number, y: number}} coord2 SVG coordinates for point 2 * @return {number} Degrees, 0 - 360 */ export const getAngleBetweenSVGCoordinates = (coord1, coord2) => { const cartesianY1 = coord1.y * -1; const cartesianY2 = coord2.y * -1; const radians = Math.atan2(cartesianY1, coord1.x) - Math.atan2(cartesianY2, coord2.x); const theta = radians * (180 / Math.PI); return theta < 0 ? 360 + theta : theta; }; /** * Get the initial coordinates of the arc drawn in an SVG pie slice * * @param {String} sliceCommandString The command attribute of a `path` element * @return {{x: number, y: number}} SVG coordinates */ export const getSliceArcStart = (sliceCommandString) => { const cmds = parseSvgPathCommands(sliceCommandString); return { x: cmds[0].args[0], y: cmds[0].args[1], }; }; /** * Get the final coordinates of the arc drawn in an SVG pie slice * * @param {String} sliceCommandString The command attribute of a `path` element * @return {{x: number, y: number}} SVG coordinates */ export const getSliceArcEnd = (sliceCommandString) => { const cmds = parseSvgPathCommands(sliceCommandString); // @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#Arcto return { x: cmds[1].args[5], y: cmds[1].args[6], }; }; /** * Translate SVG coordinates to cartesian system and get clockwise * from positive Y axis * * @param {{x: number, y: number}} coord X & Y values in SVG coordinate system * @return {number} Degrees from normal Cartesian positive Y axis axis, counter clockwise */ export const getSvgCoordinatesAngleFromCartesianYAxis = (coord) => { const cartesianY = coord.y * -1; // Y coordinate in SVG system is inverse of normal Cartesian const theta = Math.atan2(coord.x, cartesianY) * (180 / Math.PI); return theta < 0 ? 360 + theta : theta; }; /** * Determines if a rectangular shape is produced from the provided path command. * * @param {String} commandString - The command attribute of a `path` element. * @returns {Boolean} - Boolean indicating if the command string produces a rectangular shape. */ export const isBar = (commandString) => exhibitsShapeSequence(commandString, RECTANGULAR_SEQUENCE); /** * Determines if a circular shape is produced from the provided path command. * * @param {String} commandString - The command attribute of a `path` element. * @returns {Boolean} - Boolean indicating if the command string produces a circular shape. */ export const isCircle = (commandString) => exhibitsShapeSequence(commandString, CIRCULAR_SEQUENCE); /** * Determines if a triangular shape is produced from the provided path command. * * @param {String} commandString - The command attribute of a `path` element. * @returns {Boolean} - Boolean indicating if the command string produces a triangular shape. */ export const isTriangle = (commandString) => exhibitsShapeSequence(commandString, TRIANGULAR_SEQUENCE); /** * Determines if a circular sector (slice of pie) shape is produced from the provided path command. * * @param {String} commandString - The command attribute of a `path` element. * @returns {Boolean} - Boolean indicating if the command string produces a circular sector shape. */ export const isCircularSector = (commandString) => exhibitsShapeSequence(commandString, CIRCULAR_SECTOR_SEQUENCE); export const getSvgPointCoordinates = (container) => { const commands = getPathCommandsFromContainer(container); return commands[0].args; }; /** * Convert the raw svg coordinates to scaled Cartesian coordinates. * * @param {Array} coords - The x and y coordinates, respectively. * @param {Object} svgDimensions - The width, height, and padding of the svg. * @param {Number} svgDimensions.width - The width of the svg. * @param {Number} svgDimensions.height - The height of the svg. * @param {Number} svgDimensions.padding - The space between the edge of the * svg and the chart area. * @param {Object} domain - The x and y domains. * @param {Array} domain.x - The lower and upper x bounds, respectively. * @param {Array} domain.y - The lower and upper y bounds, respectively. * @returns {Array} The Cartesian x and y coordinates, respectively. */ export const convertSvgCoordinatesToCartesian = ( coords, svgDimensions, domain, ) => { const { width, height, padding } = svgDimensions; const cartesianX = coords[0] - padding; const cartesianY = height - coords[1] - padding; const chartWidth = width - padding * 2; const chartHeight = height - padding * 2; const scaledX = (cartesianX / chartWidth) * (domain.x[1] - domain.x[0]); const scaledY = (cartesianY / chartHeight) * (domain.y[1] - domain.y[0]); const shiftedX = scaledX + domain.x[0]; const shiftedY = scaledY + domain.y[0]; return [shiftedX, shiftedY]; }; ================================================ FILE: test/helpers/wrappers.tsx ================================================ import * as React from "react"; export function SVGWrapper(props: any) { return ; } ================================================ FILE: test/jest-setup.ts ================================================ import "@testing-library/jest-dom/matchers"; import "@testing-library/jest-dom/jest-globals"; ================================================ FILE: test/jest.config.ts ================================================ // paths are relative to the root of the package they are executed in const jestConfig = { preset: "ts-jest", testEnvironment: "jsdom", testMatch: ["**/src/**/?(*.)+(spec|test).[jt]s?(x)"], setupFilesAfterEnv: ["../../test/jest-setup.ts"], }; export default jestConfig; ================================================ FILE: test/tsconfig.json ================================================ { "extends": "../tsconfig.base.json" } ================================================ FILE: tsconfig.base.json ================================================ { "extends": "./tsconfig.json", } ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "target": "ES2015", "module": "es6", "jsx": "react", "declaration": true, "declarationMap": true, "sourceMap": true, "isolatedModules": true, "strict": true, "noImplicitAny": false, "moduleResolution": "node", "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "baseUrl": ".", "lib": [ "dom", "dom.iterable", "esnext" ], "paths": { "@/*": ["packages/*"] }, "types": ["@testing-library/jest-dom"], }, } ================================================ FILE: tsconfig.storybook.json ================================================ { "extends": "./tsconfig.json", "include": ["stories/**/*"], "compilerOptions": { "noEmit": true } } ================================================ FILE: vercel.json ================================================ { "redirects": [ { "source": "/open-source/victory/docs", "destination": "/open-source/victory/docs/introduction", "permanent": true }, { "source": "/open-source/victory/docs/faq", "destination": "/open-source/victory/docs/introduction", "permanent": true }, { "source": "/open-source/victory/gallery", "destination": "/open-source/victory/docs/introduction", "permanent": true }, { "source": "/open-source/victory/gallery/:slug", "destination": "/open-source/victory/docs/introduction", "permanent": true }, { "source": "/open-source/victory/about", "destination": "/open-source/victory", "permanent": true }, { "source": "/open-source/victory/docs/victory-area", "destination": "/open-source/victory/docs/api/victory-area", "permanent": true }, { "source": "/open-source/victory/docs/react-native", "destination": "/open-source/victory/docs/api/react-native", "permanent": true }, { "source": "/open-source/victory/docs/server-side-rendering", "destination": "/open-source/victory/docs/api/server-side-rendering", "permanent": true }, { "source": "/open-source/victory/docs/victory-axis", "destination": "/open-source/victory/docs/api/victory-axis", "permanent": true }, { "source": "/open-source/victory/docs/victory-bar", "destination": "/open-source/victory/docs/api/victory-bar", "permanent": true }, { "source": "/open-source/victory/docs/victory-box-plot", "destination": "/open-source/victory/docs/api/victory-box-plot", "permanent": true }, { "source": "/open-source/victory/docs/victory-candlestick", "destination": "/open-source/victory/docs/api/victory-candlestick", "permanent": true }, { "source": "/open-source/victory/docs/victory-error-bar", "destination": "/open-source/victory/docs/api/victory-error-bar", "permanent": true }, { "source": "/open-source/victory/docs/victory-histogram", "destination": "/open-source/victory/docs/api/victory-histogram", "permanent": true }, { "source": "/open-source/victory/docs/victory-line", "destination": "/open-source/victory/docs/api/victory-line", "permanent": true }, { "source": "/open-source/victory/docs/victory-pie", "destination": "/open-source/victory/docs/api/victory-pie", "permanent": true }, { "source": "/open-source/victory/docs/victory-polar-axis", "destination": "/open-source/victory/docs/api/victory-polar-axis", "permanent": true }, { "source": "/open-source/victory/docs/victory-scatter", "destination": "/open-source/victory/docs/api/victory-scatter", "permanent": true }, { "source": "/open-source/victory/docs/victory-voronoi", "destination": "/open-source/victory/docs/api/victory-voronoi", "permanent": true }, { "source": "/open-source/victory/docs/victory-chart", "destination": "/open-source/victory/docs/api/victory-chart", "permanent": true }, { "source": "/open-source/victory/docs/victory-brush-container", "destination": "/open-source/victory/docs/api/victory-brush-container", "permanent": true }, { "source": "/open-source/victory/docs/victory-clip-container", "destination": "/open-source/victory/docs/api/victory-clip-container", "permanent": true }, { "source": "/open-source/victory/docs/victory-container", "destination": "/open-source/victory/docs/api/victory-container", "permanent": true }, { "source": "/open-source/victory/docs/victory-cursor-container", "destination": "/open-source/victory/docs/api/victory-cursor-container", "permanent": true }, { "source": "/open-source/victory/docs/victory-group", "destination": "/open-source/victory/docs/api/victory-group", "permanent": true }, { "source": "/open-source/victory/docs/victory-selection-container", "destination": "/open-source/victory/docs/api/victory-selection-container", "permanent": true }, { "source": "/open-source/victory/docs/victory-stack", "destination": "/open-source/victory/docs/api/victory-stack", "permanent": true }, { "source": "/open-source/victory/docs/victory-voronoi-container", "destination": "/open-source/victory/docs/api/victory-voronoi-container", "permanent": true }, { "source": "/open-source/victory/docs/victory-zoom-container", "destination": "/open-source/victory/docs/api/victory-zoom-container", "permanent": true }, { "source": "/open-source/victory/docs/common-container-props", "destination": "/open-source/victory/docs/api/victory-common-container-props", "permanent": true }, { "source": "/open-source/victory/docs/create-container", "destination": "/open-source/victory/docs/guides/containers", "permanent": true }, { "source": "/open-source/victory/docs/victory-accessible-group", "destination": "/open-source/victory/docs/api/victory-accessible-group", "permanent": true }, { "source": "/open-source/victory/docs/victory-animation", "destination": "/open-source/victory/docs/api/victory-animation", "permanent": true }, { "source": "/open-source/victory/docs/victory-brush-line", "destination": "/open-source/victory/docs/api/victory-brush-line", "permanent": true }, { "source": "/open-source/victory/docs/victory-label", "destination": "/open-source/victory/docs/api/victory-label", "permanent": true }, { "source": "/open-source/victory/docs/victory-legend", "destination": "/open-source/victory/docs/api/victory-legend", "permanent": true }, { "source": "/open-source/victory/docs/victory-portal", "destination": "/open-source/victory/docs/api/victory-portal", "permanent": true }, { "source": "/open-source/victory/docs/victory-primitives", "destination": "/open-source/victory/docs/api/victory-primitives", "permanent": true }, { "source": "/open-source/victory/docs/victory-shared-events", "destination": "/open-source/victory/docs/api/victory-shared-events", "permanent": true }, { "source": "/open-source/victory/docs/victory-theme", "destination": "/open-source/victory/docs/api/victory-theme", "permanent": true }, { "source": "/open-source/victory/docs/victory-tooltip", "destination": "/open-source/victory/docs/api/victory-tooltip", "permanent": true }, { "source": "/open-source/victory/docs/victory-transition", "destination": "/open-source/victory/docs/api/victory-transition", "permanent": true }, { "source": "/open-source/victory/docs/victory-canvas", "destination": "/open-source/victory/docs/api/victory-canvas", "permanent": true }, { "source": "/open-source/victory/docs/common-props", "destination": "/open-source/victory/docs/api/victory-common-props", "permanent": true } ] } ================================================ FILE: website/.gitignore ================================================ # Dependencies /node_modules # Production /build # Generated files .docusaurus .cache-loader # Misc .DS_Store .env.local .env.development.local .env.test.local .env.production.local npm-debug.log* yarn-debug.log* yarn-error.log* ================================================ FILE: website/README.md ================================================ # Website This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. ### Installation ``` $ yarn ``` ### Local Development ``` $ yarn start ``` This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. ### Build ``` $ yarn build ``` This command generates static content into the `build` directory and can be served using any static contents hosting service. ### Deployment This site is deployed using Vercel, which will automatically detect the site config and deploy ================================================ FILE: website/babel.config.js ================================================ module.exports = { presets: [require.resolve("@docusaurus/core/lib/babel/preset")], }; ================================================ FILE: website/docs/api/_category_.json ================================================ { "label": "API", "position": 3, "link": null } ================================================ FILE: website/docs/api/victory-accessible-group.mdx ================================================ --- title: VictoryAccessibleGroup --- A specialized group container that enables users to assign `aria-label`s, `desc`s and other props specified below which allow for improved access by screen readers. `VictoryAccessibleGroup` can be used as any `groupComponent` prop value. `VictoryAccessibleGroup` will render its children in a `` element and includes a `desc` tag if provided as a prop. ## Props --- ### aria-describedby The `aria-describedby` prop applies to the `g` element rendered by `VictoryAccessibleGroup` as well as `descId` if a `desc` is provided. This prop should be given as a string corresponding to the id of an element that describes the chart. --- ### aria-label The `aria-label` prop applies to the `g` element rendered by `VictoryAccessibleGroup`. --- ### children `VictoryAccessibleGroup` renders a single child, or an array of children in the group element. --- ### className The `className` prop specifies a className that will be applied to the `g` element rendered by `VictoryAccessibleGroup`. If this prop is not set, the className will default to "VictoryAccessibleGroup". _example:_ `className="myChartAccessibleGroup"` --- ### desc The `desc` prop specifies the description of the chart/SVG to assist with accessibility for screen readers. The more descriptive this title is, the more useful it will be for people using screen readers. _example:_ `desc="Golden retrievers make up 30%, Labs make up 25%, and other dog breeds are not represented above 5% each."` --- ### tabIndex The `tabIndex` will be applied to the `g` element. ================================================ FILE: website/docs/api/victory-animation.mdx ================================================ --- title: VictoryAnimation --- :::info [See this guide to animations with Victory Charts](/docs/guides/animations). The `VictoryAnimation` component is used for animating arbitrary React components and is not commonly used. ::: `VictoryAnimation` animates prop changes for any React component. To animate prop changes, define a child function that accepts an object of tweened values and other animation information and returns a component to render. ```jsx {(tweenedProps, animationInfo) => { if (animationInfo.animating && animationInfo.progress < 1) { return ; } }} ``` ## Props --- ### children `VictoryAnimation` takes a single child, which should be given as a function of a tweened props object and an animation information object. The child function should return a component to render. --- ### data The `data` prop specifies a set of values to tween between. When this prop changes, `VictoryAnimation` will begin animating between the current and next values. This prop should be given as an array or an object. `VictoryAnimation` uses [d3-interpolate][] to tween between values, with some [slight modifications][]. --- ### duration The `duration` prop determines the number of milliseconds the animation should take to complete. This prop should be given as a number. --- ### delay The `delay` prop specifies a delay in milliseconds before the start of an animation, or between each animation in the queue. This prop should be given as a number. ### easing The `easing` prop specifies the type of easing to use for an animation. The supported types of easing are: _"back", "backIn", "backOut", "backInOut", "bounce", "bounceIn", "bounceOut", "bounceInOut", "circle", "circleIn", "circleOut", "circleInOut", "linear", "linearIn", "linearOut", "linearInOut", "cubic", "cubicIn", "cubicOut", "cubicInOut", "elastic", "elasticIn", "elasticOut", "elasticInOut", "exp", "expIn", "expOut", "expInOut", "poly", "polyIn", "polyOut", "polyInOut", "quad", "quadIn", "quadOut", "quadInOut", "sin", "sinIn", "sinOut", "sinInOut"_. --- ### onEnd The `onEnd` prop specifies a function that will be called when the animation ends. If there are multiple animations in the queue, the `onEnd` function will be called after the last animation in the queue completes. [d3-interpolate]: https://github.com/d3/d3-interpolate [slight modifications]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-animation/util.ts ================================================ FILE: website/docs/api/victory-area.mdx ================================================ --- title: VictoryArea --- :::info For examples of `VictoryArea` in action, visit the [Area Chart](/docs/charts/area) examples. ::: ## Inherited Props ## Component Props --- ### dataComponent `VictoryArea` overrides the standard [`dataComponent`](/docs/api/victory-datatable-props#datacomponent) prop and supplies the following props to its `dataComponent`: - `data` - `events` - `groupComponent` - `interpolation` - `origin` (for polar charts) - `polar` - `scale` - `style` :::note Because `VictoryArea` renders a single element to represent the entire dataset, the `dataComponent` it renders will not have access to `datum` like the `dataComponent` elements rendered by other Victory components such as `VictoryScatter`. ::: --- ### eventKey `VictoryArea` uses the standard `eventKey` prop. [Read about the `eventKey` prop in more detail here](/docs/guides/events) :::note `VictoryArea` only renders one element per dataset, so only one event key will be generated. ::: --- ### events `VictoryArea` uses the standard `events` prop. [Read about it in detail](/docs/guides/events) See the [Events Guide](/docs/guides/events) for more information on defining events. :::note `VictoryArea` uses the special `eventKey` "all" rather than referring to data by index, as it renders only one element for an entire dataset ::: ```jsx live

Click the area chart element

{ return [ { eventKey: "all", mutation: (props) => { const fill = props.style && props.style.fill; return fill === "black" ? null : { style: { fill: "black", }, }; }, }, ]; }, }, }, ]} data={sampleData} theme={VictoryTheme.clean} />
``` --- ### groupComponent `VictoryArea` uses the standard `groupComponent` prop. [Read about it in detail](/docs/api/victory-common-theme-props#groupcomponent) :::note `VictoryArea` uses [`VictoryClipContainer`](/docs/api/victory-clip-container) as its default `groupComponent` `VictoryClipContainer` renders a `` tag with a `clipPath` `def`. This allows continuous data components to transition smoothly when new data points enter and exit. ::: :::warning Using a custom `groupComponent` with `VictoryArea` may result in broken animations. ::: ```jsx live } style={{ data: { stroke: "#c43a31", strokeWidth: 5, strokeLinecap: "round", }, }} data={sampleData} /> ``` --- ### interpolation The `interpolation` prop determines how data points should be connected when creating a path. Victory uses [d3-shape](https://github.com/d3/d3-shape#curves) for interpolating curves. #### Interpolation Options Both cartesian and polar charts may use the following interpolation options: - `"basis"` - `"cardinal"` - `"catmullRom"` - `"linear"` Cartesian charts may also use the following interpolation options: - `"monotoneX"` - `"monotoneY"` - `"natural"` - `"step"` - `"stepAfter"` - `"stepBefore"` You can also provide a [custom curve function](https://github.com/d3/d3-shape#custom-curves). ```jsx live ``` --- ### labelComponent `VictoryArea` uses the standard `labelComponent` prop. [Read about it in detail](/docs/api/victory-datatable-props#datacomponent) :::note To enable tooltips on `VictoryArea`, it is necessary to use [`VictoryVoronoiContainer`](/docs/api/victory-voronoi-container) ::: ```jsx live datum.y} labelComponent={ } theme={VictoryTheme.clean} /> ``` --- ### style Defines the style of the component using [VictoryStyleInterface](/docs/api/victory-style-interface). :::note Since `VictoryArea` renders a single element to represent an entire dataset, it is not possible to use functional styles to change the style of the line as a function of an individual `datum`. Instead, try using [gradient fills](/docs/guides/themes#using-gradient-fills) for styling continuous data. ::: ```jsx live datum.x === 3 ? "#000000" : "#c43a31", }, }} data={sampleData} labels={({ datum }) => datum.x} theme={VictoryTheme.clean} /> ``` ================================================ FILE: website/docs/api/victory-axis-common-props.mdx ================================================ --- title: VictoryAxisCommonProps --- A set of props available to components that implement an Axis in Victory. ## Props --- ### axisComponent The `axisComponent` prop takes a component instance which will be responsible for rendering an axis line. The new element created from the passed `axisComponent` will be provided with the following props calculated by `VictoryAxis`: `x1`, `y1`, `x2`, `y2`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If an `axisComponent` is not provided, `VictoryAxis` will use its default [LineSegment][] component. ```jsx axisComponent={} ``` --- ### axisLabelComponent The `axisLabelComponent` prop takes a component instance which will be used to render the axis label. The new element created from the passed `axisLabelComponent` will be supplied with the following props: `x`, `y`, `verticalAnchor`, `textAnchor`, `angle`, `transform`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `axisLabelComponent` is omitted, a new [`VictoryLabel`][] will be created with props described above. ```jsx axisLabelComponent={} ``` --- ### axisValue The `axisValue` prop may be used to position the dependent axis. This prop is useful when dependent axes should line up with values on the independent axis. --- ### dependentAxis The `dependentAxis` boolean prop specifies whether the axis corresponds to the dependent variable (usually y). This prop is useful when composing `VictoryAxis` with other components to form a chart. ```jsx live ``` --- ### gridComponent The `gridComponent` prop takes a component instance which will be responsible for rendering a grid element. The new element created from the passed `gridComponent` will be provided with the following props calculated by `VictoryAxis`: `x1`, `y1`, `x2`, `y2`, `tick`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If a `gridComponent` is not provided, `VictoryAxis` will use its default [LineSegment][] component. ```jsx gridComponent={} ``` --- ### invertAxis The `invertAxis` boolean prop specifies whether the domain for a given axis should be inverted. By default, axes will be displayed with lower values on the bottom / left, and higher values on the top / right regardless of orientation. --- ### style The `style` prop defines the style of the component. The style prop should be given as an object with styles defined for `parent`, `axis`, `axisLabel`, `grid`, `ticks`, and `tickLabels`. Any valid svg styles are supported, but `width`, `height`, and `padding` should be specified via props as they determine relative layout for components in VictoryChart. Functional styles may be defined for `grid`, `tick`, and `tickLabel` style properties, and they will be evaluated with the props corresponding to each of these elements, such as `tick`, `index`, and `text`. ```ts style?: { parent?: VictoryStyleObject; axis?: VictoryStyleObject; axisLabel?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; grid?: VictoryStyleObject; ticks?: VictoryTickStyleObject; tickLabels?: VictoryLabelStyleObject | VictoryLabelStyleObject[]; }; ``` :::note When a component is rendered as a child of another Victory component, or within a custom `` element with `standalone={false}` parent styles will be applied to the enclosing `` tag. Many styles that can be applied to a parent `` will not be expressed when applied to a ``. ::: :::note custom `angle` and `verticalAnchor` properties may be included in `labels` styles. ::: ```jsx live tick > 0.5 ? "red" : "grey", }, ticks: { stroke: "grey", size: 5 }, tickLabels: { fontSize: 15, padding: 5, }, }} /> ``` --- ### tickComponent The `tickComponent` prop takes a component instance which will be responsible for rendering a tick element. The new element created from the passed `tickComponent` will be provided with the following props calculated by `VictoryAxis`: `x1`, `y1`, `x2`, `y2`, `tick`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If a `tickComponent` is not provided, `VictoryAxis` will use its default [LineSegment][] component. ```jsx tickComponent={} ``` --- ### tickCount Specifies approximately how many ticks should be drawn on the axis. If an array of ticks is supplied in `tickValues` or `tickFormat`, the `tickCount` prop will be used to _downsample_ the provided array to the specified length. If `tickValues` are not explicitly provided, this value is used by [d3Scale][] to calculate an _approximate_ number of ticks. [d3Scale][] prioritizes returning "nice" values and evenly spaced ticks over an exact number of ticks. This prop must be given as a positive integer. --- ### tickFormat Specifies how tick values should be labeled. The `tickFormat` prop can be given as an array of values to display for each tick, or as a function to be applied to every `tickValue`. When given as a function, `tickFormat` will be called with the following arguments: `tick` - the individual tick value, `index` - the index of the tick in the array, and `ticks` - the entire array of ticks. ```jsx live `${Math.round(t)}k` } /> ``` --- ### tickLabelComponent The `tickLabelComponent` prop takes a component instance which will be used to render the axis label. The new element created from the passed `tickLabelComponent` will be supplied with the following props: `x`, `y`, `text`, `verticalAnchor`, `textAnchor`, `angle`, `transform`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `tickLabelComponent` is omitted, a new [`VictoryLabel`][] will be created with props described above. ```jsx tickLabelComponent={} ``` --- ### tickValues `type: array` The `tickValues` prop explicitly specifies a set of tick values to draw on the axis. This prop should be given as an array of unique values of the same type (_i.e.,_ all numbers). The `tickValues` prop is used to specify the _values_ of each tick, so numeric values are typically appropriate. An array of strings or dates may be supplied for categorical and time series data respectively. Use the [tickFormat][] prop to specify how ticks should be labeled. _Note:_ `tickValues` should be given as a unique array. ```jsx live ``` [tickformat]: /docs/api/victory-axis-common-props#tickformat [d3scale]: https://github.com/d3/d3-scale [linesegment]: /docs/api/victory-primitives#linesegment [`victorylabel`]: /docs/api/victory-label ================================================ FILE: website/docs/api/victory-axis.mdx ================================================ --- title: VictoryAxis --- :::info For examples of `VictoryAxis` in action, visit the [Chart Axis](/docs/guides/axis) guide. ::: ## Inherited Props ## Component Props --- ### crossAxis The `crossAxis` boolean prop specifies whether a given axis is intended to cross another axis. When this prop is true, zeroes will be removed from the array of ticks so that they do not clutter the origin of the chart. :::note When `VictoryAxis` is nested within `VictoryChart`, `VictoryChart` will determine a value for the `crossAxis` prop based on domain, but this prop may be overridden by supplying a `crossAxis` prop directly to the `VictoryAxis` child component. ::: --- ### domain The `domain` prop describes the range of data the component will include. This prop can be given as an array of the minimum and maximum expected values of the data or as an object that specifies separate arrays for x and y. If this prop is not provided, a domain will be calculated from data, or other available information. :::note The `x` value supplied to the `domain` prop refers to the _independent_ variable, and the `y` value refers to the _dependent_ variable. This may cause confusion in horizontal charts, as the independent variable will corresponds to the y axis. ::: --- ### fixLabelOverlap When true, this prop reduces the number of tick labels to fit the length of the axis. Labels are removed at approximately even intervals from the original array of labels. This feature only works well for labels that are approximately evenly spaced. --- ### offsetX The `offsetX` prop defines how far from the edge of its permitted area an axis should be offset in the x direction. If this prop is not given, the offset will be calculated based on font size, axis orientation, and label padding. When `VictoryAxis` is used with `VictoryChart`, `VictoryChart` will determine a value for `offsetX` that makes the axes line up correctly, but this value may be overridden by supplying an `offsetX` prop directly to the `VictoryAxis` child component. :::note The `offsetX` prop is relative to the edge corresponding to the orientation of the axis, _e.g._ the left edge when `orientation="left"`. ::: ```jsx live ``` --- ### offsetY The `offsetY` prop defines how far from the edge of its permitted area an axis should be offset in the y direction. If this prop is not given, the offset will be calculated based on font size, axis orientation, and label padding. When `VictoryAxis` is used with `VictoryChart`, `VictoryChart` will determine a value for `offsetY` that makes the axes line up correctly, but this value may be overridden by supplying an `offsetY` prop directly to the `VictoryAxis` child component. :::note The `offsetY` prop is relative to the edge corresponding to the orientation of the axis, _e.g._ the bottom edge when `orientation="bottom"`. ::: ```jsx live ``` --- ### orientation The `orientation` prop specifies the position and orientation of your axis. Options are "top", "bottom", "left", and "right". ```jsx live ``` [animations guide]: /docs/guides/animations [events guide]: /docs/guides/events [themes guide]: /docs/guides/themes [`victorychart`]: /docs/api/victory-chart [tickformat]: /docs/api/victory-axis-common-props#tickformat [d3scale]: https://github.com/d3/d3-scale [grayscale theme]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-theme/grayscale.tsx [linesegment component]: /docs/api/victory-primitives#linesegment [`victorylabel`]: /docs/api/victory-label ================================================ FILE: website/docs/api/victory-bar.mdx ================================================ --- title: VictoryBar --- :::info For examples of `VictoryBar` in action, visit the [Bar Chart](/docs/charts/bar) examples. ::: ## Inherited Props ## Component Props --- ### alignment The `alignment` prop specifies how bars should be aligned relative to their data points. This prop may be given as "start", "middle" or "end". When this prop is not specified, bars will have "middle" alignment relative to their data points. ```jsx live ``` --- ### barRatio The `barRatio` prop specifies an _approximate_ ratio between bar widths and spaces between bars. When width is not specified via the `barWidth` prop or in bar styles, the `barRatio` prop will be used to calculate a default width for each bar given the total number of bars in the data series and the overall width of the chart. ```jsx live ``` --- ### barWidth The `barWidth` prop is used to specify the width of each bar. This prop may be given as a number of pixels or as a function that returns a number. When this prop is given as a function, it will be evaluated for each bar with the props object corresponding to that bar. When this value is not given, a default value will be calculated based on the overall dimensions of the chart, and the number of bars. :::note It is still possible to define bar width via the style prop with the `width` attribute, but `barWidth` will take precedence. ::: ```jsx live datum.x * 7} /> ``` --- ### cornerRadius The `cornerRadius` prop specifies a radius to apply to each bar. If this prop is given as a single number, the radius will only be applied to the _top_ of each bar. When this prop is given as a function, it will be evaluated for each bar with the props object corresponding to that bar. ```jsx live datum.x * 4 }} /> ``` --- ### getPath The `getPath` prop is used to customize the path of the bar. This prop should be given as a function that takes an object of props and returns a string. The `getPath` function will be called with the props object for each bar. --- ### style Defines the style of the component using [VictoryStyleInterface](/docs/api/victory-style-interface). ```jsx live datum.x === 3 ? "#000000" : "#c43a31", stroke: ({ index }) => +index % 2 === 0 ? "#000000" : "#c43a31", fillOpacity: 0.7, strokeWidth: 3 }, labels: { fontSize: 15, fill: ({ datum }) => datum.x === 3 ? "#000000" : "#c43a31" } }} data={sampleData} labels={({ datum }) => datum.x} /> ``` [animations guide]: /docs/guides/animations [data accessors guide]: /docs/guides/data-accessors [custom components guide]: /docs/guides/custom-components [events guide]: /docs/guides/events [themes guide]: /docs/guides/themes [`victorychart`]: /docs/api/victory-chart [grayscale theme]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-theme/grayscale.tsx ================================================ FILE: website/docs/api/victory-boxplot.mdx ================================================ --- title: VictoryBoxPlot --- :::info For examples of `VictoryBoxPlot` in action, visit the [Box Plot](/docs/charts/box-plot) examples. ::: ## Inherited Props ## Component Props --- ### boxWidth The `boxWidth` prop specifies how wide each box should be. If the `whiskerWidth` prop is not set, this prop will also determine the width of the whisker crosshair. ```jsx live ``` --- ### data The `data` prop for `VictoryBoxPlot` may be given in a a variety of formats: - As an array of standard data objects with values specified for `x` and `y` When given in this format, repeated values for `x` will be used for calculating summary statistics ```jsx data={[ { x: 1, y: 2 }, { x: 1, y: 3 }, { x: 1, y: 5 }, { x: 2, y: 1 }, { x: 2, y: 4 }, { x: 2, y: 5 }, ... ]} ``` - As an array of data objects where `y` is given as an array of values When given in this format, array values are used for calculating summary statistics. ```jsx data={[ { x: 1, y: [1, 2, 3, 5] }, { x: 2, y: [3, 2, 8, 10] }, { x: 3, y: [2, 8, 6, 5] }, { x: 4, y: [1, 3, 2, 9] } ]} ``` - As an array of data objects with pre-calculated summary statistics(`min`, `median`, `max`, `q1`, `q3`) When given in this format, `VictoryBoxPlot` _will not_ perform statistical analysis. Pre-calculating summary statistics for large datasets will improve performance. ```ts data={[ { x: 1, min: 2, median: 5, max: 10, q1: 3, q3: 7 }, { x: 2, min: 1, median: 4, max: 9, q1: 3, q3: 6 }, { x: 3, min: 1, median: 6, max: 12, q1: 4, q3: 10 }, }] ``` Use the [`x`][], [`y`][], [`min`][], [`max`][], [`median`][], [`q1`][], and [`q3`][] data accessor props to specify custom data formats. Refer to the [Data Accessors Guide][] for more detail. ### eventKey Specifies how event targets are addressed. **This prop is not commonly used.** [Read about the `eventKey` prop in more detail here](/docs/guides/events) --- ### events `VictoryBoxPlot` uses the standard `events` prop. [Read about it here](/docs/guides/events) See the [Events Guide][] for more information on defining events. :::note valid event targets for `VictoryBoxPlot` are: "min", "minLabels", "grid", "ticks", and "tickLabels". ::: ```jsx live

Click a data bar below

null} events={[ { target: "q3", eventHandlers: { onClick: () => { return [ { mutation: (props) => { return { style: Object.assign( props.style, { fill: "tomato", }, ), }; }, }, ]; }, }, }, ]} data={[ { x: 1, y: [1, 2, 3, 5] }, { x: 2, y: [3, 2, 8, 10] }, { x: 3, y: [2, 8, 6, 5] }, { x: 4, y: [1, 3, 2, 9] }, ]} theme={VictoryTheme.clean} />
``` --- ### labelOrientation The `labelOrientation` prop determines where labels are placed relative to their corresponding data. If this prop is not set, it will be set to "top" for horizontal charts, and "right" for vertical charts. ```jsx live ``` --- ### labels When the boolean `labels` prop is set to `true`, the values for `min`, `max`, `median`, `q1`, and `q3` will be displayed for each box. For more granular label control, use the individual [`minLabels`][], [`maxLabels`][], [`medianLabels`][], [`q1Labels`][], and [`q3Labels`][] props. --- ### max Defines the max value of a box plot. **string:** specify which property in an array of data objects should be used as the max value _examples:_ `max="max_value"` **function:** use a function to translate each element in a data array into a max value _examples:_ `max={() => 10}` **path string or path array:** specify which property in an array of nested data objects should be used as a max value _examples:_ `max="bonds.max"`, `max={["bonds", "max"]}` --- ### maxComponent The `maxComponent` prop takes a component instance which will be responsible for rendering an element to represent the maximum value of the box plot. The new element created from the passed `maxComponent` will be provided with the following props calculated by `VictoryBoxPlot`: `datum`, `index`, `scale`, `style`, `events`, `majorWhisker` and `minorWhisker`. The `majorWhisker` and `minorWhisker` props are given as objects with values for `x1`, `y1`, `x2` and `y2` that describes the lines that make up the major and minor whisker. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If a `maxComponent` is not provided, `VictoryBoxPlot` will use its default [Whisker component][]. See the [Custom Components Guide][] for more detail on creating your own components ```jsx maxComponent={} ``` --- ### maxLabelComponent The `maxLabelComponent` prop takes a component instance which will be used to render the label corresponding to the maximum value for each box. The new element created from the passed `maxLabelComponent` will be supplied with the following props: `x`, `y`, `datum`, `index`, `scale`, `verticalAnchor`, `textAnchor`, `angle`, `transform`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `maxLabelComponent` is omitted, a new [`VictoryLabel`][] will be created with props described above. See the [Custom Components Guide][] for more detail on creating your own components ```jsx maxLabelComponent={} ``` ```jsx live } theme={VictoryTheme.clean} /> ``` --- ### maxLabels The `maxLabels` prop defines the labels that will appear above each point. This prop should be given as a boolean, an array or as a function of the props corresponding to that label. When given as a boolean value, the max value of each datum will be used for the label. #### Common Usage - `maxLabels` - `maxLabels={["first", "second", "third"]}` - `maxLabels={({ datum }) => Math.round(datum.max)}` --- ### median Use the `median` data accessor prop to define the median value of a box plot. **string:** specify which property in an array of data objects should be used as the median value _examples:_ `median="median_value"` **function:** use a function to translate each element in a data array into a median value _examples:_ `median={() => 10}` **path string or path array:** specify which property in an array of nested data objects should be used as a median value _examples:_ `median="bonds.median"`, `median={["bonds", "median"]}` --- ### medianComponent The `medianComponent` prop takes a component instance which will be responsible for rendering an element to represent the median value of the box plot. The new element created from the passed `medianComponent` will be provided with the following props calculated by `VictoryBoxPlot`: `datum`, `index`, `scale`, `style`, `events`, `x1`, `y1`, `x2` and `y2` Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If a `medianComponent` is not provided, `VictoryBoxPlot` will use its default [Line component][]. See the [Custom Components Guide][] for more detail on creating your own components ```jsx medianComponent={} ``` --- ### medianLabelComponent The `medianLabelComponent` prop takes a component instance which will be used to render the label corresponding to the median value for each box. The new element created from the passed `medianLabelComponent` will be supplied with the following props: `x`, `y`, `datum`, `index`, `scale`, `verticalAnchor`, `textAnchor`, `angle`, `transform`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `medianLabelComponent` is omitted, a new [`VictoryLabel`][] will be created with props described above. See the [Custom Components Guide][] for more detail on creating your own components ```jsx medianLabelComponent={} ``` ```jsx live } theme={VictoryTheme.clean} /> ``` --- ### medianLabels The `medianLabels` prop defines the labels that will appear above each point. This prop should be given as a boolean, an array or as a function of the props corresponding to that label. When given as a boolean value, the median value of each datum will be used for the label. #### Common Usage - `medianLabels` - `medianLabels={["first", "second", "third"]}` - `medianLabels={({ datum }) => Math.round(datum.median)}` --- ### min Use the `min` data accessor prop to define the min value of a box plot. **string:** specify which property in an array of data objects should be used as the min value _examples:_ `min="min_value"` **function:** use a function to translate each element in a data array into a min value _examples:_ `min={() => 10}` **path string or path array:** specify which property in an array of nested data objects should be used as a min value _examples:_ `min="bonds.min"`, `min={["bonds", "min"]}` --- ### minComponent The `minComponent` prop takes a component instance which will be responsible for rendering an element to represent the minimum value of the box plot. The new element created from the passed `minComponent` will be provided with the following props calculated by `VictoryBoxPlot`: `datum`, `index`, `scale`, `style`, `events`, `majorWhisker` and `minorWhisker`. The `majorWhisker` and `minorWhisker` props are given as objects with values for `x1`, `y1`, `x2` and `y2` that describes the lines that make up the major and minor whisker. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If a `minComponent` is not provided, `VictoryBoxPlot` will use its default [Whisker component][]. See the [Custom Components Guide][] for more detail on creating your own components ```jsx minComponent={} ``` --- ### minLabelComponent The `minLabelComponent` prop takes a component instance which will be used to render the label corresponding to the minimum value for each box. The new element created from the passed `minLabelComponent` will be supplied with the following props: `x`, `y`, `datum`, `index`, `scale`, `verticalAnchor`, `textAnchor`, `angle`, `transform`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `minLabelComponent` is omitted, a new [`VictoryLabel`][] will be created with props described above. See the [Custom Components Guide][] for more detail on creating your own components ```jsx minLabelComponent={} ``` ```jsx live } theme={VictoryTheme.clean} /> ``` --- ### minLabels The `minLabels` prop defines the labels that will appear above each point. This prop should be given as a boolean, an array or as a function of the props corresponding to that label. When given as a boolean value, the min value of each datum will be used for the label. #### Common Usage - `minLabels` - `minLabels={["first", "second", "third"]}` - `minLabels={({ datum }) => Math.round(datum.min)}` --- ### q1 Use the `q1` data accessor prop to define the q1 value of a box plot. **string:** specify which property in an array of data objects should be used as the q1 value _examples:_ `q1="q1_value"` **function:** use a function to translate each element in a data array into a q1 value _examples:_ `q1={() => 10}` **path string or path array:** specify which property in an array of nested data objects should be used as a q1 value _examples:_ `q1="bonds.q1"`, `q1={["bonds", "q1"]}` --- ### q1Component The `q1Component` prop takes a component instance which will be responsible for rendering an element to represent the q1 value of the box plot. The new element created from the passed `q1Component` will be provided with the following props calculated by `VictoryBoxPlot`: `datum`, `index`, `scale`, `style`, `events`, `x`, `y`, `width` and `height` Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If a `q1Component` is not provided, `VictoryBoxPlot` will use its default [Box component][]. See the [Custom Components Guide][] for more detail on creating your own components ```jsx q1Component={} ``` --- ### q1LabelComponent The `q1LabelComponent` prop takes a component instance which will be used to render the label corresponding to the q1 value for each box. The new element created from the passed `q1LabelComponent` will be supplied with the following props: `x`, `y`, `datum`, `index`, `scale`, `verticalAnchor`, `textAnchor`, `angle`, `transform`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `q1LabelComponent` is omitted, a new [`VictoryLabel`][] will be created with props described above. See the [Custom Components Guide][] for more detail on creating your own components ```jsx q1LabelComponent={} ``` ```jsx live } theme={VictoryTheme.clean} /> ``` --- ### q1Labels The `q1Labels` prop defines the labels that will appear above each point. This prop should be given as a boolean, an array or as a function of the props corresponding to that label. When given as a boolean value, the q1 value of each datum will be used for the label. #### Common Usage - `q1Labels` - `q1Labels={["first", "second", "third"]}` - `q1Labels={({ datum }) => Math.round(datum.q1)}` --- ### q3 Use the `q3` data accessor prop to define the q3 value of a box plot. **string:** specify which property in an array of data objects should be used as the q3 value _examples:_ `q3="q3_value"` **function:** use a function to translate each element in a data array into a q3 value _examples:_ `q3={() => 10}` **path string or path array:** specify which property in an array of nested data objects should be used as a q3 value _examples:_ `q3="bonds.q3"`, `q3={["bonds", "q3"]}` --- ### q3Component The `q3Component` prop takes a component instance which will be responsible for rendering an element to represent the q3 value of the box plot. The new element created from the passed `q3Component` will be provided with the following props calculated by `VictoryBoxPlot`: `datum`, `index`, `scale`, `style`, `events`, `x`, `y`, `width` and `height` Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If a `q3Component` is not provided, `VictoryBoxPlot` will use its default [Box component][]. See the [Custom Components Guide][] for more detail on creating your own components ```jsx q3Component={} ``` --- ### q3LabelComponent The `q3LabelComponent` prop takes a component instance which will be used to render the label corresponding to the q3 value for each box. The new element created from the passed `q3LabelComponent` will be supplied with the following props: `x`, `y`, `datum`, `index`, `scale`, `verticalAnchor`, `textAnchor`, `angle`, `transform`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `q3LabelComponent` is omitted, a new [`VictoryLabel`][] will be created with props described above. See the [Custom Components Guide][] for more detail on creating your own components ```jsx q3LabelComponent={} ``` ```jsx live } theme={VictoryTheme.clean} /> ``` --- ### q3Labels The `q3Labels` prop defines the labels that will appear above each point. This prop should be given as a boolean, an array or as a function of the props corresponding to that label. When given as a boolean value, the q3 value of each datum will be used for the label. #### Common Usage - `q3Labels` - `q3Labels={["first", "second", "third"]}` - `q3Labels={({ datum }) => Math.round(datum.q3)}` --- ### style Defines the style of the component using `VictoryBoxPlotStyleInterface`. ```ts type VictoryBoxPlotStyleInterface = { parent: object, max: object, maxLabels: object, min: object, minLabels: object, median: object, medianLabels: object, q1: object, q1Labels: object, q3: object, q3Labels: object } ``` The `style` prop defines the style of the component. The style prop should be given as an object with styles defined for `parent`, `max`, `maxLabels`, `min`, `minLabels`,`median`, `medianLabels`,`q1`, `q1Labels`,`q3`, `q3Labels`. Any valid svg styles are supported, but `width`, `height`, and `padding` should be specified via props as they determine relative layout for components in VictoryChart. Functional styles may be defined for style properties, and they will be evaluated with the props corresponding to each element. _note:_ When a component is rendered as a child of another Victory component, or within a custom `` element with `standalone={false}` parent styles will be applied to the enclosing `` tag. Many styles that can be applied to a parent `` will not be expressed when applied to a ``. _note:_ custom `angle` and `verticalAnchor` properties may be included in `labels` styles. ```jsx live ``` --- ### whiskerWidth The `whiskerWidth` prop specifies how wide each whisker crosshair should be. If the `whiskerWidth` prop is not set, the width of the whisker crosshair will match the width of the box. ```jsx live ``` ================================================ FILE: website/docs/api/victory-brush-container.mdx ================================================ --- title: VictoryBrushContainer --- Adds the ability to highlight a region of a chart, and interact with highlighted regions. :::info For examples of `VictoryBrushContainer` in action, visit the [Data Selection](/docs/guides/data-selection) guide. ::: ## Inherited Props ## Component Props --- ### allowDrag The optional `allowDrag` prop accepts a boolean that enables dragging behavior for the highlighted brush area. Resizing will still be enabled when the `allowDrag` prop is set to false. ```jsx live } theme={VictoryTheme.clean} >
``` --- ### allowDraw The optional `allowDraw` prop accepts a boolean that enables drawing new brush areas. When this prop is set to false, existing brush areas may still be resized or dragged, but clicking outside of existing brush areas will not trigger the creation of new brush areas. ```jsx live } theme={VictoryTheme.clean} > ``` --- ### allowResize The optional `allowResize` prop accepts a boolean that enables resizing the highlighted brush area. Dragging will still be enabled when the `allowResize` prop is set to false, but the dimensions of the brush area will be fixed. By default, when `allowResize` is set to false, clicking outside of the brush area will center the brush on the user's mouse position without resizing it. This behavior can be controlled via the [defaultBrushArea prop](/docs/api/victory-brush-container/#defaultbrusharea). ```jsx live } theme={VictoryTheme.clean} > ``` --- ### brushComponent The `brushComponent` prop specifies the component to be rendered for the highlighted area. This component will be supplied with the following props: x, y, width, height, and style. When this prop is not specified, a `` will be rendered. --- ### brushDimension When the `brushDimension` prop is set, brushing will only be specific to the given dimension (either "x" or "y"), and the entire domain of the other dimension will be highlighted. When this prop is not specified, highlighting will occur along both dimensions. --- ### brushDomain The optional `brushDomain` prop describes the highlighted state. This prop is an object that specifies separate arrays for `x` and `y`. Each array is a tuple that describes the minimum and maximum values to render. If this prop is not provided initially, the chart will render with the entire domain highlighted. When this prop changes, the chart will render with a new highlighted domain. _example:_ `brushDomain={{x: [50, 100], y: [0, 100]}` --- ### brushStyle The `brushStyle` adds custom styles to the `brushComponent`. --- ### defaultBrushArea The `defaultBrushArea` prop specifies how the container will behave when a region outside the active brush is clicked without selecting a new area. When the prop is set to "all", the entire domain will be selected. When the prop is set to "none", no new region will be selected, and any existing active brush will be cleared. When the prop is set to "disable" the new selected region will default to the current active brush. When this prop is set to "move", the active brush will be moved so that it is centered over the location of the click event. ```jsx live } > ``` --- ### disable When the `disable` prop is set to `true`, `VictoryBrushContainer` events will not fire. --- ### handleComponent The `handleComponent` prop specifies the component to be rendered for each handle for the highlighted area. This component will be supplied with the following props: `x`, `y`, `width`, `height`, `cursor`, and `style`. When this prop is not specified, a `` will be rendered. --- ### handleStyle The `handleStyle` adds custom styles to the `handleComponents`. This prop should be given as an object of SVG style attributes. Handles refer to the region on each highlighted area where the area may be expanded. Only handles relevant to the given `dimension` will be rendered. For example, when `brushDimension="x"` only "left" and "right" handles will be rendered. Handles are automatically styled with cursors appropriate to their orientation. --- ### handleWidth The `handleWidth` prop specifies the size of the rendered handle component in pixels. This prop will determine the width of "left" and "right" handles and the height of "top" and "bottom" handles. --- ### onBrushCleared The optional `onBrushCleared` prop accepts a function to be called when the active brush area is cleared. The function accepts the parameters of `domain` (the updated domain), and `props` (the props used by `VictoryBrushContainer`). _example:_ `onBrushCleared={(domain, props) => handleBrushCleared(domain, props)}` --- ### onBrushDomainChange The optional `onBrushDomainChange` prop accepts a function to be called on each update to the highlighted domain. The function accepts the parameters of `domain` (the updated domain), and `props` (the props used by `VictoryBrushContainer`). _example:_ `onBrushDomainChange={(domain, props) => handleDomainChange(domain, props)}` --- ### onBrushDomainChangeEnd The optional `onBrushDomainChangeEnd` prop accepts a function to be called only on mouse up events. The function accepts the parameters of `domain` (the updated domain), and `props` (the props used by `VictoryBrushContainer`). _example:_ `onBrushDomainChangeEnd={(domain, props) => handleDomainChangeEnd(domain, props)}` ================================================ FILE: website/docs/api/victory-brush-line.mdx ================================================ --- title: VictoryBrushLine --- `VictoryBrushLine` renders a brush component centered around a line. It may be used in place of the default `axisComponent` or `gridComponent` within `VictoryAxis`. Use `VictoryBrushLine` instead of [`VictoryBrushContainer`](/docs/api/victory-brush-container) in charts that require multiple brushes. ```jsx live } /> ``` ## Component Props --- ### allowDrag The optional `allowDrag` prop accepts a boolean that enables dragging behavior for the highlighted brush area. Resizing will still be enabled when the `allowDrag` prop is set to false. --- ### allowResize The optional `allowResize` prop accepts a boolean that enables resizing the highlighted brush area. Dragging will still be enabled when the `allowResize` prop is set to false, but the dimensions of the brush area will be fixed. --- ### brushAreaComponent The `brushAreaComponent` prop specifies the component to be rendered for the interactive brush region. This component will be supplied with the following props: x, y, width, height, and style. When this prop is not specified, a [`Box`](/docs/api/victory-primitives) component will be rendered. --- ### brushAreaStyle The `brushAreaStyle` prop adds custom styles to the `brushAreaComponent`. This prop should be given as an object of SVG style attributes. Styles supplied to `brushAreaStyle` are assigned to the following default styles: ```js { stroke: "none", fill: "black", opacity: ({ active }) => active ? 0.2 : 0.1 } ``` :::note `cursor` styles should not be applied via this prop, as they are dynamically assigned ::: --- ### brushAreaWidth The `brushAreaWidth` prop is used to specify the width of the interactive brush region. If this prop is not supplied, the `width` prop will be used. --- ### brushComponent The `brushComponent` prop specifies the component to be rendered for the active brush. This component will be supplied with the following props: x, y, width, height, and style. When this prop is not specified, a [`Box`](/docs/api/victory-primitives) component will be rendered. --- ### brushDomain The optional `brushDomain` prop describes the highlighted state. This prop should be given as an array of the minimum and maximum values of the highlighted region. _example:_ `brushDomain={[50, 100]}` --- ### brushStyle The `brushStyle` prop adds custom styles to the `brushComponent`. This prop should be given as an object of SVG style attributes. Styles supplied to `brushStyle` are assigned to the following default styles: ```js { pointerEvents: "none", stroke: "none", fill: "black", opacity: ({ active }) => active ? 0.4 : 0.3 } ``` --- ### brushWidth The `brushWidth` prop is used to specify the width of the active brush. If this prop is not supplied, the `width` prop will be used. --- ### className This prop specifies the class name that will be applied to the rendered element --- ### dimension The `dimension` prop specified whether the brush will be vertical ("y"), or horizontal ("x") --- ### disable When the `disable` prop is set to `true`, `VictoryBrushLine` events will not fire. --- ### events The `events` prop specifies a set of events that will be attached to the brush component group. This prop should not be set manually. --- ### groupComponent This prop specifies the element used to group rendered elements --- ### handleComponent The `handleComponent` prop specifies the component to be rendered for each handle. This component will be supplied with the following props: x, y, width, height, and style. When this prop is not specified, a [`Box`](/docs/api/victory-primitives) component will be rendered. --- ### handleStyle The `handleStyle` props adds custom styles to the `handleComponent`. This prop should be given as an object of SVG style attributes. Styles supplied to `handleStyle` are assigned to the following default styles: ```js { pointerEvents: "none", stroke: "none", fill: "none" } ``` --- ### handleWidth The `handleWidth` prop is used to specify the width of each handle component. --- ### lineComponent The `lineComponent` prop specifies the component to render for the underlying axis or grid line. This component will be supplied with the following props: x1, y1, x2, y2 and style. When this prop is not specified, an [`Axis`](/docs/api/victory-primitives) component will be rendered. --- ### onBrushDomainChange The `onBrushDomainChange` prop specifies a callback function which will be called whenever the brush domain changes. The callback provided will be called with the following arguments: - `currentDomain`: The current brush domain - `props`: the current set of props for `VictoryBrushLine` --- ### scale This prop specifies `scale` of the parent chart with `domain` and `range` applied. This prop should not be set manually. --- ### style The `style` prop specifies the styles that will be applied to the `lineComponent`. This prop should be given as an object of SVG style attributes. --- ### type The `type` is used to specify which event target a particular `VictoryBrushLine` belongs to. When `VictoryBrushLine` is used by `VictoryAxis` as its `axisComponent` or `gridComponent`, this prop will be set automatically to "axis" or "grid" as appropriate. --- ### width The `width` prop specified the width of both the `brush` and `brushArea`. When `brushWidth` or `brushAreaWidth` are specified, this prop will not be used ================================================ FILE: website/docs/api/victory-candlestick.mdx ================================================ --- title: VictoryCandlestick --- :::info For examples of `VictoryCandlestick` in action, visit the [Candlestick](/docs/charts/candlestick) examples. ::: ## Inherited Props ## Component Props --- ### candleColors Candle colors are significant in candlestick charts, with colors indicating whether a market closed higher than it opened (positive), or closed lower than it opened (negative). The `candleColors` prop should be given as an object with color strings specified for positive and negative. ```jsx live ``` --- ### candleRatio The `candleRatio` prop specifies an _approximate_ ratio between candle widths and spaces between candles. When width is not specified via the `candleWidth` prop or in candle styles, the `candleRatio` prop will be used to calculate a default width for each candle given the total number of candles in the data series and the overall width of the chart. ```jsx live ``` --- ### candleWidth The `candleWidth` prop is used to specify the width of each candle. This prop may be given as a number of pixels or as a function that returns a number. When this prop is given as a function, it will be evaluated with a single argument: an object containing all the props passed to the `Candle` component. When this value is not given, a default value will be calculated based on the overall dimensions of the chart, and the number of candles. :::note It is still possible to define candle width via the style prop with the `width` attribute, but `candleWidth` will take precedence. ::: ```jsx live ``` --- ### close Use `close` data accessor prop to define the close value of a candle. **string:** specify which property in an array of data objects should be used as the close value _examples:_ `close="closing_value"` **function:** use a function to translate each element in a data array into a close value _examples:_ `close={() => 10}` **array index:** specify which index of an array should be used as a close value when data is given as an array of arrays _examples:_ `close={1}` **path string or path array:** specify which property in an array of nested data objects should be used as a close value _examples:_ `close="bonds.close"`, `close={["bonds", "close"]}` --- ### closeLabelComponent The `closeLabelComponent` prop takes a component instance which will be used to render the label corresponding to the close value for each candle. The new element created from the passed `closeLabelComponent` will be supplied with the following props: `x`, `y`, `datum`, `index`, `scale`, `verticalAnchor`, `textAnchor`, `angle`, `transform`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `closeLabelComponent` is omitted, a new [`VictoryLabel`](/docs/api/victory-label) will be created with props described above. ```jsx live } /> ``` --- ### closeLabels The `closeLabels` prop defines the labels that will correspond to the close value for each candle. This prop should be given as a boolean, an array or as a function of the props corresponding to that label. When given as a boolean value, the max value of each datum will be used for the label. _examples:_ - `closeLabels` - `closeLabels={["first", "second", "third"]}` - `closeLabels={({ datum }) => Math.round(datum.close)}` --- ### data :::caution This property is not currently typed, but requires a specific data format. We are working on adding more specific types to this prop. ::: Specify data via the data prop. By default, `VictoryCandlestick` expects data as an array of objects with `x`, `open`, `close`, `high`, and `low` keys. Use the `x`, `open`, `close`, `high`, and `low` data accessor props to specify custom data formats. ```jsx live ``` --- ### eventKey `VictoryCandlestick` uses the standard `eventKey` prop to specify how event targets are addressed. **This prop is not commonly used.** [Read about the `eventKey` prop in more detail here](/docs/guides/events) --- ### events `VictoryCandlestick` uses the standard `events` prop. [Read about it here](/docs/guides/events) See the [Events Guide][] for more information on defining events. ```jsx live

Click Me

{ return [ { target: "data", mutation: (props) => { const fill = props.style && props.style.fill; return fill === "#c43a31" ? null : { style: { fill: "#c43a31", }, }; }, }, ]; }, }, }, ]} data={sampleDataDates} />
``` --- ### high Use `high` data accessor prop to define the high value of a candle. **string:** specify which property in an array of data objects should be used as the high value _examples:_ `high="highest_value"` **function:** use a function to translate each element in a data array into a high value _examples:_ `high={() => 10}` **array index:** specify which index of an array should be used as a high value when data is given as an array of arrays _examples:_ `high={1}` **path string or path array:** specify which property in an array of nested data objects should be used as a high value _examples:_ `high="bonds.high"`, `high={["bonds", "high"]}` --- ### highLabelComponent The `highLabelComponent` prop takes a component instance which will be used to render the label corresponding to the highest value for each candle. The new element created from the passed `highLabelComponent` will be supplied with the following props: `x`, `y`, `datum`, `index`, `scale`, `verticalAnchor`, `textAnchor`, `angle`, `transform`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `highLabelComponent` is omitted, a new [`VictoryLabel`](/docs/api/victory-label) will be created with props described above. See the [Custom Components Guide][] for more detail on creating your own components ```jsx highLabelComponent={} ``` ```jsx live } /> ``` --- ### highLabels The `highLabels` prop defines the labels that will correspond to the high value for each candle. This prop should be given as a boolean, an array or as a function of the props corresponding to that label. When given as a boolean value, the max value of each datum will be used for the label. _examples:_ - `highLabels` - `highLabels={["first", "second", "third"]}` - `highLabels={({ datum }) => Math.round(datum.high)}` --- ### labelOrientation The `labelOrientation` prop determines where a label should be placed in relation to the candle it corresponds to. This prop may be given as "top", "bottom", "left", "right", or as an object with an option defined for some or all of the labels. ```jsx live "close"} highLabels={() => "high"} lowLabels={() => "low"} openLabels={() => "open"} style={{ labels: { padding: 4 } }} labelOrientation={{ close: "right", open: "right", high: "top", low: "bottom", }} /> ``` --- ### labels The `labels` prop defines the label associated with the candle. This prop is typically given as a function. ```jsx live `open: ${datum.open}` } theme={VictoryTheme.clean} /> ``` --- ### low Use `low` data accessor prop to define the low value of a candle. **string:** specify which property in an array of data objects should be used as the low value _examples:_ `low="lowest_value"` **function:** use a function to translate each element in a data array into a low value _examples:_ `low={() => 10}` **array index:** specify which index of an array should be used as a low value when data is given as an array of arrays _examples:_ `low={1}` **path string or path array:** specify which property in an array of nested data objects should be used as a low value _examples:_ `low="bonds.low"`, `low={["bonds", "low"]}` --- ### lowLabelComponent The `lowLabelComponent` prop takes a component instance which will be used to render the label corresponding to the lowest value for each candle. The new element created from the passed `lowLabelComponent` will be supplied with the following props: `x`, `y`, `datum`, `index`, `scale`, `verticalAnchor`, `textAnchor`, `angle`, `transform`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `lowLabelComponent` is omitted, a new [`VictoryLabel`](/docs/api/victory-label) will be created with props described above. ```jsx live } events={[ { target: "data", eventHandlers: { onMouseOver: () => ({ target: "lowLabels", mutation: () => ({ active: true, }), }), onMouseOut: () => ({ target: "lowLabels", mutation: () => ({ active: false, }), }), }, }, ]} /> ``` --- ### lowLabels The `lowLabels` prop defines the labels that will correspond to the low value for each candle. This prop should be given as a boolean, an array or as a function of the props corresponding to that label. When given as a boolean value, the max value of each datum will be used for the label. _examples:_ - `lowLabels` - `lowLabels={["first", "second", "third"]}` - `lowLabels={({ datum }) => Math.round(datum.low)}` --- ### open Use `open` data accessor prop to define the open value of a candle. **string:** specify which property in an array of data objects should be used as the open value _examples:_ `open="opening_value"` **function:** use a function to translate each element in a data array into an open value _examples:_ `open={() => 10}` **array index:** specify which index of an array should be used as an open value when data is given as an array of arrays _examples:_ `open={1}` **path string or path array:** specify which property in an array of nested data objects should be used as an open value _examples:_ `open="bonds.open"`, `open={["bonds", "open"]}` --- ### openLabelComponent The `openLabelComponent` prop takes a component instance which will be used to render the label corresponding to the open value for each candle. The new element created from the passed `openLabelComponent` will be supplied with the following props: `x`, `y`, `datum`, `index`, `scale`, `verticalAnchor`, `textAnchor`, `angle`, `transform`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `openLabelComponent` is omitted, a new [`VictoryLabel`](/docs/api/victory-label) will be created with props described above. ```jsx live } /> ``` --- ### openLabels The `openLabels` prop defines the labels that will correspond to the open value for each candle. This prop should be given as a boolean, an array or as a function of the props corresponding to that label. When given as a boolean value, the max value of each datum will be used for the label. _examples:_ - `openLabels` - `openLabels={["first", "second", "third"]}` - `openLabels={({ datum }) => Math.round(datum.open)}` --- ### style The `style` prop defines the style of the component. The style prop should be given as an object with styles defined for `parent`, `data`, `labels`, `closeLabels`, `highLabels`,`lowLabels`, and `openLabels`. Any valid svg styles are supported, but `width`, `height`, and `padding` should be specified via props as they determine relative layout for components in VictoryChart. Functional styles may be defined for style properties, and they will be evaluated with the props corresponding to each element. :::note When a component is rendered as a child of another Victory component, or within a custom `` element with `standalone={false}` parent styles will be applied to the enclosing `` tag. Many styles that can be applied to a parent `` will not be expressed when applied to a ``. ::: ```jsx live "labels"} closeLabels={() => "close"} highLabels={() => "high"} lowLabels={() => "low"} openLabels={() => "open"} style={{ data: { fill: "#c43a31", fillOpacity: 0.7, stroke: "#c43a31", strokeWidth: 3, }, labels: { fill: "tomato", padding: 2, }, closeLabels: { fill: "orange", padding: 2, }, highLabels: { fill: "blue", padding: 2, }, lowLabels: { fill: "teal", padding: 2, }, openLabels: { fill: "green", padding: 2, }, }} /> ``` --- ### wickStrokeWidth When the `wickStrokeWidth` prop is set, this value will be used to determine the stroke width for the candle wick. When this prop is not set, the `strokeWidth` set by the `style` prop will apply to both the candle and the wick. ================================================ FILE: website/docs/api/victory-canvas.mdx ================================================ --- title: VictoryCanvas --- The `victory-canvas` package currently provides a set of *experimental* primitive components that will allow Victory to render data in a Canvas container rather than as an SVG. :::danger This API should not be used in production and only serves as an experiment. ::: ## Container Components ### CanvasGroup This component is designed to be used as the `groupComponent` for any chart type to enable Canvas rendering. This component will create a `` HTML component, and allow other child components to access the Canvas context via React context. ```jsx const MyLine = (props) => ( } {...props} /> ); ``` ## Primitive Components Each of these primitive components depends on the Canvas context that it gets through the `useCanvasContext` React hook. Rather than returning a React component, they render elements to the screen through the Canvas context. ### CanvasPoint Designed to be used with `VictoryScatter`, this component mimics the behavior of `Point`. ```jsx const MyScatter = (props) => ( } dataComponent={} {...props} /> ); ``` **Props** - `active` _boolean_ a flag signifying whether the component is active - `data` _array_ the entire dataset - `datum` _object_ the data point corresponding to this point - `events` _object_ events to attach to the rendered element - `getPath` _function_ a function of `x`, `y`, and `size` that returns a path string. When this optional function is provided, it will be used to calculate a path, rather than one of the path functions corresponding to the `symbol`s supported by `Point`. - `index` _number_ the index of this point within the dataset - `origin` _object_ the svg coordinates of the center point of a polar chart - `polar` _boolean_ a flag specifying whether the component is part of a polar chart - `scale` _object_ the x and y scale of the parent chart with `domain` and `range` applied - `size` _number or function_ the size of the point. When this prop is given as a function, it will be called with the rest of the props supplied to `Point`. - `style` _object_ the styles to apply to the rendered element - `symbol` _"circle", "cross", "diamond", "plus", "minus", "square", "star", "triangleDown", "triangleUp"_ which symbol the point should render. This prop may also be given as a function that returns one of the above options. When this prop is given as a function, it will be called with the rest of the props supplied to `Point`. - `transform` _string_ a transform that will be supplied to elements this component renders - `x` _number_ the x coordinate of the center of the point - `y` _number_ the y coordinate of the center of the point ### CanvasCurve Designed to be used with [VictoryLine][], this component mimics the behavior of `Curve`. ```jsx const MyLine = (props) => ( } dataComponent={} {...props} /> ); ``` **Props** - `active` _boolean_ a flag signifying whether the component is active - `data` _array_ the entire dataset used to define the curve - `events` _object_ events to attach to the rendered element - `interpolation` _string or function_ the interpolation to use when calculating a path - `origin` _object_ the svg coordinates of the center point of a polar chart - `polar` _boolean_ a flag specifying whether the component is part of a polar chart - `role` _string_ the aria role to assign to the element - `scale` _object_ the x and y scale of the parent chart with `domain` and `range` applied - `style` _object_ the styles to apply to the rendered element - `transform` _string_ a transform that will be supplied to elements this component renders ### CanvasBar Designed to be used with `VictoryBar`, this component mimics the behavior of `Bar`. ```jsx const Bar = (props) => ( } dataComponent={} {...props} /> ); ``` **Props** - `active` _boolean_ a flag signifying whether the component is active - `alignment` \*"start", "middle", or "end" specifies how a bar path should be aligned in relation to its data point - `barRatio` _number_ a number between zero and one that will be used to calculate bar width when an explicit width is not given - `barWidth` _number or function_ A prop defining the width of the bar. When this prop is given as a function, it will be called with the rest of the props supplied to `Bar`. - `cornerRadius` _number, function or object_ the number of pixels of corner radius to apply when calculating a bar path. When this prop is given as a function, it will be called with the rest of the props supplied to `Bar`. When given as an object, this prop may include values for top, bottom, topLeft, topRight, bottomLeft and bottomRight, with more specific values overriding less specific values - `data` _array_ the entire dataset - `datum` _object_ the data point corresponding to this bar - `events` _object_ events to attach to the rendered element - `index` _number_ the index of this bar within the dataset - `origin` _object_ the svg coordinates of the center point of a polar chart - `polar` _boolean_ a flag specifying whether the component is part of a polar chart - `scale` _object_ the x and y scale of the parent chart with `domain` and `range` applied - `style` _object_ the styles to apply to the rendered element - `transform` _string_ a transform that will be supplied to elements this component renders - `width` _number_ the width of parent chart (used to calculate default bar width `style.width` is not supplied) - `x` _number_ the x coordinate of the top of the bar - `y0` _number_ the y coordinate of the baseline of the bar - `y` _number_ the y coordinate of the top of the bar ================================================ FILE: website/docs/api/victory-chart.mdx ================================================ --- title: VictoryChart --- ## Inherited Props --- ### backgroundComponent The `backgroundComponent` prop takes a component instance which will be responsible for rendering a background if the `VictoryChart`'s `style` component includes `background` styles. The new element created from the passed `backgroundComponent` will be provided with the following properties calculated by `VictoryChart`: `height`, `polar`, `scale`, `style`, `x`, `y`, `width`. All of these props on `Background` should take precedence over what `VictoryChart` is trying to set. ```jsx live } theme={VictoryTheme.clean} /> ``` --- ### children `VictoryChart` works with any combination of the following children: [VictoryArea][], [VictoryAxis][] / [VictoryPolarAxis][], [VictoryBar][], [VictoryCandlestick][], [VictoryErrorBar][], [VictoryGroup][], [VictoryLine][], [VictoryScatter][], [VictoryHistogram][], [VictoryStack][], and [VictoryVoronoi][]. Children supplied to `VictoryChart` will be cloned and rendered with new props so that all children share common props such as `domain` and `scale`. :::note polar charts must use `VictoryPolarAxis` rather than `VictoryAxis` ::: --- ### containerComponent `VictoryChart` uses the standard `containerComponent` prop. [Read about it in detail here](/docs/api/victory-common-theme-props/#containercomponent) ```jsx containerComponent={} ``` --- ### desc The `desc` prop specifies the description of the chart/SVG to assist with accessibility for screen readers. The more descriptive this title is, the more useful it will be for people using screen readers. --- ### defaultAxes Set the default axis for this chart when no axis is provided. Allows you to customize the axis component for the chart. :::note This property is not typically used ::: ```ts // default defaultAxes = { independent: , dependent: } ``` ### defaultPolarAxes Set the default axis for this chart when no axis is provided. Allows you to customize the axis component for the chart. :::note This property is not typically used ::: ```ts // default defaultPolarAxes ={ independent: , dependent: } ``` --- ### domain The `domain` prop describes the range of data the component will include. This prop can be given as an array of the minimum and maximum expected values of the data or as an object that specifies separate arrays for x and y. If this prop is not provided, a domain will be calculated from data, or other available information. :::note `VictoryChart` controls the `domain` prop of its children. ::: --- ### endAngle The `endAngle` props defines the overall end angle of a polar chart in degrees. This prop is used in conjunction with `startAngle` to create polar chart that spans only a segment of a circle, or to change overall rotation of the chart. This prop should be given as a number of degrees. Degrees are defined as starting at the 3 o'clock position, and proceeding counterclockwise. ```jsx live
``` --- ### events `VictoryChart` uses the standard `events` prop. [Read about it in more detail here](/docs/guides/events) See the [Events Guide][] for more information on defining events. :::note `VictoryChart` coordinates events between children using the `VictorySharedEvents` and the `sharedEvents` prop ::: ```jsx live { return [ { childName: "area-2", target: "data", mutation: (props) => ({ style: Object.assign( {}, props.style, { fill: "gold" }, ), }), }, { childName: "area-3", target: "data", mutation: (props) => ({ style: Object.assign( {}, props.style, { fill: "orange" }, ), }), }, { childName: "area-4", target: "data", mutation: (props) => ({ style: Object.assign( {}, props.style, { fill: "red" }, ), }), }, ]; }, }, }, ]} theme={VictoryTheme.clean} > ``` --- ### innerRadius When the `innerRadius` prop is set, polar charts will be hollow rather than circular. ```jsx live ``` --- ### prependDefaultAxes By default, `VictoryChart` will prepend default axes to the beginning of the children array. This behavior can be disabled by setting `prependDefaultAxes` to `false`. --- ### startAngle The `startAngle` props defines the overall start angle of a polar chart in degrees. This prop is used in conjunction with `endAngle` to create polar chart that spans only a segment of a circle, or to change overall rotation of the chart. This prop should be given as a number of degrees. Degrees are defined as starting at the 3 o'clock position, and proceeding counterclockwise. ```jsx live
``` --- ### style Defines the style of the component using [VictoryStyleInterface](/docs/api/victory-style-interface). note: custom valid svg style properties that are supported may be included in `background` styles. _default (provided by default theme):_ See [grayscale theme][] for more detail ```jsx live ``` --- ### title The `title` prop specifies the title to be applied to the SVG to assist with accessibility for screen readers. The more descriptive this title is, the more useful it will be for people using screen readers ================================================ FILE: website/docs/api/victory-clip-container.mdx ================================================ --- title: VictoryClipContainer --- Enables curtain-style transitions for continuous data types like `VictoryLine` and `VictoryArea`. `VictoryClipContainer` will render its children either in a regular `` element, or in a `` element clipped by a rectangular clip path when a `clipWidth` is supplied. :::info For examples of `VictoryClipContainer` in action, visit the [containers](/docs/guides/containers) guide. ::: ## Component Props --- ### children `VictoryClipContainer` renders a single child, or an array of children in group element. --- ### circleComponent The `circleComponent` prop specifies the element to use when a `VictoryClipContainer` renders a circular clip path. By default, `VictoryClipContainer` uses the [Circle](/docs/api/victory-primitives#circle) component. --- ### className The `className` prop specifies a class name that will be applied to the rendered element. --- ### clipHeight The `clipHeight` prop determines the base height of the rectangular clip path. This prop should be given as a number. If this prop is not given, it will be calculated based on the height and padding of the parent chart. --- ### clipId The `clipId` prop may be used to set a deterministic id for the container. When a `containerId` is not manually set, a unique id will be generated. It is usually necessary to set deterministic ids for automated testing. --- ### clipPadding The `clipPadding` prop is used when the clipped area should be larger than the range of a chart. This prop should be given as an object with `top`, `bottom`, `left`, and `right` properties. Set the `clipPadding` prop is useful for extending the visible area of a chart in some dimension so that data or labels are not cut off. --- ### clipPathComponent The `clipPathComponent` prop specifies the clip path to apply to the rendered group when appropriate. By default, `VictoryClipContainer` uses the [ClipPath](/docs/api/victory-primitives#clippath) component. --- ### clipWidth The `clipWidth` prop determines the base width of the rectangular clip path. This prop should be given as a number. If this prop is not given, it will be calculated based on the width and padding of the parent chart. --- ### events The `events` prop attaches arbitrary event handlers to the group element. This prop should be given as an object of event names and corresponding event handlers. When events are provided via Victory's event system, event handlers will be called with the event, the props of the component it is attached to, and an `eventKey` when applicable. _examples:_ `events={{onClick: (evt) => alert("x: " + evt.clientX)}}` --- ### groupComponent `VictoryClipContainer` uses the standard `groupComponent` prop. [Read about it here](/docs/api/victory-common-theme-props#groupcomponent) --- ### origin Victory components will pass an `origin` prop to define the center point in svg coordinates for polar charts. **This prop should not be set manually.** --- ### polar Victory components can pass a boolean `polar` prop to specify whether a label is part of a polar chart. **This prop should not be set manually.** --- ### radius The `radius` prop determines the radius of the circular clip path used for polar charts. This prop should be given as a number. If this prop is not given, it will be calculated based on the dimensions and padding of the parent chart. --- ### rectComponent The `rectComponent` prop specifies the element to use when a `VictoryClipContainer` renders a rectangular clip path. By default, `VictoryClipContainer` uses the [Rect](/docs/api/victory-primitives) component. --- ### translateX The `translateX` prop determines the offset of the clip path from the base x coordinate. This prop should be given as a number. If this prop is not given, it will be calculated based on the padding of the parent chart. --- ### translateY The `translateY` prop determines the offset of the clip path from the base y coordinate. This prop should be given as a number. If this prop is not given, it will be calculated based on the padding of the parent chart. ================================================ FILE: website/docs/api/victory-common-props.mdx ================================================ --- title: VictoryCommonProps --- Components that allow theming will implement these props. ## Inherited Props ## Props --- ### theme The `theme` prop specifies a theme to use for determining styles and layout properties for a component. Any styles or props defined in `theme` may be overwritten by props specified on the component instance. By default, components use a [grayscale theme][]. See the [Themes Guide][] for information about creating custom themes. ================================================ FILE: website/docs/api/victory-common-theme-props.mdx ================================================ --- title: VictoryCommonThemeProps --- Common props for all Victory components that use themes. Some components override these props with specific implementations. See the specific component's API documentation for more information. ## Props --- ### animate The `animate` prop specifies props for [VictoryAnimation][] and [VictoryTransition][] to use. The animate prop may be used to specify the duration, delay, and easing of an animation as well as the behavior of `onEnter` and `onExit` and `onLoad` transitions. Each Victory component defines its own default transitions, but these may be modified, or overwritten with the `animate` prop. An `animationWhitelist` may also be specified on the `animate` prop. When given, only props specified in the whitelist will animate. See the [Animations Guide][] for more detail on animations and transitions ```jsx live noInline function App(props) { const [state, setState] = React.useState({ data: getData(), size: getSize(), }); React.useEffect(() => { const setStateInterval = window.setInterval(() => { setState({ data: getData(), size: getSize(), }); }, 3000); return () => { window.clearInterval( setStateInterval, ); }; }, []); return ( datum.opacity || 1, }, }} animate={{ animationWhitelist: [ "style", "data", "size", ], // Try removing "size" onExit: { duration: 500, before: () => ({ opacity: 0.3, _y: 0, }), }, onEnter: { duration: 500, before: () => ({ opacity: 0.3, _y: 0, }), after: (datum) => ({ opacity: 1, _y: datum._y, }), }, }} /> ); } function getData() { const num = Math.floor( 10 * Math.random() + 5, ); const points = new Array(num).fill(1); return points.map((point, index) => { return { x: index + 1, y: Math.random(), }; }); } function getSize() { return Math.random() * 10; } render(); ``` --- ### containerComponent The `containerComponent` prop takes a component instance which will be used to create a container element for standalone charts. If a `containerComponent` is not provided, the default `VictoryContainer` component will be used. Other Victory container components include: - [VictoryBrushContainer][] - [VictoryCursorContainer][] - [VictorySelectionContainer][] - [VictoryVoronoiContainer][] - [VictoryZoomContainer][] - hybrid containers may be created using the [createContainer][] helper Victory container components all support `title` and `desc` props, which are intended to add accessibility to Victory components. The more descriptive these props are, the more accessible your data will be for people using screen readers. These props may be set by passing them directly to the supplied component. By default, all Victory container components render responsive `svg` elements using the `viewBox` attribute. To render a static container, set `responsive={false}` directly on the container instance supplied via the `containerComponent` prop. All Victory container components also render a `Portal` element that may be used in conjunction with [VictoryPortal][] to force components to render above other children. Container components are supplied with the following props: - `domain` - `height` - `horizontal` - `origin` (for polar charts) - `padding` - `polar` - `scale` - `standalone` - `style` - `theme` - `width` ```jsx live `${datum.x.toPrecision( 2, )}, ${datum.y.toPrecision(2)}` } /> } /> ``` --- ### disableInlineStyles Allows Victory components to work better with CSS classes or styled-components. By default, Victory provides inline styles to chart components, which will override any conflicting CSS styles. This flag will remove the inline styles, making it easier to provide custom styling for components via CSS. If this prop is passed to a chart type (e.g. `VictoryBar`), it will apply to all data and label components for that chart. --- ### domainPadding The `domainPadding` prop specifies a number of pixels of padding to add to the beginning or end of a domain. This prop is useful for explicitly spacing data elements farther from the beginning or end of a domain to prevent axis crowding. When given as a single number, `domainPadding` will be applied to the upper and lower bound of both the x and y domains. This prop may also be given as an object with numbers or two-element arrays specified for x and y. When specifying arrays for `domainPadding`, the first element of the array will specify the padding to be applied to domain minimum, and the second element will specify padding the be applied to domain maximum. :::note The `x` value supplied to the `domainPadding` prop refers to the _independent_ variable, and the `y` value refers to the _dependent_ variable. This may cause confusion in horizontal charts, as the independent variable will corresponds to the y axis. ::: #### Common Usage - `domainPadding={20}` - `domainPadding={{ x: [20, 0] }}` :::note Values supplied for `domainPadding` will be coerced so that padding a domain will never result in charts including an additional quadrant. For example, if an original domain included only positive values, `domainPadding` will be coerced so that the resulted padded domain will not include negative values. ::: ```jsx live ``` --- ### externalEventMutations Occasionally is it necessary to trigger events in Victory's event system from some external element such as a button or a form field. Use the `externalEventMutation` prop to specify a set of mutations to apply to a given chart. ```ts type EventCallbackInterface = { target: T; eventKey: U; childName?: StringOrNumberOrList; mutation: (props: any) => any; callback?: () => void; }; type externalEventMutations = EventCallbackInterface< string | string[], StringOrNumberOrList >[]; ``` The `target`, `eventKey`, and `childName` (when applicable) must always be specified. The `mutation` function will be called with the current props of the element specified by the `target`, `eventKey` and `childName` provided. The mutation function should return a mutation object for that element. The `callback` prop should be used to clear the `externalEventMutations` prop once the mutation has been applied. Clearing `externalEventMutations` is crucial for charts that animate. ```jsx live noInline function App() { const [state, setState] = React.useState({ externalMutations: undefined, }); function removeMutation() { setState({ externalMutations: undefined, }); } function clearClicks() { setState({ externalMutations: [ { childName: "Bar-1", target: ["data"], eventKey: "all", mutation: () => ({ style: undefined, }), callback: removeMutation, }, ], }); } const buttonStyle = { backgroundColor: "black", color: "white", padding: "10px", marginTop: "10px", }; return (
({ target: "data", mutation: () => ({ style: { fill: "orange", }, }), }), }, }, ]} theme={VictoryTheme.clean} > "click me!"} data={[ { x: 1, y: 2 }, { x: 2, y: 4 }, { x: 3, y: 1 }, { x: 4, y: 5 }, ]} />
); } render(); ``` :::note External mutations are applied to the same state object that is used to control events in Victory, so depending on the order in which they are triggered, external event mutations may override mutations caused by internal Victory events or vice versa. ::: --- ### groupComponent The `groupComponent` prop takes a component instance which will be used to create group elements for use within container elements. For most components, this prop defaults to a `` tag. Continuous data components like `VictoryLine` and `VictoryArea` use [VictoryClipContainer][] a component which renders a `` tag with a `clipPath` `def`. This allows continuous data components to transition smoothly when new data points enter and exit. `VictoryClipContainer` may also be used with components like `VictoryScatter` to prevent data from overflowing the chart area. ```jsx live } /> ``` --- ### height The `height` prop determines the height of the containing ``. By default Victory components render responsive containers with the `viewBox` attribute set to `viewBox="0, 0, width, height"` and `width="100%"`, `height="auto"`. In responsive containers, the `width` and `height` props affect the _aspect ratio_ of the rendered component, while the absolute width and height are determined by the container. To render a static container, pass `responsive={false}` to the `containerComponent` like `containerComponent={}`, or set `standalone={false}` and render the resulting `` tag in your own `` container. When a component is nested within `VictoryChart`, `VictoryStack`, or `VictoryGroup` setting the `height` prop on the child component will have no effect. ```jsx live
} />
``` --- ### horizontal The horizontal prop determines whether data will be plotted horizontally. When this prop is set to true, the independent variable will be plotted on the y axis and the dependent variable will be plotted on the x axis. ```jsx live ``` --- ### maxDomain The `maxDomain` prop defines a maximum domain value for a chart. This prop is useful in situations where the maximum domain of a chart is static, while the minimum value depends on data or other variable information. If the `domain` prop is set in addition to `maximumDomain`, `domain` will be used. :::note The `x` value supplied to the `maxDomain` prop refers to the _independent_ variable, and the `y` value refers to the _dependent_ variable. This may cause confusion in horizontal charts, as the independent variable will corresponds to the y axis. ::: #### Common Usage - `maxDomain={0}` - `maxDomain={{ y: 0 }}` ```jsx live ``` --- ### minDomain The `minDomain` prop defines a minimum domain value for a chart. This prop is useful in situations where the minimum domain of a chart is static, while the maximum value depends on data or other variable information. If the `domain` prop is set in addition to `minimumDomain`, `domain` will be used. :::note The `x` value supplied to the `minDomain` prop refers to the _independent_ variable, and the `y` value refers to the _dependent_ variable. This may cause confusion in horizontal charts, as the independent variable will corresponds to the y axis. ::: #### Common Usage - `minDomain={0}` - `minDomain={{ y: 0 }}` ```jsx live ``` --- ### name The `name` prop is used to reference a component instance when defining shared events. --- ### origin The origin prop is used to define the center point in svg coordinates for polar charts. All children within a polar chart must share the same origin, so setting this prop on children nested within `VictoryChart`, `VictoryStack`, or `VictoryGroup` will have no effect. When this prop is not set, it will be calculated based on the `width`, `height` and `padding` of the chart. :::note This prop is typically not set by external consumers. ::: --- ### padding The `padding` prop specifies the amount of padding in number of pixels between the edge of the chart and any rendered child components. This prop can be given as a number or as an object with padding specified for top, bottom, left and right. As with [width][] and [height][], the absolute padding will depend on whether the component is rendered in a responsive container. When a component is nested within `VictoryChart`, `VictoryStack`, or `VictoryGroup` setting `padding` on the child component will have no effect. #### Common Usage - `padding={{top: 20, bottom: 60}}` - `padding={40}` ```jsx live ``` --- ### polar Specifies whether a chart should be plotted on a polar coordinate system. All components in a given chart must share the same coordinate system, so setting this prop on children nested within `VictoryChart`, `VictoryStack`, or `VictoryGroup` will have no effect. ```jsx live
d.x.toFixed(0)} width={400} height={400} domain={{ x: [0, 7], y: [0, 7] }} style={{ data: { fill: "#c43a31", stroke: "black", strokeWidth: 2, }, }} theme={VictoryTheme.clean} /> d.x.toFixed(0)} width={400} height={400} domain={{ x: [0, 7], y: [0, 7] }} style={{ data: { fill: "#c43a31", stroke: "black", strokeWidth: 2, }, }} theme={VictoryTheme.clean} />
``` --- ### range Describes the dimensions over which data may be plotted. For cartesian coordinate systems, this corresponds to minimum and maximum svg coordinates in the x and y dimension. In polar coordinate systems this corresponds to a range of angles and radii. When this value is not given it will be calculated from the `width`, `height`, and `padding`, or from the `startAngle` and `endAngle` in the case of polar charts. All components in a given chart must share the same range, so setting this prop on children nested within `VictoryChart`, `VictoryStack`, or `VictoryGroup` will have no effect. #### Common Usage - Cartesian: `range={{ x: [50, 250], y: [50, 250] }}` - Polar: `range={{ x: [0, 360], y: [0, 250] }}` :::note This prop is typically not set by external consumers. ::: --- ### scale The `scale` prop determines which scales your chart should use. In this case, "scale" refers to the d3 scale that is used inside Victory to determine the placement of data, ticks, and labels. A scale type can be either a string ("linear", "time", "log", "sqrt"), or a custom d3 scale function. This prop can be passed as a single scale, or as an object with scales specified for x and y. For "time" scales, data points should be `Date` objects or `getTime()` instances. This prop should be set at the top-level of the chart in order to avoid being overwritten by the default value. In other words, unless an individual chart component is being used as a standalone component (without a `VictoryChart` wrapper), this prop should be added to the `VictoryChart` component. :::note The `x` value supplied to the `scale` prop refers to the _independent_ variable, and the `y` value refers to the _dependent_ variable. This may cause confusion in horizontal charts, as the independent variable will correspond to the y axis. ::: :::caution On categorical axis domains (such as bar chart), the only valid scale is "linear". ::: _examples:_ - `scale="time"` - `scale={{x: "linear", y: "log"}}` ```jsx live Math.pow(1 - d.x, 10)} /> ``` In this example, a [discontinous scale plugin from d3fc](https://github.com/d3fc/d3fc/blob/master/packages/d3fc-discontinuous-scale/README.md) can be used to create a custom scale function to skip weekends along the x-axis. :::note The data set has already been filtered to only include weekdays. ::: ```jsx live noInline function App() { const data = [ { x: new Date(2021, 5, 1), y: 8 }, { x: new Date(2021, 5, 2), y: 10 }, { x: new Date(2021, 5, 3), y: 7 }, { x: new Date(2021, 5, 4), y: 4 }, { x: new Date(2021, 5, 7), y: 6 }, { x: new Date(2021, 5, 8), y: 3 }, { x: new Date(2021, 5, 9), y: 7 }, { x: new Date(2021, 5, 10), y: 9 }, { x: new Date(2021, 5, 11), y: 6 }, ]; const discontinuousScale = scaleDiscontinuous( d3Scale.scaleTime(), ).discontinuityProvider( discontinuitySkipWeekends(), ); return ( ); } render(); ``` --- ### sharedEvents Used to coordinate events between Victory components using `VictorySharedEvents`. :::warning This prop should not be set manually. ::: --- ### singleQuadrantDomainPadding By default `domainPadding` is coerced to existing quadrants. This means that if a given domain only includes positive values, no amount of padding applied by `domainPadding` will result in a domain with negative values. This is the desired behavior in most cases. For users that need to apply padding without regard to quadrant, the `singleQuadrantDomainPadding` prop may be used. This prop may be given as a boolean or an object with boolean values specified for "x" and/or "y". When this prop is false (or false for a given dimension), padding will be applied without regard to quadrant. If this prop is not specified, `domainPadding` will be coerced to existing quadrants. :::note The `x` value supplied to the `singleQuadrantDomainPadding` prop refers to the _independent_ variable, and the `y` value refers to the _dependent_ variable. This may cause confusion in horizontal charts, as the independent variable will corresponds to the y axis. ::: #### Common Usage - `singleQuadrantDomainPadding={false}` - `singleQuadrantDomainPadding={{ x: false }}` ```jsx live ``` --- ### standalone Specifies whether the component should be rendered in an independent `` element or in a `` tag. This prop defaults to true, and renders an `svg`. Wrapper components like `VictoryChart`, `VictoryStack`, and `VictoryGroup` force children to use `standalone={false}`. ```jsx live ``` --- ### width The `width` prop determines the width of the containing ``. By default Victory components render responsive containers with the `viewBox` attribute set to `viewBox="0, 0, width, height"` and `width="100%"`, `height="auto"`. In responsive containers, the `width` and `height` props affect the _aspect ratio_ of the rendered component, while the absolute width and height are determined by the container. To render a static container, pass `responsive={false}` to the `containerComponent` like `containerComponent={}`, or set `standalone={false}` and render the resulting `` tag in your own `` container. When a component is nested within `VictoryChart`, `VictoryStack`, or `VictoryGroup` setting `width` prop on the child component will have no effect. ```jsx live
} />
``` [x]: #x [y]: #y [grayscale theme]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-theme/grayscale.tsx [width]: #width [height]: #height [victorylabel]: /docs/api/victory-label [victorytooltip]: /docs/api/victory-tooltip [victoryportal]: /docs/api/victory-portal [victoryboxplot]: /docs/api/victory-box-plot [victoryclipcontainer]: /docs/api/victory-clip-container [victorybrushcontainer]: /docs/api/victory-brush-container [victorycursorcontainer]: /docs/api/victory-cursor-container [victoryselectioncontainer]: /docs/api/victory-selection-container [victoryvoronoicontainer]: /docs/api/victory-voronoi-container [victoryzoomcontainer]: /docs/api/victory-zoom-container [createcontainer]: /docs/guides/containers [victoryanimation]: /docs/api/victory-animation [victorytransition]: /docs/api/victory-transition [sortby]: https://lodash.com/docs/4.17.4#sortBy [animations guide]: /docs/guides/animations [data accessors guide]: /docs/guides/data-accessors [custom components guide]: /docs/guides/custom-components [events guide]: /docs/guides/events [themes guide]: /docs/guides/themes ================================================ FILE: website/docs/api/victory-container-props.mdx ================================================ --- title: VictoryContainerProps --- A set of props available to Victory [container](/docs/guides/containers) components. ## Props --- ### aria-describedby The `aria-describedby` prop applies to the `svg` element rendered by `VictoryContainer`. This prop should be given as a string corresponding to the id of an element that describes the chart. If the `desc` prop is set on `VictoryContainer`, the `aria-describedby` prop applied to `VictoryContainer`'s `svg` will correspond to the id of the `desc` tag `VictoryContainer` renders. --- ### aria-labelledby The `aria-labelledby` prop applies to the `svg` element rendered by `VictoryContainer`. This prop should be given as a string corresponding to the id of an element that labels the chart. If the `title` prop is set on `VictoryContainer`, the `aria-labelledby` prop applied to `VictoryContainer`'s `svg` will correspond to the id of the `title` tag `VictoryContainer` renders. --- ### children The `children` prop specifies the child or children that will be rendered within the container. This prop should not be set manually. It will be set by whatever Victory component is rendering the container. --- ### className The `className` prop specifies a className that will be applied to the outer-most div rendered by `VictoryContainer` if this prop is not set, the className will default to "VictoryContainer" --- ### containerId The `containerId` prop may be used to set a deterministic id for the container. When a `containerId` is not manually set, a unique id will be generated. It is usually necessary to set deterministic ids for automated testing. --- ### containerRef The `containerRef` prop may be used to attach a ref to the outermost element rendered by the container. This prop should be given as a function. _example:_ `containerRef={(ref) => { this.chartRef = ref; }}` --- ### desc The `desc` prop specifies the description of the chart/SVG to assist with accessibility for screen readers. The more descriptive this title is, the more useful it will be for people using screen readers. _example:_ `desc="Golden retrievers make up 30%, Labs make up 25%, and other dog breeds are not represented above 5% each."` --- ### events The `events` prop attaches arbitrary event handlers to the container element. This prop should be given as an object of event names and corresponding [React event handlers][]. Events defined directly via this prop will be masked by `defaultEvents` on `VictorySelectionContainer` (`onMouseDown`, `onMouseUp`, and `onMouseMove`), and by any events defined through Victory's event system that target parent elements. _example:_ `events={{onClick: (evt) => alert("x: " + evt.clientX)}}` --- ### height The `height` prop determines the height of the containing ``. By default VictoryContainer renders responsive containers with the `viewBox` attribute set to `viewBox="0, 0, width, height"` and `width="100%"`, `height="100%"`. In responsive containers, the `width` and `height` props affect the _aspect ratio_ of the rendered component, while the absolute width and height are determined by the container. To render a static container, set `responsive={false}` _example:_ `height={350}` --- ### name The `name` prop is used to reference a component instance when defining shared events. --- ### origin The origin prop is used to define the center point in svg coordinates for polar charts. :::note This prop is typically not set by external consumers. ::: --- ### ouiaId The `ouiaId` prop outputs an id attribute called `data-ouia-component-id`, which must be unique within the surrounding context of the component. This prop is used by the Open UI Automation 1.0-RC spec to help maintain automated testing environments. Components that are OUIA compliant must provide the following props; `ouiaId`, `ouiaSafe`, and `ouiaType`. --- ### ouiaSafe The `ouiaSafe` outputs an attribute called `data-ouia-safe`, which indicates that the component is in a static state. This prop is used by the Open UI Automation 1.0-RC spec to help maintain automated testing environments. Components that are OUIA compliant must provide the following props; `ouiaId`, `ouiaSafe`, and `ouiaType`. --- ### ouiaType The `ouiaType` prop outputs an attribute called `data-ouia-component-type`, which specifies a unique name identifying the root level HTML element. This prop is used by the Open UI Automation 1.0-RC spec to help maintain automated testing environments. Components that are OUIA compliant must provide the following props; `ouiaId`, `ouiaSafe`, and `ouiaType`. _example:_ A page that has a special container could choose to name that container as `FrameworkA/CustomContainer`. --- ### polar Specifies whether a chart should be plotted on a polar coordinate system. :::note This prop is typically not set by external consumers on containers ::: --- ### portalComponent The `portalComponent` prop takes a component instance which will be used as a container for children that should render inside a top-level container so that they will always appear above other elements. [VictoryTooltip][] renders inside a portal so that tooltips always render above data. [VictoryPortal][] is used to define elements that should render in the portal container. This prop defaults to [Portal][], and should only be overridden when changing rendered elements from SVG to another type of element _e.g.,_ [react-native-svg][] elements. --- ### portalZIndex The `portalZIndex` prop determines the z-index of the div enclosing the portal component. If a `portalZIndex` prop is not set, the z-index of the enclosing div will be set to 99. --- ### preserveAspectRatio The `preserveAspectRatio` prop applies to the `svg` elements rendered by `VictoryContainer` to give users more control over how responsive svgs are positioned and scaled. When the `responsive` prop on `VictoryContainer` is set to `false`, this prop has no effect. ```jsx live
} theme={VictoryTheme.clean} >
``` --- ### responsive The `responsive` prop specifies whether the rendered container should be a responsive container with a `viewBox` attribute, or a static container with absolute width and height. --- ### role The `role` prop specifies the role attribute that will be applied to the `svg` element rendered by `VictoryContainer` --- ### scale Specifies the scale for a container. :::note This prop is typically not set by external consumers on containers ::: --- ### style The `style` prop defines the style of the container, and should be given as an object of style attributes. The `width` and `height` should be specified via props instead of style attributes as they determine relative layout for components. _example:_ `style={{border: "1px solid #ccc"}}` --- ### tabIndex The `tabIndex` prop applies to the `svg` element rendered by `VictoryContainer` to allow users to focus on the chart container via keyboard navigation. This prop should be given as a number. --- ### theme The `theme` prop specifies a theme to use for determining styles and layout props for a component. Any styles or props defined in `theme` may be overridden by props specified on the component instance. --- ### title The `title` prop specifies the title to be applied to the SVG to assist with accessibility for screen readers. The more descriptive this title is, the more useful it will be for people using screen readers --- ### width The `width` prop determines the width of the containing ``. By default VictoryContainer renders responsive containers with the `viewBox` attribute set to `viewBox="0, 0, width, height"` and `width="100%"`, `height="auto"`. In responsive containers, the `width` and `height` props affect the _aspect ratio_ of the rendered component, while the absolute width and height are determined by the container. To render a static container, set `responsive={false}` ## Native-Only Props ### onTouchStart The optional `onTouchStart` prop takes a function that is called on every touch event on the chart (when using `victory-native`). The most common use of `onTouchStart` is to prevent the chart's parent `ScrollView` from scrolling, so that the chart and container can be interacted with unencumbered. The function accepts a single parameter, `event`, a React Native [Synthetic Event][]. Also see `onTouchEnd`. ```jsx this.setState({ scrollEnabled: false })} onTouchEnd={() => this.setState({ scrollEnabled: true })} /> } theme={VictoryTheme.clean} > ``` --- ### onTouchEnd The optional `onTouchEnd` prop takes a function that is called at the conclusion of every touch event on the chart (when using `victory-native`). The most common use of `onTouchEnd` is to prevent the chart's parent `ScrollView` from scrolling, so that the chart and container can be interacted with unencumbered. The function accepts a single parameter, `event`, a React Native [Synthetic Event][]. Also see `onTouchStart`. --- [victoryportal]: /docs/api/victory-portal [portal]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-portal/portal.tsx [react-native-svg]: https://github.com/react-native-community/react-native-svg [victorytheme]: /docs/api/victory-theme [victorytooltip]: /docs/api/victory-tooltip [grayscale theme]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-theme/grayscale.tsx [read more about themes here]: /docs/guides/themes [synthetic event]: https://facebook.github.io/react-native/docs/gesture-responder-system.html#responder-lifecycle [react event handlers]: https://reactjs.org/docs/handling-events.html ================================================ FILE: website/docs/api/victory-container.mdx ================================================ --- title: VictoryContainer --- `VictoryContainer` provides a top-level `` element for other Victory components to render within. By default, `VictoryContainer` renders responsive SVGs. `VictoryContainer` also provides a [Portal][] container that can be accessed via [VictoryPortal][] in order to render specified children above others. All other Victory containers extend `VictoryContainer`. Check out documentation for [common container props][] for more information about customizing container props. :::info For an overview of containers, visit the [containers](/docs/guides/containers) guide. ::: ## Inherited Props [victoryportal]: /docs/api/victory-portal [portal]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-portal/portal.tsx [common container props]: /docs/api/victory-container-props ================================================ FILE: website/docs/api/victory-cursor-container.mdx ================================================ --- title: VictoryCursorContainer --- Adds a cursor to a chart to inspect coordinates. :::info For examples of `VictoryCursorContainer` in action, visit the [Tooltips](/docs/guides/tooltips) guide. ::: ## Inherited Props ## Component Props --- ### cursorComponent The `cursorComponent` prop takes a component instance which will be used to render a cursor element. The new element created will be supplied with `x1`, `y1`, `x2` and `y2` positioning props. If a `cursorComponent` is not supplied, a new [LineSegment][] component will be rendered. --- ### cursorDimension When the `cursorDimension` prop is set, the cursor will be a line to inspect the given dimension (either "x" or "y"). When this prop is not specified, the cursor will be a 2-dimensional crosshair. For example, if you would like to inspect the time of time-series data, set `dimension={"x"}`; the cursor will then be a vertical line that will inspect the time value of the current mouse position. ```jsx live `${_.round(datum.x, 2)}, ${_.round( datum.y, 2, )}` } /> } /> ``` --- ### cursorLabel The `cursorLabel` prop defines the label that will appear next to the cursor. A label will only appear if `cursorLabel` is set. This prop should be given as a function of a point (an Object with `x` and `y` properties). _example:_ `cursorLabel={(point) => point.x}` --- ### cursorLabelComponent The `cursorLabelComponent` prop takes a component instance which will be used to render a label for the cursor. The new element created from the passed `cursorLabelComponent` will be supplied with the following props: `x`, `y`, `active`, `text`. If `cursorLabelComponent` is omitted, a new [VictoryLabel][] will be created with the props described above. --- ### cursorLabelOffset The `cursorLabelOffset` prop determines the pixel offset of the cursor label from the cursor point. This prop should be an Object with `x` and `y` properties, or a number to be used for both dimensions. --- ### defaultCursorValue Whenever the mouse is not over the chart, the cursor will not be displayed. If instead you would like to keep it displayed, use the `defaultCursorValue` prop to set the default value. The prop should be a point (an Object with `x` and `y` properties) for 2-dimensional cursors, or a number for 1-dimensional cursors. _examples:_ `defaultCursorValue={{x: 1, y: 1}}`, `defaultCursorValue={0}` --- ### disable When the `disable` prop is set to `true`, `VictoryCursorContainer` events will not fire. --- ### onCursorChange If provided, the `onCursorChange` function will be called every time the cursor value changes. `onCursorChange` is called with `value` (the updated cursor value) and `props` (the props used by `VictoryCursorContainer`). A common use for `onCursorChange` is to save the cursor value to state and use it in another part of the view. _example:_ `onCursorChange={(value, props) => this.setState({cursorValue: value})}` [victoryvoronoicontainer]: /docs/api/victory-voronoi-container [victorycontainer]: /docs/api/victory-container [victorylabel]: /docs/api/victory-label [linesegment]: /docs/api/victory-primitives#linesegment ================================================ FILE: website/docs/api/victory-datatable-props.mdx ================================================ --- title: VictoryDatatableProps --- A set of props available to components that implement data visualization in Victory. These props are used to define the data that will be visualized, as well as the appearance and behavior of the visualization. ## Props --- ### categories Specifies how categorical data for a chart should be ordered. This prop should be given as an array of string values, or an object with these arrays of values specified for x and y. If this prop is not set, categorical data will be plotted in the order it was given in the data array. :::note The `x` value supplied to the `categories` prop refers to the _independent_ variable, and the `y` value refers to the _dependent_ variable. This may cause confusion in horizontal charts, as the independent variable will corresponds to the y axis. ::: ```jsx live ``` --- ### data By default, Victory components expect data as an array of objects with `x` and `y` properties. Use the [x][] and [y][] data accessor props to define a custom data format. Data objects may also include information about ~~styles~~, labels, and props that may be applied to individual data components. :::note All values stored on the data object will be interpolated during animation. Do not store functions on data objects. ::: ```jsx live datum.fill, opacity: ({ datum }) => datum.opacity, }, }} /> ``` --- ### dataComponent A component instance which will be responsible for rendering a data element. The new element created from the passed `dataComponent` will be provided with all the props it needs to render. These props will always include `data`, `events`, `scale` and `style`. Individual components will supply additional props expected by their default `dataComponents`. See individual api docs for complete props lists. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If a `dataComponent` is not provided, each component will use its own default `dataComponent`. See the [Custom Components Guide][] for more detail on creating your own `dataComponents` ```jsx live noInline function CatPoint(props) { const { x, y, datum } = props; // VictoryScatter supplies x, y and datum const cat = datum._y >= 0 ? "😻" : "😹"; return ( {cat} ); } function App() { return ( } y={(d) => Math.sin(2 * Math.PI * d.x) } samples={15} /> ); } render(); ``` --- ### domain The `domain` prop describes the range of data the component will include. This prop can be given as an array of the minimum and maximum expected values of the data or as an object that specifies separate arrays for x and y. If this prop is not provided, a domain will be calculated from data, or other available information. :::note The `x` value supplied to the `domain` prop refers to the _independent_ variable, and the `y` value refers to the _dependent_ variable. This may cause confusion in horizontal charts, as the independent variable will corresponds to the y axis. ::: #### Common Usage - `domain={[-1, 1]}` - `domain={{x: [0, 100], y: [0, 1]}}` ```jsx live ``` --- ### domainPadding The `domainPadding` prop specifies a number of pixels of padding to add to the beginning or end of a domain. This prop is useful for explicitly spacing data elements farther from the beginning or end of a domain to prevent axis crowding. When given as a single number, `domainPadding` will be applied to the upper and lower bound of both the x and y domains. This prop may also be given as an object with numbers or two-element arrays specified for x and y. When specifying arrays for `domainPadding`, the first element of the array will specify the padding to be applied to domain minimum, and the second element will specify padding the be applied to domain maximum. :::note The `x` value supplied to the `domainPadding` prop refers to the _independent_ variable, and the `y` value refers to the _dependent_ variable. This may cause confusion in horizontal charts, as the independent variable will corresponds to the y axis. ::: #### Common Usage - `domainPadding={20}` - `domainPadding={{ x: [20, 0] }}` :::note Values supplied for `domainPadding` will be coerced so that padding a domain will never result in charts including an additional quadrant. For example, if an original domain included only positive values, `domainPadding` will be coerced so that the resulted padded domain will not include negative values. ::: ```jsx live ``` --- ### samples Specifies how many individual points to plot when plotting y as a function of x. The `samples` prop is ignored if `data` is supplied in props. ```jsx live Math.sin(5 * Math.PI * d.x) } /> Math.cos(5 * Math.PI * d.x) } /> ``` --- ### sortKey Indicates how data should be sorted. This prop is given directly to the lodash [sortBy][] function to be executed on the final dataset. :::note Sorting only applies to categorical axis data. Linear data will not be sorted. ::: #### Common Usage `string` - specify which property in a data object to sort the data array by ```jsx sortKey = "x"; ``` `function` - use a function to determine how to sort data elements in an array ```jsx sortKey={(datum) => datum.xValue + datum.error} ``` `number` - specify which index of an array should be used to sort data when data is given as an array of arrays ```jsx sortKey={0} ``` `array` - specify multiple properties to sort by ```jsx sortKey={["age", "height"]} ``` ```jsx live ({ t }), )} sortKey="t" x={(d) => Math.sin(3 * d.t + 2 * Math.PI) } y={(d) => Math.sin(2 * d.t)} /> ``` --- ### sortOrder The `sortOrder` prop specifies whether sorted data should be returned in ascending or descending order. --- ### x Use the `x` data accessor prop to determine how the component defines data in the x dimension. `string` - specify which property in an array of data objects should be used as the x value. This string may reference a nested property using dot notation. ```jsx x="name"; x="employees.name" ``` `string[]` - specify which property in an array of nested data objects should be used as an x value ```jsx x={["employees", "name"]} ``` `number` - specify which index of an array should be used as an x value when data is given as an array of arrays ```jsx x={0} ``` `function` - use a function to translate each element in a data array into an x value ```jsx x={(datum) => datum.xValue + datum.error} ``` See the [Data Accessors Guide][] for more detail on formatting and processing data. --- ### y Use the `y` data accessor prop to determine how the component defines data in the x dimension. `string` - specify which property in an array of data objects should be used as the x value. This string may reference a nested property using dot notation. ```jsx y="salary"; y="employees.salary" ``` `string[]` - specify which property in an array of nested data objects should be used as an x value ```jsx y={["employees", "salary"]} ``` `number` - specify which index of an array should be used as an x value when data is given as an array of arrays ```jsx y={0} ``` `function` - use a function to translate each element in a data array into an x value ```jsx y={(datum) => Math.sin(2 * Math.PI * datum.x)} ``` See the [Data Accessors Guide][] for more detail on formatting and processing data. ### y0 --- Use the `y0` data accessor prop to determine how the component defines the baseline of `y` data. `string` - specify which property in an array of data objects should be used as the `y0` value. This string may reference a nested property using dot notation. ```jsx y0="last_quarter_profit"; y0="sales.last_quarter_profit" ``` `string[]` - specify which property in an array of nested data objects should be used as a `y0` value ```jsx y0={["sales", "last_quarter_profit"]} ``` `number` - specify which index of an array should be used as an `y0` value when data is given as an array of arrays ```jsx y0={1} ``` `function` - use a function to translate each element in a data array into an `y0` value ```jsx y0={() => 10} ``` See the [Data Accessors Guide][] for more detail on formatting and processing data. [x]: #x [y]: #y [grayscale theme]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-theme/grayscale.tsx [width]: #width [height]: #height [victorylabel]: /docs/api/victory-label [victorytooltip]: /docs/api/victory-tooltip [victoryportal]: /docs/api/victory-portal [victoryboxplot]: /docs/api/victory-box-plot [victoryclipcontainer]: /docs/api/victory-clip-container [victorybrushcontainer]: /docs/api/victory-brush-container [victorycursorcontainer]: /docs/api/victory-cursor-container [victoryselectioncontainer]: /docs/api/victory-selection-container [victoryvoronoicontainer]: /docs/api/victory-voronoi-container [victoryzoomcontainer]: /docs/api/victory-zoom-container [createcontainer]: /docs/guides/containers [victoryanimation]: /docs/api/victory-animation [victorytransition]: /docs/api/victory-transition [sortby]: https://lodash.com/docs/4.17.4#sortBy [animations guide]: /docs/guides/animations [data accessors guide]: /docs/guides/data-accessors [custom components guide]: /docs/guides/custom-components [events guide]: /docs/guides/events [themes guide]: /docs/guides/themes ================================================ FILE: website/docs/api/victory-error-bar.mdx ================================================ --- title: VictoryErrorBar --- :::info For examples of `VictoryErrorBar` in action, visit the [Error Bar](/docs/charts/error-bar) examples. ::: ## Inherited Props ## Component Props --- ### borderWidth The `borderWidth` prop sets the border width of the error bars. `borderWidth` will set both x and y error bar width. ```jsx borderWidth={10} ``` --- ### data Specify data via the data prop. By default, `VictoryErrorBar` expects data as an array of objects with `x`, `y`, `errorX` and `errorY` keys. Use the `x`, `y`, `errorX` and `errorY` data accessor props to specify custom data formats. --- ### errorX Use `errorX` data accessor prop to define the x error bar. **string:** specify which property in an array of data objects should be used as the errorX value _examples:_ `errorX="uncertainty"` **function:** use a function to translate each element in a data array into a errorX value _examples:_ `errorX={() => 10}` **array index:** specify which index of an array should be used as a errorX value when data is given as an array of arrays _examples:_ `errorX={1}` **path string or path array:** specify which property in an array of nested data objects should be used as an errorX value _examples:_ `errorX="measurement.uncertainty"`, `errorX={["measurement", "uncertainty"]}` --- ### errorY Use `errorY` data accessor prop to define the y error bar. **string:** specify which property in an array of data objects should be used as the errorY value _examples:_ `errorY="uncertainty"` **function:** use a function to translate each element in a data array into an errorY value _examples:_ `errorY={() => 10}` **array index:** specify which index of an array should be used as an errorY value when data is given as an array of arrays _examples:_ `errorY={1}` **path string or path array:** specify which property in an array of nested data objects should be used as an errorY value _examples:_ `errorY="measurement.uncertainty"`, `errorY={["measurement", "uncertainty"]}` --- ### eventKey `VictoryErrorBar` uses the standard `eventKey` prop to specify how event targets are addressed. **This prop is not commonly used.** [Read about the `eventKey` prop in more detail here](/docs/guides/events) ```jsx eventKey = "x"; ``` --- ### events `VictoryErrorBar` uses the standard `events` prop. [Read about it here](/docs/guides/events) See the [Events Guide][] for more information on defining events. ```jsx live

Click an error bar below

{ return [ { target: "data", mutation: (props) => { const stroke = props.style && props.style.stroke; return stroke === "#c43a31" ? null : { style: { stroke: "#c43a31", strokeWidth: 7, }, }; }, }, ]; }, }, }, ]} data={[ { x: 15, y: 35, errorX: 1, errorY: 3, }, { x: 20, y: 42, errorX: 3, errorY: 2, }, { x: 25, y: 30, errorX: 5, errorY: 5, }, { x: 30, y: 35, errorX: 5, errorY: 3, }, { x: 35, y: 22, errorX: 8, errorY: 2, }, ]} />
``` --- ### style `VictoryErrorBar` uses the standard `style` prop. [Read about it here](/docs/guides/themes) _default (provided by default theme):_ See [grayscale theme][] for more detail ```jsx live datum.x} /> ``` ================================================ FILE: website/docs/api/victory-group.mdx ================================================ --- title: VictoryGroup --- `VictoryGroup` is a wrapper component that renders a given set of children with some shared props. `VictoryGroup` reconciles the domain and layout for all its children, and coordinates animations and shared events. `VictoryGroup` may also be used to supply common data and styles to all its children. This is especially useful when adding markers to a line, or adding voronoi tooltips to data. `VictoryGroup` may also be used to apply an offset to a group of children, as with grouped bar charts, or may be used to stack several components on the same level, _e.g.,_ stacked area charts with data markers. `VictoryGroup` works with: [VictoryArea][], [VictoryBar][], [VictoryBoxPlot][], [VictoryCandlestick][], [VictoryErrorBar][], [VictoryLine][], [VictoryScatter][], [VictoryHistogram][], [VictoryStack][], and [VictoryVoronoi][]. :::note `VictoryGroup` _should not_ be used with [VictoryAxis][] children. Use [VictoryChart][] instead. ::: ```jsx live ``` ## Inherited Props ## Component Props --- ### children `VictoryGroup` works with any combination of the following children: [VictoryArea][], [VictoryBar][], [VictoryCandlestick][], [VictoryErrorBar][], [VictoryLine][], [VictoryScatter][], [VictoryHistogram][], [VictoryStack][], and [VictoryVoronoi][]. Children supplied to `VictoryGroup` will be cloned and rendered with new props so that all children share common props such as `domain` and `scale`. --- ### color The `color` prop is an optional prop that defines a single color to be applied to the children of `VictoryGroup`. The `color` prop will override colors specified via `colorScale`. ```jsx live ``` --- ### eventKey `VictoryGroup` uses the standard `eventKey` prop to specify how event targets are addressed. **This prop is not commonly used.** [Read about the `eventKey` prop in more detail here](/docs/guides/events) ```jsx eventKey = "x"; ``` --- ### events `VictoryGroup` uses the standard `events` prop. [Read about it in more detail here](/docs/guides/events) :::note `VictoryGroup` coordinates events between children using the `VictorySharedEvents` and the `sharedEvents` prop ::: ```jsx live { return [ { childName: "bar-2", target: "data", mutation: (props) => ({ style: Object.assign( {}, props.style, { fill: "gold" }, ), }), }, { childName: "bar-3", target: "data", mutation: (props) => ({ style: Object.assign( {}, props.style, { fill: "orange" }, ), }), }, { childName: "bar-4", target: "data", mutation: (props) => ({ style: Object.assign( {}, props.style, { fill: "red" }, ), }), }, ]; }, }, }, ]} > ``` --- ### offset The `offset` prop determines the number of pixels each element in a group should be offset from its original position on the independent axis. In the case of groups of bars, this number should be equal to the width of the bar plus the desired spacing between bars. ```jsx live ``` --- ### style Defines the style of the component using [VictoryStyleInterface](/docs/api/victory-style-interface). Styles on children of `VictoryGroup` will override styles set on the `VictoryGroup` component. _default (provided by default theme):_ See [grayscale theme][] for more detail ```jsx live ``` [animations guide]: /docs/guides/animations [data accessors guide]: /docs/guides/data-accessors [events guide]: /docs/guides/events [themes guide]: /docs/guides/themes [grayscale theme]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-theme/grayscale.tsx [victoryarea]: /docs/api/victory-area [victoryaxis]: /docs/api/victory-axis [victorybar]: /docs/api/victory-bar [victoryboxplot]: /docs/api/victory-boxplot [victorycandlestick]: /docs/api/victory-candlestick [victorychart]: /docs/api/victory-chart [victoryerrorbar]: /docs/api/victory-error-bar [victoryline]: /docs/api/victory-line [victoryscatter]: /docs/api/victory-scatter [victoryhistogram]: /docs/api/victory-histogram [victorystack]: /docs/api/victory-stack [victoryvoronoi]: /docs/api/victory-voronoi [victorylabel]: /docs/api/victory-label [x]: /docs/api/victory-datatable-props#x [y]: /docs/api/victory-datatable-props#y ================================================ FILE: website/docs/api/victory-histogram.mdx ================================================ --- title: VictoryHistogram --- :::info For examples of `VictoryHistogram` in action, visit the [Histogram Chart](/docs/charts/histogram) examples. ::: ## Inherited Props ## Component Props --- ### bins **`VictoryHistogram` uses [`d3.bin`](https://observablehq.com/@d3/d3-bin) to do binning.** The `bins` prop is used to specify how the data will be binned. There are a few options for this, the first being passing no value, ie the default behavior, which is letting `d3.bin` generate the buckets based on the data. The second is passing a number, which specifies _approximately_ the number of bins to generate, this is not a guarantee (see `d3.bin` for more details). The last options are passing an array of numbers or dates (depending on the data), this array represents an array of thresholds. So for example if the bin prop provided is `[0, 10, 20, 35]`, this would result in 3 bins, that would look like [0, 10) , [10, 20), \[20, 35\]. This prop allows for a lot of flexibility in how the data is displayed. For example it is possible to have uneven sized bins if so desired. It is also possible to group the data by days, weeks, or years. ```jsx live ``` ```jsx live ``` ```jsx live noInline const App = () => { const niceTimeScale = d3Scale .scaleTime() .domain( d3Array.extent( sampleHistogramDateData, ({ x }) => x, ), ) .nice(); // get thresholds to bin data by months const bins = niceTimeScale.ticks( d3Time.utcMonth, ); // try utcDay return ( ); }; render(); ``` --- ### binSpacing The `binSpacing` prop is used to specify space between each bin. `binSpacing` represents the number of pixels that will be between each bin (including at the beginning and end of the bins). By default, bins are rendered with no spacing. ```jsx live ``` --- ### cornerRadius The `cornerRadius` prop specifies a radius to apply to each bar. If this prop is given as a single number, the radius will only be applied to the _top_ of each bar. When this prop is given as a function, it will be evaluated for each bar with the props object corresponding to that bar. ```jsx live datum.y * 4, }} data={sampleHistogramData} /> ``` --- ### data `VictoryHistogram` uses the standard data prop, except for it only expects each object within the array to have an `x` property. The `x` data accessor prop can be used to define a custom data property. Because each bar represents a bin rather than a particular data point (like with `VictoryScatter` for example), when accessing `datum` via a prop that passes `datum` such as style, `datum` will have properties `x`, `x0`, `x1`, `y`, and `binnedData`. `x` is the midpoint between the bin, `x0` is the beginning of the bin, `x1` is the end of the bin, `y` is the aggregate amount of data points within that bin, and `binnedData` is an array of the original data points that were grouped into this bin. --- ### eventKey `VictoryHistogram` uses the standard `eventKey` prop to specify how event targets are addressed. **This prop is not commonly used.** [Read about the `eventKey` prop in more detail here](/docs/guides/events) --- ### events `VictoryHistogram` uses the standard `events` prop. [Read about it here](/docs/guides/events) See the [Events Guide][] for more information on defining events. ```jsx live

Click a bar below

{ return [ { target: "data", mutation: (props) => { const fill = props.style && props.style.fill; return fill === "black" ? null : { style: { fill: "black", }, }; }, }, ]; }, }, }, ]} data={sampleHistogramData} theme={VictoryTheme.clean} />
``` --- ### style `VictoryHistogram` uses the standard `style` prop. [Read about it here](/docs/guides/themes) _default (provided by default theme):_ See [grayscale theme][] for more detail ```jsx live datum.y === 3 ? "#000000" : "lightblue", stroke: ({ index }) => +index % 2 === 0 ? "#000000" : "lightblue", fillOpacity: 0.7, strokeWidth: 3, }, labels: { fontSize: 15, fill: ({ datum }) => datum.y === 3 ? "#000000" : "lightblue", }, }} data={sampleHistogramData} labels={({ datum }) => datum.y} /> ``` ================================================ FILE: website/docs/api/victory-label.mdx ================================================ --- title: VictoryLabel --- VictoryLabel renders the label components that are used across all of Victory. ## Component Props --- ### angle The `angle` prop specifies the angle to rotate the text around its anchor point. ```jsx live } /> ``` --- ### backgroundComponent The `backgroundComponent` prop takes a component instance which will be used to create backgrounds for labels. The new element created from the passed `backgroundComponent` will be supplied with the following properties: x, y, height, width, style, and transform. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `backgroundComponent` is omitted, a default [Rect][] component will be created with props described above. `backgroundComponent` is only rendered when a `backgroundStyle` prop is added to `VictoryLabel`. _examples:_ `backgroundComponent={}` --- ### backgroundPadding The `backgroundPadding` prop adds padding around background elements. This prop may be given as a number or an object with values for "top", "bottom", "left", and "right". In the case of multi-line, multi-background labels, this prop may be given as an array. ```jsx live ["This is a", "multi-line", "label"]} labelComponent={ } /> ``` --- ### backgroundStyle The `backgroundStyle` prop defines a set of SVG style properties that will be applied to the rendered background element(s). This prop should be given as an object, or array of objects. When this prop is given as an array of objects _and_ there are multi-line labels, multiple background elements will be rendered, and styled individually. When this prop is given as an object, a single background element will be rendered for the entire label. ```jsx live ["This is a", "multi-line", "label"]} labelComponent={ } /> ``` --- ### capHeight The `capHeight` prop defines a text metric for the font being used: the expected height of capital letters. This is necessary because of SVG, which (a) positions the _bottom_ of the text at `y`, and (b) has no notion of line height. This prop should be given as a number of ems. --- ### className The `className` prop specifies a class name that will be applied to the rendered text element. --- ### data Victory components can pass a `data` prop to their label component. This can be useful in custom components that need to make use of the entire dataset. --- ### datum Victory components can pass a `datum` prop to their label component. This can be used to calculate functional styles, and determine text. If `x` and `y` are not specified, `datum` will be used to determine label position. --- ### desc The `desc` prop specifies the description of the chart/SVG to assist with accessibility for screen readers. The more descriptive this title is, the more useful it will be for people using screen readers. --- ### direction The `direction` prop determines which text direction to apply to the rendered `text` element --- ### dx The `dx` prop defines a horizontal shift from the `x` coordinate. ```jsx live ["This is a", "multi-line", "label"]} style={{ labels: { padding: 0 } }} labelComponent={ } /> ``` --- ### dy The `dy` prop defines a vertical shift from the `y` coordinate. This prop is affected by `capHeight`, `lineHeight`, and `verticalAnchor`, and the number of lines of text that make up the label. ```jsx live ["This is a", "multi-line", "label"]} style={{ labels: { padding: 0 } }} labelComponent={ } /> ``` --- ### events The `events` prop attaches arbitrary event handlers to the label component. This prop should be given as an object of event names and corresponding event handlers. When events are provided via Victory's event system, event handlers will be called with the event, the props of the component it is attached to, and an `eventKey`. _example:_ `events={{onClick: (evt) => alert("x: " + evt.clientX)}}` --- ### groupComponent The `groupComponent` prop takes a component instance which will be used to create group elements when `VictoryLabel` renders both labels and backgrounds. _default:_ `` --- ### id The `id` prop specifies a HTML ID that will be applied to the rendered text element. --- ### inline When the `text` property contains an array of strings, the `inline` property lets the `` elements lay out next to each other. If this property is not specified, the `` elements will stack vertically instead. _default:_ `false` ```jsx live ["This is a", "multi-line", "label"]} labelComponent={ } /> ``` --- ### labelPlacement The `labelPlacement` prop is used to specify the placement of labels relative to the data point they represent. This prop may be given as "vertical", "parallel" or "perpendicular". This props is particularly useful in polar charts, where it may be desirable to position a label either parallel or perpendicular to its corresponding angle. When this prop is not set, perpendicular label placement will be used for polar charts, and vertical label placement will be used for cartesian charts. --- ### lineHeight The `lineHeight` prop defines how much space a single line of text should take up. Note that SVG has no notion of line-height, so the positioning may differ slightly from what you would expect with CSS, but the result is similar: a roughly equal amount of extra space is distributed above and below the line of text. This prop should be given as a number of ems. ```jsx live ["This is a", "multi-line", "label"]} labelComponent={ } /> ``` --- ### origin Victory components will pass an `origin` prop is to define the center point in svg coordinates for polar charts. **This prop should not be set manually.** --- ### polar Victory components can pass a boolean `polar` prop to specify whether a label is part of a polar chart. **This prop should not be set manually.** --- ### renderInPortal The `renderInPortal` prop specifies whether `VictoryLabel` should render text in place or within a `VictoryPortal`. Setting `renderInPortal` to true is equivalent to wrapping `VictoryLabel` in a `VictoryPortal`. This prop is false by default. --- ### scale Victory components can pass a `scale` prop to their label component. This can be used to calculate the position of label elements from `datum`. **This prop should not be set manually.** --- ### style The `style` prop defines a set of SVG style properties that will be applied to the rendered `` element. This prop should be given as an object, or array of objects. When this prop is given as an array of objects, each style object in the array will be applied to the corresponding `` in multi-line labels. When this prop is given as an array with fewer elements than there are `` elements, the _first_ element of the style array will be applied to extra lines. ```jsx live ["This is a", "multi-line", "label"]} labelComponent={ } /> ``` --- ### tabIndex The `tabIndex` prop specifies the `tabIndex` that will be applied to the rendered label. This prop may be given as a number or as a function that returns a number. --- ### text The `text` prop defines the text `VictoryLabel` will render. The `text` prop may be given as a string, number, a function of `datum`, or an array of any of these. Strings may include newline characters, which `VictoryLabel` will split into separate `` elements. When `text` is given as an array, separate `` elements will be created for each element in the array. _examples:_ `text={(datum) => "x: " + datum.x}`, `text="Apples\n(green)"`, `text={["first line", "second line"]}` ```jsx live [`x: ${datum.x}`, `y: ${datum.y}`]} /> } /> ``` --- ### textComponent The `textComponent` prop takes a component instance which will be used to create text elements when `VictoryLabel` renders labels. --- ### textAnchor The `textAnchor` prop defines how the text is horizontally positioned relative to the given `x` and `y` coordinates. Options are "start", "middle", "end", and "inherit". This prop may also be given as a function that returns one of these options. ```jsx live ["This is a", "multi-line", "label"]} labelComponent={ text.length > 1 ? "start" : "middle"} /> } /> ``` --- ### transform The `transform` prop applies a transform to the rendered `` element. This prop may be supplied as a string or an object containing transform definitions. ```jsx live ["This is a", "multi-line", "label"]} labelComponent={ } /> ``` --- ### verticalAnchor The `verticalAnchor` prop defines how the text is vertically positioned relative to the given `x` and `y` coordinates. Options are "start", "middle" and "end". This prop may also be given as a function that returns one of these options. ```jsx live ["This is a", "multi-line", "label"]} labelComponent={ text.length > 1 ? "start" : "middle"} /> } /> ``` --- ### x The `x` prop defines the x coordinate to use as a basis for positioning the label element. Please note that this prop should be given in terms of `svg` coordinates, not data coordinates. To add a label annotation to a chart that is fixed to a specific _data_ coordinate, please use the `scale` prop that `VictoryChart` provides to its children to transform data coordinates into `svg` coordinates. ```jsx live noInline const DataLabel = props => { const x = props.scale.x(props.x); const y = props.scale.y(props.y); return }; const MyChart = () => { return ( ); }; render(); ``` --- ### y The `y` prop defines the y coordinate to use as a basis for positioning the label element. Please note that this prop should be given in terms of `svg` coordinates, not data coordinates. [Rect]: /docs/api/victory-primitives#rect ================================================ FILE: website/docs/api/victory-labelable-props.mdx ================================================ --- title: VictoryLabelableProps --- A set of props available to components that can display labels. ## Props --- ### labelComponent The `labelComponent` prop takes a component instance which will be used to render labels for the component. The new element created from the passed `labelComponent` will be supplied with the following properties: x, y, index, data, datum, verticalAnchor, textAnchor, angle, style, text, and events. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `labelComponent` is omitted, a new [VictoryLabel][] will be created with the props described above. [VictoryTooltip][] is commonly used as a `labelComponent` :::note `labelComponent` is not currently supported for `VictoryBoxPlot` as it has its own label components (`maxLabelComponent`, `medianLabelComponent`, `minLabelComponent`, `q1LabelComponent` & `q3LabelComponent`). See [VictoryBoxPlot][] for more info. ::: #### Common Usage - `labelComponent={}` - `labelComponent={}` ```jsx live datum.y} style={{ labels: { fill: "white" } }} labelComponent={} theme={VictoryTheme.clean} /> ``` ================================================ FILE: website/docs/api/victory-legend.mdx ================================================ --- title: VictoryLegend --- :::info For examples of `VictoryLegend` in action, visit the [Chart Legends](/docs/guides/legends) guide. ::: ## Inherited Props ## Component Props --- ### borderComponent The `borderComponent` prop takes a component instance which will be responsible for rendering a border around the legend. The new element created from the passed `borderComponent` will be provided with the following properties calculated by `VictoryLegend`: `x`, `y`, `width`, `height`, and `style`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If a `borderComponent` is not provided, `VictoryLegend` will use its default [Border][] component. Please note that the default width and height calculated for the border component is based on _approximated_ text measurements, and may need to be adjusted. ```jsx borderComponent={} ``` --- ### borderPadding The `borderPadding` specifies the amount of padding that should be added between the legend items and the border. This prop may be given as a number, or as an object with values specified for `top`, `bottom`, `left`, and `right`. Please note that the default width and height calculated for the border component is based on _approximated_ text measurements, so padding may need to be adjusted. ```jsx borderPadding={{ top: 20, bottom: 10 }} ``` --- ### centerTitle The `centerTitle` boolean prop specifies whether a legend title should be centered. ```jsx live ``` --- ### containerComponent `VictoryLegend` uses the standard `containerComponent` prop. [Read about it here](/docs/api/victory-common-theme-props#containercomponent) :::warning `VictoryLegend` only works with the `VictoryContainer` component ::: ```jsx containerComponent={} ``` --- ### data Specify data via the `data` prop. `VictoryLegend` expects data as an array of objects with `name` (required), `symbol`, and `labels` properties. The `data` prop must be given as an array. The symbol rendered may be changed by altering the `type` property of the `symbol` object. Valid types include: circle", "diamond", "plus", "minus", "square", "star", "triangleDown", and "triangleUp". If you want to use SVG icons from a custom component or an SVG based icon library like [react-icons](https://react-icons.github.io/react-icons/) use the `dataComponent` property. ```jsx live ``` --- ### dataComponent `VictoryLegend` uses the standard `dataComponent` prop. [Read about it here](/docs/api/victory-datatable-props#datacomponent) `VictoryLegend` supplies the following props to its `dataComponent`: `data`, `datum`, `events`, `index`, `x`, `y`, `size`, `style`, and `symbol`. `VictoryLegend` renders a [Point][] component by default. ```jsx dataComponent={} ``` An example of using Custom icons as `dataComponent` in `VictoryLegend`. ```jsx live noInline const { FaSun } = reactIconsFa; const CustomIcon = (props) => { return ( ); }; function App() { return ( } /> ); } render(); ``` An example of using multiple Custom icons as `dataComponent` in `VictoryLegend`. ```jsx live noInline const CustomMultipleIcon = (props) => { const { x, y, datum } = props; const Icon = reactIconsFa[datum.icon]; return ( ); }; function App() { return ( } /> ); } render(); ``` --- ### eventKey `VictoryLegend` uses the standard `eventKey` prop to specify how event targets are addressed. **This prop is not commonly used.** [Read about the `eventKey` prop in more detail here](/docs/guides/events) --- ### events `VictoryLegend` uses the standard `events` prop. [Read about it here](/docs/guides/events) See the [Events Guide][] for more information on defining events. ```jsx live

Click Me

{ return [ { target: "data", mutation: (props) => { const fill = props.style && props.style.fill; return fill === "#c43a31" ? null : { style: { fill: "#c43a31", }, }; }, }, { target: "labels", mutation: (props) => { return props.text === "clicked" ? null : { text: "clicked", }; }, }, ]; }, }, }, ]} data={[ { name: "One" }, { name: "Two" }, { name: "Three" }, ]} />
``` --- ### gutter The `gutter` prop defines the number of pixels between legend columns. This prop may be given as a number, or as an object with values specified for "left" and "right" gutters. To set spacing between rows, use the `rowGutter` prop. ```jsx live ``` --- ### itemsPerRow The `itemsPerRow` prop determines how many items to render in each row of a horizontal legend, or in each column of a vertical legend. This prop should be given as an integer. When this prop is not given, legend items will be rendered in a single row or column. ```jsx live ``` --- ### orientation The `orientation` prop takes a string that defines whether legend data are displayed in a row or column. When `orientation` is `"horizontal"`, legend items will be displayed in rows. When `orientation` is `"vertical"`, legend items will be displayed in columns. --- ### rowGutter The `rowGutter` prop defines the number of pixels between legend rows. This prop may be given as a number, or as an object with values specified for "top" and "bottom" gutters. To set spacing between columns, use the `gutter` prop. ```jsx live ``` --- ### style The `style` prop defines the style of the component. The style prop should be given as an object with styles defined for `parent`, `data`, `labels`, `title`, and `border`. Any valid svg styles are supported, but `width`, `height`, and `padding` should be specified via props as they determine relative layout for components in VictoryChart. Functional styles may be defined for `data`, and `labels` style properties, and they will be evaluated with the props corresponding to each element. _note:_ When a component is rendered as a child of another Victory component, or within a custom `` element with `standalone={false}` parent styles will be applied to the enclosing `` tag. Many styles that can be applied to a parent `` will not be expressed when applied to a ``. _note:_ custom `angle` and `verticalAnchor` properties may be included in `labels` and `title` styles. _default (provided by default theme):_ See [grayscale theme][] for more detail ```jsx live ``` --- ### symbolSpacer The `symbolSpacer` prop defines the number of pixels between data components and label components. When a `symbolSpacer` is not defined, spacing is calculated based on symbol size and label font size. ```jsx live ``` --- ### title The `title` prop specifies a title to render with the legend. This prop should be given as a string, or an array of strings for multi-line titles. ```jsx live ``` --- ### titleComponent The `titleComponent` prop takes a component instance which will be used to render a title for the component. The new element created from the passed `labelComponent` will be supplied with the following properties: `x`, `y`, `index`, `data`, `datum`, `verticalAnchor`, `textAnchor`, `style`, `text`, and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `labelComponent` is omitted, a new [VictoryLabel][] will be created with the props described above. ```jsx live } data={[ { name: "One" }, { name: "Two" }, { name: "Three" }, ]} /> ``` --- ### titleOrientation The `titleOrientation` prop specifies where the title should be rendered in relation to the rest of the legend. Possible values for this prop are "top", "bottom", "left", and "right". ```jsx live ``` --- ### x The `x` prop defines the x coordinate corresponding to the upper left corner of the legend. --- ### y The `y` prop defines the y coordinate corresponding to the upper left corner of the legend. [victorylabel]: /docs/api/victory-label [point]: /docs/api/victory-primitives#point [border]: /docs/api/victory-primitives#border [grayscale theme]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-theme/grayscale.tsx [read more about themes here]: /docs/guides/themes [custom components guide]: /docs/guides/custom-components [events guide]: /docs/guides/events ================================================ FILE: website/docs/api/victory-line.mdx ================================================ --- title: VictoryLine --- :::info For examples of `VictoryLine` in action, visit the [Line Chart](/docs/charts/line) examples. ::: ## Inherited Props ## Component Props --- ### eventKey `VictoryLine` uses the standard `eventKey` prop. **This prop is not commonly used.** [Read about the `eventKey` prop in more detail here](/docs/guides/events) :::note `VictoryLine` only renders one element per dataset, so only one event key will be generated. ::: --- ### events `VictoryLine` uses the standard `events` prop. [Read about it here](/docs/guides/events) :::note `VictoryLine` will use the special `eventKey` "all" rather than referring to data by index, as it renders only one element for an entire dataset ::: ```jsx live

Click the line below

{ return [ { target: "data", eventKey: "all", mutation: ({ style }) => { return style.stroke === "black" ? null : { style: { stroke: "black", strokeWidth: 5 } }; } } ]; } } }]} data={sampleData} theme={VictoryTheme.clean} />
``` --- ### interpolation The `interpolation` prop determines how data points should be connected when creating a path. Victory uses [d3-shape](https://github.com/d3/d3-shape#curves) for interpolating curves. Polar line charts may use the following interpolation options: "basis", "cardinal", "catmullRom", "linear" Cartesian line charts may use the following interpolation options: "basis", "bundle", "cardinal", "catmullRom", "linear", "monotoneX", "monotoneY", "natural", "step", "stepAfter", "stepBefore" You can also provide a function if you need to adjust parameters for d3-shape curves or to use a [custom curve function](https://github.com/d3/d3-shape#custom-curves). ```jsx live ``` --- ### style `VictoryLine` uses the standard `style` prop. [Read about it here](/docs/guides/themes) :::note Since `VictoryLine` renders a single element to represent an entire dataset, it is not possible to use functional styles to change the style of the line as a function of an individual `datum`. Instead, try using [gradient fills](/docs/guides/themes) for styling continuous data. ::: _default (provided by default theme):_ See [grayscale theme][] for more detail ```jsx live data.length }, labels: { fontSize: 15, fill: ({ datum }) => datum.x === 3 ? "#000000" : "#c43a31" } }} data={sampleData} labels={({ datum }) => datum.x} /> ``` ================================================ FILE: website/docs/api/victory-multi-labelable-props.mdx ================================================ --- title: VictoryMultiLabelableProps --- A set of props available to components that can display multiple labels. ## Inherited Props ## Props --- ### labels The `labels` prop defines the labels that will appear above each point. This prop should be given as an array or as a function. When given as a function, `labels` will be called with a single argument: an object containing all the props supplied to the label component. A full list of props that will be passed to `VictoryLabel` is given [here](/docs/api/victory-label). #### Common Usage - `labels={["first", "second", "third"]}` - `labels={({ datum }) => datum.y}` ```jsx live `y: ${datum.y}`} theme={VictoryTheme.clean} /> ``` ================================================ FILE: website/docs/api/victory-pie.mdx ================================================ --- title: VictoryPie --- :::info For examples of `VictoryPie` in action, visit the [Pie Chart](/docs/charts/pie) examples. ::: ## Inherited Props ## Component Props --- ### cornerRadius The `cornerRadius` prop specifies the corner radius of the slices rendered in the pie chart. When given as a function, `cornerRadius` will be evaluated for each slice of the pie with an object corresponding to the props for that slice. ```jsx live datum.y * 5 } data={sampleData} theme={VictoryTheme.clean} /> ``` --- ### endAngle The `endAngle` props defines the overall end angle of the pie in degrees. This prop is used in conjunction with `startAngle` to create a pie that spans only a segment of a circle, or to change overall rotation of the pie. This prop should be given as a number of degrees. Degrees are defined as starting at the 12 o'clock position, and proceeding clockwise. ```jsx live
``` --- ### eventKey `VictoryPie` uses the standard `eventKey` prop to specify how event targets are addressed. **This prop is not commonly used.** [Read about the `eventKey` prop in more detail here](/docs/guides/events) --- ### events `VictoryPie` uses the standard `events` prop. [Read about it here](/docs/guides/events) See the [Events Guide][] for more information on defining events. ```jsx live

Click a pie slice below

{ return [ { target: "data", mutation: ({ style, }) => { return style.fill === "#c43a31" ? null : { style: { fill: "#c43a31", }, }; }, }, { target: "labels", mutation: ({ text, }) => { return text === "clicked" ? null : { text: "clicked", }; }, }, ]; }, }, }, ]} data={sampleData} theme={VictoryTheme.clean} />
``` --- ### innerRadius The `innerRadius` prop determines the number of pixels between the center of the chart and the inner edge of a donut chart. When this prop is set to zero a regular pie chart is rendered. When this prop is given as a function, `innerRadius` will be evaluated for each slice of the pie with the props corresponding to that slice ```jsx live datum.y * 20 } data={sampleData} theme={VictoryTheme.clean} /> ``` --- ### labelIndicator The `labelIndicator` prop defines the label indicator line between labels and the pie chart. If this prop is used as a boolean,then the default indicator will be displayed. To customize or pass your own styling `` can be passed to labelIndicator. LabelIndicator is functional only when labelPosition = "centroid". To adjust the labelIndicator length, `labelIndicatorInnerOffset` and `labelIndicatorOuterOffset` props can be used alongside labelIndicator. ```jsx live } /> } labelIndicatorInnerOffset={10} labelIndicatorOuterOffset={5} /> ``` --- ### labelIndicatorInnerOffset The `labelIndicatorInnerOffset` prop defines the offset by which the indicator length inside pie chart is being drawn. Higher the number shorter the length. ```jsx live ``` --- ### labelIndicatorOuterOffset The `labelIndicatorOuterOffset` prop defines the offset by which the indicator length outside the pie chart is being drawn. Higher the number shorter the length. ```jsx live ``` --- ### labelPlacement The `labelPlacement` prop specifies the angular placement of each label relative to the angle of its corresponding slice. This prop should be given as "parallel", "perpendicular", "vertical", or as a function that returns one of these values. When this prop is not given, the label will be placed vertically. ```jsx live `y: ${datum.y}` } labelPosition={({ index }) => index ? "centroid" : "startAngle" } labelPlacement={({ index }) => index ? "parallel" : "vertical" } theme={VictoryTheme.clean} /> ``` --- ### labelPosition The `labelPosition` prop specifies the position of each label relative to its corresponding slice. This prop should be given as "startAngle", "endAngle", "centroid", or as a function that returns one of these values. When this prop is not given, the label will be positioned at the centroid of each slice. ```jsx live datum.y} labelPosition={({ index }) => index ? "centroid" : "startAngle" } theme={VictoryTheme.clean} /> ``` --- ### labelRadius The `labelRadius` prop defines the radius of the arc that will be used for positioning each slice label. If this prop is not set, the label radius will default to the radius of the pie + label padding. If this prop is given as a function, it will be evaluated for each label `VictoryPie` renders, and will be evaluated with the props that correspond to that label, as well as the radius and innerRadius of the corresponding slice. If `labelIndicator` prop is being used, passed `labelRadius`(> radius) is used to calculate the co-ordinates of the outer indicator line. If no specific value for labelRadius is passed , default values will be considered. The outer indicator line length is the difference between `labelRadius` and `labelIndicatorOuterOffset`. ```jsx live innerRadius + 5 } radius={({ datum }) => 50 + datum.y * 20 } innerRadius={50} style={{ labels: { fill: "white", fontSize: 20, fontWeight: "bold", }, }} theme={VictoryTheme.clean} /> ``` --- ### padAngle The `padAngle` prop defines the amount of separation between adjacent data slices in number of degrees. When this prop is given as a function it will be evaluated for each slice, and will be evaluated with the props that correspond to that slice. ```jsx live datum.y} innerRadius={100} data={sampleData} /> ``` --- ### radius The `radius` prop specifies the radius of the pie. When this prop is not given, it will be calculated based on the `width`, `height`, and `padding` props. When this prop is given as a function it will be evaluated for each slice with the props corresponding to that slice. ```jsx live 20 + datum.y * 20 } data={sampleData} theme={VictoryTheme.clean} /> ``` --- ### startAngle The `startAngle` props defines the overall start angle of the pie in degrees. This prop is used in conjunction with `endAngle` to create a pie that spans only a segment of a circle, or to change overall rotation of the pie. This prop should be given as a number of degrees. Degrees are defined as starting at the 12 o'clock position, and proceeding clockwise. ```jsx live
``` --- ### style `VictoryPie` uses the standard `style` prop. [Read about it here](/docs/guides/themes) _default (provided by default theme):_ See [grayscale theme][] for more detail ```jsx live ``` ================================================ FILE: website/docs/api/victory-polar-axis.mdx ================================================ --- title: VictoryPolarAxis --- Renders a circular axis which can be used on its own or composed with [`VictoryChart`](/docs/api/victory-chart). :::info For examples of `VictoryPolarAxis` in action, visit the [Chart Axis](/docs/guides/axis) guide. ::: ## Example ```jsx live ``` ## Inherited Props ## Component Props --- ### axisAngle The `axisAngle` prop is used to position the dependent axis. This prop should be given in degrees. Degrees are defined as starting at the 3 o'clock position, and proceeding counterclockwise. This prop only affects the dependent axis. ```jsx live ``` --- ### circularAxisComponent The `circularAxisComponent` prop takes a component instance which will be responsible for rendering an axis arc for the independent axis. The dependent axis renders an `axisComponent`. The new element created from the passed `circularAxisComponent` will be provided with the following props calculated by `VictoryPolarAxis`: `style`, `events`, `cx`, `cy`, `r`, `startAngle`, and `endAngle`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If a `circularAxisComponent` is not provided, `VictoryPolarAxis` will use its default [Arc component][]. ```jsx circularAxisComponent={} ``` --- ### circularGridComponent The `circularGridComponent` prop takes a component instance which will be responsible for rendering a grid element. The new element created from the passed `circularGridComponent` will be provided with the following props calculated by `VictoryPolarAxis`: `x1`, `y1`, `x2`, `y2`, `tick`, `style` and `events`. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If a `circularGridComponent` is not provided, `VictoryPolarAxis` will use its default [Arc component][]. ```jsx circularGridComponent={} ``` --- ### endAngle The `endAngle` props defines the overall end angle of the axis in degrees. This prop is used in conjunction with `startAngle` to create an axis that spans only a segment of a circle, or to change overall rotation of the axis. This prop should be given as a number of degrees. Degrees are defined as starting at the 3 o'clock position, and proceeding counterclockwise. ```jsx live
``` --- ### events `VictoryPolarAxis` uses the standard `events` prop. See the [Events Guide](/docs/guides/events) for more information on defining events. :::note Valid event targets for `VictoryPolarAxis` are "axis", "axisLabel", "grid", "ticks", and "tickLabels". Targets that correspond to only one element ("axis" and "axisLabel") should use the special eventKey "all". ::: --- ### innerRadius When the `innerRadius` prop is set, polar axes will be hollow rather than circular. ```jsx live ``` --- ### labelPlacement The `labelPlacement` prop specifies how tick labels should be placed relative to the angular tick values. Options for this prop are "vertical", "parallel", and "perpendicular". ```jsx live ``` --- ### startAngle The `startAngle` props defines the overall end angle of the axis in degrees. This prop is used in conjunction with `endAngle` to create an axis that spans only a segment of a circle, or to change overall rotation of the axis. This prop should be given as a number of degrees. Degrees are defined as starting at the 3 o'clock position, and proceeding counterclockwise. ```jsx live
``` --- ### style The `style` prop defines the style of the component. The style prop should be given as an object with styles defined for `parent`, `axis`, `axisLabel`, `grid`, `ticks`, and `tickLabels`. Any valid svg styles are supported, but `width`, `height`, and `padding` should be specified via props as they determine relative layout for components in VictoryChart. Functional styles may be defined for `grid`, `tick`, and `tickLabel` style properties, and they will be evaluated with the props corresponding to each axis element, such as `tick` and `index`. ```jsx live tick > 0.5 ? "red" : "blue", }, tickLabels: { fontSize: 15, padding: 15, }, }} /> ``` ================================================ FILE: website/docs/api/victory-portal.mdx ================================================ --- title: VictoryPortal --- `VictoryPortal` is a wrapper component that renders a child in a top-level [Portal][] element within [VictoryContainer][]. This is useful in instances where elements should always be rendered above others, like tooltips and labels. If a Portal element is not found, `VictoryPortal` will render its child in place. --- ### children `VictoryPortal` takes a single `children`, and renders it in a top level portal element. Any additional props passed to `VictoryPortal` will be spread onto the child. In the following example, the `labelComponent` passed to the first series of bars has been wrapped in `VictoryPortal`. The resulting labels will be rendered in a top-level portal container, and will not be overlapped by subsequent data series, as they otherwise would have. ```jsx live } data={[{x: 1, y: 1}, {x: 2, y: 2}, {x: 3, y: 5}]} /> ``` --- ### groupComponent The `groupComponent` prop takes a component instance which will be used to create a group element for `VictoryPortal` to render its child component into. This prop defaults to a `` tag. [victorycontainer]: /docs/api/victory-container [portal]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-portal/portal.tsx ================================================ FILE: website/docs/api/victory-primitives.mdx ================================================ --- title: VictoryPrimitives --- Victory is built around a set of simple, stateless components. Along with [VictoryContainer][], [VictoryClipContainer][], and [VictoryLabel][], the following list represents every simple element a Victory component might render. These simple components are responsible for supplying props to primitive components. Victory maintains a small subset of primitive components with additional logic in place to prevent unnecessary rendering. Extracting every rendered element into its own component allows Victory to support both web and React Native applications with very little additional code, as only a few components need to be modified to render [react-native-svg][] elements rather than SVG elements. These primitives are also exposed for users to wrap, extend or reference when creating their own custom rendered components. ## Primitive Components Each of these primitive components renders SVG elements. The following components are the only Victory components that render SVG elements with the exception of `VictoryContainer` and `VictoryPortal`. These elements are used by other simple components such as `Bar` and `Area`. ### Border Used by `VictoryLegend` ```jsx const Border = (props) => ; ``` ### Circle Used by `Background`, `VictoryClipContainer`, and `Voronoi` ```jsx const Circle = (props) => ; ``` ### ClipPath Used by `VictoryClipContainer` and `Voronoi` ```jsx const ClipPath = (props) => ( {props.children} ); ``` ### Line Used by `Axis`, `Candle`, and `ErrorBar` ```jsx const Line = (props) => ; ``` ### Path Used by `Arc`, `Area`, `Bar`, `Curve`, `Flyout`, `Point`, `Slice`, and `Voronoi` ```jsx const Path = (props) => ; ``` ### Rect Used by `VictoryClipPath`, `Background`, `Border`, and `Candle` ```jsx const Rect = (props) => ; ``` ### Text Used by `VictoryLabel` ```jsx const Text = (props) => { const { children, title, desc, ...rest } = props; return ( {title && {title}} {desc && {desc}} {children} ); }; ``` ### TSpan Used by `VictoryLabel` ```jsx const TSpan = (props) => ; ``` ## Simple Components ### Arc [VictoryPolarAxis][] uses the `Arc` primitive to draw circular axes and grid lines. `Arc` renders a `` element. [View the source][arc] **Props** - `active` _boolean_ a flag signifying whether the component is active - `ariaLabel` _string or function_ a prop controlling the aria-label that will be applied to the rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Arc` - `className` _string_ the class name that will be applied to the rendered path - `closedPath` _boolean_ a flag signifying whether this arc is should render a closed path - `cx` _number_ the x coordinate of the center of the arc path - `cy` _number_ the y coordinate of the center of the arc path - `datum` _any_ the data point or tick corresponding to this arc - `endAngle` _number_ the end angle of the arc given in degrees - `events` _object_ events to attach to the rendered element - `groupComponent` _element_ the element used to group rendered elements _default_ `` - `id` _string or number_ an id to apply to the rendered component - `pathComponent` _element_ the rendered path element _default_ `` - `r` _number_ the radius of the arc - `role` _string_ the aria role to assign to the element - `scale` _object_ the x and y scale of the parent chart with `domain` and `range` applied - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered path - `startAngle` _number_ the start angle of the arc given in degrees - `style` _object_ the styles to apply to the rendered element - `tabIndex` _number or function_ will be applied to the rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Arc` - `transform` _string_ a transform that will be supplied to elements this component renders ### Area [VictoryArea][] uses `Area` to represent an entire dataset. `Area` renders a `` element, or a group of paths if the stroke of the area should be rendered in a different style from the filled section of the area. [View the source][area] **Props** - `active` _boolean_ a flag signifying whether the component is active - `ariaLabel` _string or function_ a prop controlling the aria-label that will be applied to the rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Area` - `className` _string_ the class name that will be applied to the rendered path - `data` _array_ the entire dataset used to define the area - `events` _object_ events to attach to the rendered element - `groupComponent` _element_ the element used to group rendered elements _default_ `` - `id` _string or number_ an id to apply to the rendered component - `interpolation` _string or function_ the interpolation to use when calculating a path - `origin` _object_ the svg coordinates of the center point of a polar chart - `polar` _boolean_ a flag specifying whether the component is part of a polar chart - `pathComponent` _element_ the rendered path element _default_ `` - `role` _string_ the aria role to assign to the element - `scale` _object_ the x and y scale of the parent chart with `domain` and `range` applied - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered path - `style` _object_ the styles to apply to the rendered element - `tabIndex` _number or function_ that will be applied to rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Area` - `transform` _string_ a transform that will be supplied to elements this component renders ### LineSegment The `LineSegment` component renders straight lines. This component is used to render grids, ticks, and axis lines in [VictoryAxis][]. [View the source][axis] **Props** - `active` _boolean_ a flag signifying whether the component is active - `ariaLabel` _string or function_ a prop controlling the aria-label that will be applied to the rendered lineComponent. When this prop is given as a function it will be called with the rest of the props supplied to `LineSegment` - `className` _string_ the class name that will be applied to the rendered element - `data` _array_ the entire dataset - `datum` _object_ the data point corresponding to this line - `events` _object_ events to attach to the rendered element - `id` _string or number_ an id to apply to the rendered component - `index` _number_ the index of this line within the dataset - `lineComponent` _element_ the rendered line element _default_ `` - `role` _string_ the aria role to assign to the element - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered elements - `style` _object_ the styles to apply to the rendered element - `tabIndex` _number or funciton_ will be applied to the rendered lineComponent. When this prop is given as a function it will be called with the rest of the props supplied to `LineSegment` - `transform` _string_ a transform that will be supplied to elements this component renders - `x1` _number_ the x coordinate of the beginning of the line - `x2` _number_ the x coordinate of the end of the line - `y1` _number_ the y coordinate of the beginning of the line - `y2` _number_ the y coordinate of the end of the line ### Background The `Background` component is used to render an SVG background on VictoryChart. `Background` will render a `` for charts with `polar={true}` and a `` element for all other charts. [View the source][background] **Props** - `className` _string_ the class name that will be applied to the rendered path - `circleComponent` _element_ the rendered circle element _default_ `` - `events` _object_ events to attach to the rendered element - `height` _number_ the height of the `` element - `id` _string or number_ an id to apply to the rendered component - `origin` _object_ the svg coordinates of the center point of a polar chart - `polar` _boolean_ a flag specifying whether the component is part of a polar chart - `rectComponent` _element_ the rendered rect element _default_ `` - `role` _string_ the aria role to assign to the element - `rx` _number_ the x radius of the rendered `` element - `ry` _number_ the y radius of the rendered `` element - `scale` _object_ the x and y scale of the parent chart with `domain` and `range` applied - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered path - `style` _object_ the styles to apply to the rendered element - `width` _number_ the width of the `` element - `x` _number_ the x coordinate of the upper-left corner of the background for non-polar charts and center of the background for polar charts - `y` _number_ the y coordinate of the top of the background ### Bar [VictoryBar][] uses `Bar` to represent a single data point as a bar extending horizontally or vertically from the independent axis. `Bar` renders a `` element. It is also used by [VictoryHistogram][] to represent "bins" of data. [View the source][bar] **Props** - `active` _boolean_ a flag signifying whether the component is active - `alignment` \*"start", "middle", or "end" specifies how a bar path should be aligned in relation to its data point - `ariaLabel` _string or function_ a prop controlling the aria-label that will be applied to the rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Bar` - `barRatio` _number_ a number between zero and one that will be used to calculate bar width when an explicit width is not given - `barWidth` _number or function_ A prop defining the width of the bar. When this prop is given as a function, it will be called with the rest of the props supplied to `Bar`. - `className` _string_ the class name that will be applied to the rendered path - `cornerRadius` _number, function or object_ the number of pixels of corner radius to apply when calculating a bar path. When this prop is given as a function, it will be called with the rest of the props supplied to `Bar`. When given as an object, this prop may include values for top, bottom, topLeft, topRight, bottomLeft and bottomRight, with more specific values overriding less specific values - `data` _array_ the entire dataset - `datum` _object_ the data point corresponding to this bar - `events` _object_ events to attach to the rendered element - `id` _string or number_ an id to apply to the rendered component - `index` _number_ the index of this bar within the dataset - `origin` _object_ the svg coordinates of the center point of a polar chart - `pathComponent` _element_ the rendered path element _default_ `` - `polar` _boolean_ a flag specifying whether the component is part of a polar chart - `role` _string_ the aria role to assign to the element - `scale` _object_ the x and y scale of the parent chart with `domain` and `range` applied - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered path - `style` _object_ the styles to apply to the rendered element - `tabIndex` _number or function_ number applied to rendered path. When given as a function it will be called with the rest of the props supplied to `Bar` - `transform` _string_ a transform that will be supplied to elements this component renders - `width` _number_ the width of parent chart (used to calculate default bar width `style.width` is not supplied) - `x` _number_ the x coordinate of the top of the bar - `y0` _number_ the y coordinate of the baseline of the bar - `y` _number_ the y coordinate of the top of the bar ### Box [VictoryLegend][] uses the `Box` component to draw a border around a legend area. `Box` renders a `` element. [View the source][border] _note_ `Box` also exported as `Border` **Props** - `active` _boolean_ a flag signifying whether the component is active - `ariaLabel` _string or function_ a prop that controls the a propcontrollings the aria-label that will be applied to the rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Box` - `className` _string_ the class name that will be applied to the rendered element - `events` _object_ events to attach to the rendered element - `height` _number_ the height of the `` element - `id` _string or number_ an id to apply to the rendered component - `rectComponent` _element_ the rendered path element _default_ `` - `role` _string_ the aria role to assign to the element - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered element - `style` _object_ the styles to apply to the rendered element - `tabIndex` _number or function_ will be applied to the rendered path. When given as a function it will be called with the rest of the props supplied to `Box` - `transform` _string_ a transform that will be supplied to elements this component renders - `width` _number_ the width of the `` element - `x` _number_ the x coordinate of the upper-left corner of the `` element - `y` _number_ the y coordinate of the upper-left corner of the `` element ### Candle [VictoryCandlestick][] uses `Candle` to represent a single data point as a candle. `Candle` renders a group with `` and `` elements. [View the source][candlestick] **Props** - `active` _boolean_ a flag signifying whether the component is active - `ariaLabel` _string or function_ a prop controlling the aria-label that will be applied to the rendered `` and `` elements. When this prop is given as a function it will be called with the rest of the props supplied to `Candle` - `candleRatio` _number_ a number between zero and one that will be used to calculate candle width when an explicit width is not given - `candleWidth` _number or function_ A prop defining the width of the candle. When this prop is given as a function, it will be called with the rest of the props supplied to `Candle`. - `className` _string_ the class name that will be applied to the rendered element - `close` _number_ the y coordinate of the closing value - `data` _array_ the entire dataset - `datum` _object_ the data point corresponding to this candle - `events` _object_ events to attach to the rendered element - `groupComponent` _element_ the element used to group rendered elements _default_ `` - `high` _number_ the y coordinate of the high value - `id` _string or number_ an id to apply to the rendered component - `index` _number_ the index of this candle within the dataset - `lineComponent` _element_ the rendered line element _default_ `` - `low` _number_ the y coordinate of the low value - `open` _number_ the y coordinate of the opening value - `rectComponent` _element_ the rendered path element _default_ `` - `role` _string_ the aria role to assign to the element - `scale` _object_ the x and y scale of the parent chart with `domain` and `range` applied - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered elements - `style` _object_ the styles to apply to the rendered element - `tabIndex` _number or function_ a prop controlling the aria-label that will be applied to the rendered `` and `` elements. When given as a function it will be called with the rest of the props supplied to `Candle` - `transform` _string_ a transform that will be supplied to elements this component renders - `width` _number_ the width of parent chart (used to calculate default candle width `style.width` is not supplied) - `widthStrokeWidth` _number_ the stroke width of the candle wick. (style.strokeWidth will be used when this value is not given) - `x` _number_ the x coordinate of the candle ### Curve [VictoryLine][] uses `Curve` to represent an entire dataset as a line or curve. `Curve` renders a `` element. [View the source][curve] **Props** - `active` _boolean_ a flag signifying whether the component is active - `ariaLabel` _string or function_ a prop controlling the aria-label that will be applied to the rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Curve` - `className` _string_ the class name that will be applied to the rendered element - `data` _array_ the entire dataset used to define the curve - `events` _object_ events to attach to the rendered element - `groupComponent` _element_ the element used to group rendered elements _default_ `` - `id` _string or number_ an id to apply to the rendered component - `interpolation` _string or function_ the interpolation to use when calculating a path - `origin` _object_ the svg coordinates of the center point of a polar chart - `pathComponent` _element_ the rendered path element _default_ `` - `polar` _boolean_ a flag specifying whether the component is part of a polar chart - `role` _string_ the aria role to assign to the element - `scale` _object_ the x and y scale of the parent chart with `domain` and `range` applied - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered path - `style` _object_ the styles to apply to the rendered element - `tabIndex` _number or function_ will be applied to the rendered path. When given as a function it will be called with the rest of the props supplied to `Curve` - `transform` _string_ a transform that will be supplied to elements this component renders ### ErrorBar [VictoryErrorBar][] uses `ErrorBar` to render x and y error bars. `ErrorBar` renders a group of `` elements. [View the source][errorbar] **Props** - `active` _boolean_ a flag signifying whether the component is active - `ariaLabel` _string or function_ a prop controlling the aria-label that will be applied to the group, `g`, containing the rendered `` elements. When this prop is given as a function it will be called with the rest of the props supplied to `ErrorBar` - `borderWidth` _number_ the width of the cross-hairs on the end of each error bar _default: 10_ - `className` _string_ the class name that will be applied to the rendered element - `data` _array_ the entire dataset - `datum` _object_ the data point corresponding to this error bar - `errorX` _number, array, or boolean_ errors in the x dimension. - `errorY` _number, array, or boolean_ errors in the y dimension. - `events` _object_ events to attach to the rendered element - `groupComponent` _element_ the element used to group rendered elements _default_ `` - `id` _string or number_ an id to apply to the rendered component - `index` _number_ the index of this error bar within the dataset - `lineComponent` _element_ the rendered line element _default_ `` - `origin` _object_ the svg coordinates of the center point of a polar chart - `polar` _boolean_ a flag specifying whether the component is part of a polar chart - `role` _string_ the aria role to assign to the element - `scale` _object_ the x and y scale of the parent chart with `domain` and `range` applied - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered elements - `style` _object_ the styles to apply to the rendered element - `tabIndex` _number or function_ applies to the group, `g`, containing the `` elements. When this prop is given as a function it will be called with the rest of the props supplied to `ErrorBar` - `transform` _string_ a transform that will be supplied to elements this component renders - `x` _number_ the x coordinate of the center of the error bar - `y` _number_ the y coordinate of the center of the error bar ### Flyout [VictoryTooltip][] uses `Flyout` to render a flyout style path around text. `Flyout` renders `` element. [View the source][flyout] **Props** - `active` _boolean_ a flag signifying whether the component is active - `center` _object_ the center coordinates of the flyout - `className` _string_ the class name that will be applied to the rendered element - `cornerRadius` _number_ the corner radius of the flyout - `data` _array_ the entire dataset if applicable - `datum` _object_ the data point corresponding to this flyout if applicable - `dx` _number_ offset in the x dimension. - `dy` _number_ offset in the y dimension. - `events` _object_ events to attach to the rendered element - `height` _number_ the height of the flyout - `id` _string or number_ an id to apply to the rendered component - `index` _number_ the index of this flyout within the dataset - `orientation` _"top", "bottom", "left", "right"_ - `origin` _object_ the svg coordinates of the center point of a polar chart - `pathComponent` _element_ the rendered path element _default_ `` - `pointerLength` _number_ the length of the triangular pointer - `pointerWidth` _number_ the width of the base of the triangular pointer - `polar` _boolean_ a flag specifying whether the component is part of a polar chart - `role` _string_ the aria role to assign to the element - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered elements - `style` _object_ the styles to apply to the rendered element - `transform` _string_ a transform that will be supplied to elements this component renders - `width` _number_ the width of the flyout - `x` _number_ the x coordinate of data point associated with this flyout - `y` _number_ the y coordinate of data point associated with this flyout ### Point [VictoryScatter][] uses `Point` to render each point in a scatter plot. `Point` renders a `` element. [View the source][point] **Props** - `active` _boolean_ a flag signifying whether the component is active - `ariaLabel` _string or function_ a prop controlling the aria-label that will be applied to the rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Point` - `className` _string_ the class name that will be applied to the rendered element - `data` _array_ the entire dataset - `datum` _object_ the data point corresponding to this point - `events` _object_ events to attach to the rendered element - `getPath` _function_ a function of `x`, `y`, and `size` that returns a path string. When this optional function is provided, it will be used to calculate a path, rather than one of the path functions corresponding to the `symbol`s supported by `Point`. - `id` _string or number_ an id to apply to the rendered component - `index` _number_ the index of this point within the dataset - `origin` _object_ the svg coordinates of the center point of a polar chart - `pathComponent` _element_ the rendered path element _default_ `` - `polar` _boolean_ a flag specifying whether the component is part of a polar chart - `role` _string_ the aria role to assign to the element - `scale` _object_ the x and y scale of the parent chart with `domain` and `range` applied - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered path - `size` _number or function_ the size of the point. When this prop is given as a function, it will be called with the rest of the props supplied to `Point`. - `style` _object_ the styles to apply to the rendered element - `symbol` _"circle", "cross", "diamond", "plus", "minus", "square", "star", "triangleDown", "triangleUp"_ which symbol the point should render. This prop may also be given as a function that returns one of the above options. When this prop is given as a function, it will be called with the rest of the props supplied to `Point`. - `tabIndex` _number or function_ number will be applied to the rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Point` - `transform` _string_ a transform that will be supplied to elements this component renders - `x` _number_ the x coordinate of the center of the point - `y` _number_ the y coordinate of the center of the point ### Slice [VictoryPie][] uses `Slice` to render each slice in a pie chart. `Slice` renders a `` element. [View the source][slice] **Props** - `active` _boolean_ a flag signifying whether the component is active - `ariaLabel` _string or function_ a prop controlling the aria-label that will be applied to the rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Slice` - `className` _string_ the class name that will be applied to the rendered element - `cornerRadius` _number or function_ the corner radius to apply to this slice. When this prop is given as a function it will be called with the rest of the props supplied to `Slice`. - `data` _array_ the entire dataset - `datum` _object_ the data point corresponding to this slice - `events` _object_ events to attach to the rendered element - `id` _string or number_ an id to apply to the rendered component - `index` _number_ the index of this slice within the dataset - `innerRadius` _number or function_ the inner radius of the slice. When this prop is given as a function it will be called with `datum` and `active`. - `padAngle` _number or function_ the angular padding to add to the slice. When this prop is given as a function it will be called with the rest of the props supplied to `Slice`. - `pathComponent` _element_ the rendered path element _default_ `` - `pathFunction` _function_ a function that calculates the path of a given slice. When given, this prop will be called with the `slice` object - `radius` _number or function_ the outer radius of the slice. When this prop is given as a function it will be called with the rest of the props supplied to `Slice`. - `role` _string_ the aria role to assign to the element - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered path - `slice` _object_ an object specifying the startAngle, endAngle, padAngle, and data of the slice - `sliceEndAngle` _number or function_ the end angle the slice. When this prop is given as a function it will be called with the rest of the props supplied to `Slice`. - `sliceStartAngle` _number or function_ the start angle of the slice. When this prop is given as a function it will be called with the rest of the props supplied to `Slice`. - `style` _object_ the styles to apply to the rendered element - `tabIndex` _number or function_ number will be applied to the rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Slice`. - `transform` _string_ a transform that will be supplied to elements this component renders ### Voronoi [VictoryVoronoi][] uses `Voronoi` to render voronoi polygons. `Voronoi` renders either a `` element corresponding to a voronoi polygon, or a `` clipped with a `` defined by the path of the polygon. [View the source][voronoi] **Props** - `active` _boolean_ a flag signifying whether the component is active - `ariaLabel` _string or function_ a prop controlling the aria-label that will be applied to the rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Voronoi` - `circleComponent` _element_ the rendered circle element _default_ `` - `className` _string_ the class name that will be applied to the rendered element - `clipPathComponent` _element_ the rendered clipPath element _default_ `` - `data` _array_ the entire dataset - `datum` _object_ the data point corresponding to this voronoi polygon - `events` _object_ events to attach to the rendered element - `groupComponent` _element_ the rendered group element _default_ `` - `id` _string or number_ an id to apply to the rendered component - `index` _number_ the index of this voronoi polygon within the dataset - `origin` _object_ the svg coordinates of the center point of a polar chart - `pathComponent` _element_ the rendered path element _default_ `` - `polar` _boolean_ a flag specifying whether the component is part of a polar chart - `polygon` _array_ an array of points defining the polygon - `role` _string_ the aria role to assign to the element - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered path - `size` _number_ the maximum size of the voronoi polygon - `style` _object_ the styles to apply to the rendered element - `tabIndex` _number or function_ will be applied to the rendered path. When this prop is given as a function it will be called with the rest of the props supplied to `Voronoi` - `transform` _string_ a transform that will be supplied to elements this component renders. - `x` _number_ the x coordinate of the data point - `y` _number_ the y coordinate of the data point ### Whisker [VictoryBoxPlot][] uses the `Whisker` component to draw whiskers for the minimum and maximum values in a box plot. `Whisker` renders a group of `` elements. [View the source][whisker] **Props** - `active` _boolean_ a flag signifying whether the component is active - `ariaLabel` _string or function_ a prop controlling the aria-label that will be applied to the rendered `` elements. When this prop is given as a function it will be called with the rest of the props supplied to `Whisker` - `className` _string_ the class name that will be applied to the rendered element - `events` _object_ events to attach to the rendered element - `groupComponent` _element_ the rendered group element _default_ `` - `id` _string or number_ an id to apply to the rendered component - `lineComponent` _element_ the rendered line element _default_ `` - `majorWhisker` _object_ an object with values `x1`, `x2`, `y1`, `y2` describing the major whisker line - `minorWhisker` _object_ an object with values `x1`, `x2`, `y1`, `y2` describing the minor whisker line - `role` _string_ the aria role to assign to the element - `shapeRendering` _string_ the shape rendering attribute to apply to the rendered element - `style` _object_ the styles to apply to the rendered element - `tabIndex` _number or function_ will be applied to the rendered ``. When this prop is given as a function it will be called with the rest of the props supplied to `Whisker` - `transform` _string_ a transform that will be supplied to elements this component renders. [victorycontainer]: /docs/api/victory-container [victoryclipcontainer]: /docs/api/victory-clip-container [victorylabel]: /docs/api/victory-label [react-native-svg]: https://github.com/react-native-community/react-native-svg [arc]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-primitives/arc.tsx [victorypolaraxis]: /docs/api/victory-polar-axis [area]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-area/src/area.tsx [victoryarea]: /docs/api/victory-area [background]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-primitives/background.tsx [bar]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-bar/src/bar.tsx [border]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-primitives/border.tsx [victorybar]: /docs/api/victory-bar [candlestick]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-candlestick/src/victory-candlestick.tsx [victorycandlestick]: /docs/api/victory-candlestick [curve]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-line/src/curve.tsx [victoryline]: /docs/api/victory-line [errorbar]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-errorbar/src/error-bar.tsx [victoryerrorbar]: /docs/api/victory-error-bar [flyout]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-tooltip/src/flyout.tsx [victorytooltip]: /docs/api/victory-tooltip [victoryaxis]: /docs/api/victory-axis [axis]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-axis/src/victory-axis.tsx [point]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-primitives/point.tsx [slice]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-pie/src/slice.tsx [whisker]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-primitives/whisker.tsx [victorypie]: /docs/api/victory-pie [voronoi]: https://github.com/FormidableLabs/victory/blob/main/packages/victory-voronoi/src/voronoi.tsx [victoryvoronoi]: /docs/api/victory-voronoi [victoryscatter]: /docs/api/victory-scatter [victorylegend]: /docs/api/victory-legend [victoryboxplot]: /docs/api/victory-boxplot [victoryhistogram]: /docs/api/victory-histogram ================================================ FILE: website/docs/api/victory-scatter.mdx ================================================ --- title: VictoryScatter --- :::info For examples of `VictoryScatter` in action, visit the [Scatter Chart](/docs/charts/scatter) examples. ::: ## Inherited Props ## Component Props --- ### bubbleProperty The `bubbleProperty` prop indicates which property of the data object should be used to scale data points in a bubble chart. If a `bubbleProperty` is given, `size` and `symbol` props will be ignored. Bubble charts always render circular points. ```jsx live ``` --- ### data `VictoryScatter` uses the standard `data` prop. However, it also will preferentially use `symbol`, `size`, and `label` properties supplied via data objects. --- ### eventKey `VictoryScatter` uses the standard `eventKey` prop to specify how event targets are addressed. **This prop is not commonly used.** [Read about the `eventKey` prop in more detail here](/docs/guides/events) --- ### events `VictoryScatter` uses the standard `events` prop. [Read about it in more detail here](/docs/guides/events) See the [Events Guide][] for more information on defining events. ```jsx live

Click a data point below

null} events={[ { target: "data", eventHandlers: { onClick: () => { return [ { target: "data", mutation: (props) => { const fill = props.style && props.style.fill; return fill === "black" ? null : { style: { fill: "black", }, }; }, }, { target: "labels", mutation: (props) => { return props.text === "clicked" ? null : { text: "clicked", }; }, }, ]; }, }, }, ]} data={sampleData} />
``` --- ### maxBubbleSize The `maxBubbleSize` prop sets an upper limit for scaling data points in a bubble chart. If not given, this prop will be calculated based on the `width`, `height`, and `padding` of the component. ```jsx maxBubbleSize={25} ``` --- ### minBubbleSize The `minBubbleSize` prop sets a lower limit for scaling data points in a bubble chart. If not given, this prop will be calculated based on the calculated `maxBubbleSize`. ```jsx minBubbleSize={5} ``` --- ### size The `size` prop determines how to scale each data point. When this prop given as a function, it will be called for each point with the props corresponding to that point. If `size` is not specified, either in props or in a theme, it will default to 1. `size` may also be set directly on each data object. ```jsx live datum.y + 2} data={sampleData} /> ``` --- ### style `VictoryScatter` uses the standard `style` prop. [Read about it in detail here](/docs/guides/themes) _default (provided by default theme):_ See [grayscale theme][] for more detail ```jsx live datum.x === 3 ? "#000000" : "#c43a31", stroke: ({ datum }) => datum.x === 3 ? "#000000" : "#c43a31", fillOpacity: 0.7, strokeWidth: 3, }, labels: { fontSize: 15, fill: ({ datum }) => datum.x === 3 ? "#000000" : "#c43a31", }, }} size={9} data={sampleData} labels={({ datum }) => datum.x} /> ``` --- ### symbol The `symbol` prop determines which symbol should be drawn to represent data points. Options are: "circle", "cross", "diamond", "plus", "minus", "square", "star", "triangleDown", "triangleUp". When this prop is given as a function, it will be evaluated for each point with the props corresponding to that point. If no `symbol` prop is specified, a circle will be rendered. `symbol` may also be set directly on each data object. ```jsx live datum.y > 3 ? "triangleUp" : "triangleDown" } size={7} data={sampleData} /> ``` ================================================ FILE: website/docs/api/victory-selection-container.mdx ================================================ --- title: VictorySelectionContainer --- Enables selecting data points within a highlighted region. :::info For examples of `VictorySelectionContainer` in action, visit the [containers](/docs/guides/containers) guide. ::: ## Inherited Props ## Component Props --- ### activateSelectedData When the `activateSelectedData` prop is set to true, the `active` prop will be set to true on all selected data points. When this prop is set to false, the `onSelection` and `onSelectionCleared` callbacks will still fire, but no mutations will occur via Victory's event system. --- ### disable When the `disable` prop is set to `true`, `VictorySelectionContainer` events will not fire. --- ### onSelection The `onSelection` prop accepts a function to be called whenever new data points are selected. The function is called with the parameters `points` (an array of objects with `childName`, `eventKey`, and `data`), `bounds` (an object with min / max arrays specified for `x` and `y`), and `props` (the props used by `VictorySelectionContainer`) _example:_ `onSelection={(points, bounds, props) => handleSelection(points, bounds, props)}` --- ### onSelectionCleared The `onSelectionCleared` prop accepts a function to be called whenever the selection is cleared. The function is called with the props used by `VictorySelectionContainer` _example:_ `onSelectionCleared={(props) => handleSelectionCleared(props)}` ### selectionBlacklist `type: array[string]` The `selectionBlacklist` prop is used to exclude data from potential selections. This prop should be given as an array of strings that match the `name` prop of Victory component that should be excluded from selection. _example:_ `selectionBlackList={["first-line", "second-line"]}` --- ### selectionComponent The `selectionComponent` prop specifies the element to be rendered for the selected area. When this prop is not specified, a `` will be rendered. This component will be supplied with the following props: `x`, `y`, `width`, `height`, and `style`. --- ### selectionDimension When the `selectionDimension` prop is set, the selection will only take the given dimension into account. For example, when `dimension` is set to "x", the selected area will cover the entire y domain regardless of mouse position. _example:_ `selectionDimension="x"` ```jsx live } theme={VictoryTheme.clean} > active ? "tomato" : "gray", }, }} /> ``` --- ### selectionStyle The `selectionStyle` prop should be given as an object of style attributes to be applied to the `selectionComponent` ```jsx live } theme={VictoryTheme.clean} > active ? "tomato" : "gray", }, }} /> ``` ================================================ FILE: website/docs/api/victory-shared-events.mdx ================================================ --- title: VictorySharedEvents --- The `VictorySharedEvents` wrapper coordinates events between its child components. Specify a set of events on the `VictorySharedEvents` wrapper to target children. [VictoryChart](/docs/api/victory-chart), [VictoryGroup](/docs/api/victory-group), and [VictoryStack](/docs/api/victory-stack) all use `VictorySharedEvents`, but it may also be used on its own. :::info For examples of using events in Victory, visit the [Chart Events](/docs/guides/events) guide. ::: ## Component Props :::caution This API is largely undocumented and is primarily used internally by Victory components. It is not recommended for general use. ::: --- ### children `VictorySharedEvents` renders an array of children with new `sharedEvents` props which define a set of events, and a shared state accessor. --- ### events The `events` prop takes an array of event objects. Event objects are composed of a `target`, an `eventKey`, a `childName` and `eventHandlers`. Targets may be any valid style namespace for a given component, so "data" and "labels" are valid targets for this components like `VictoryBar`. `eventKey` may be given as a single value, or as an array of values to specify individual targets. If `eventKey` is not specified, the given `eventHandlers` will be attached to all elements of the specified `target` type. The `childName` property may be given as a string or an array of strings to target multiple children. The `eventHandlers` object should be given as an object whose keys are standard event names (i.e. `onClick`) and whose values are event callbacks. The return value of an event handler is used to modify elements. The return value should be given as an object or an array of objects with optional `target`, `childName` and `eventKey` keys for specifying the element(s) to be modified, and a `mutation` key whose value is a function. The `target` and `eventKey` keys will default to those corresponding to the element the event handler was attached to. The `mutation` function will be called with the calculated props for each element that should be modified (i.e. a bar label), and the object returned from the mutation function will override the props of that element via object assignment. ```jsx live { return [ { childName: [ "pie", "bar", ], mutation: (props) => { return { style: Object.assign( {}, props.style, { fill: "tomato", }, ), }; }, }, ]; }, onMouseOut: () => { return [ { childName: [ "pie", "bar", ], mutation: () => { return null; }, }, ]; }, }, }, ]} > } theme={VictoryTheme.clean} /> ``` --- ### eventKey The `eventKey` prop is used to assign eventKeys to data. This prop operates identically to the `x` and `y` data accessor props. By default, the eventKey of each datum will be equal to its index in the data array. `eventKey` may also be defined directly on each data object. --- ### externalEventMutations Occasionally is it necessary to trigger events in Victory's event system from some external element such as a button or a form field. Use the `externalEventMutation` prop to specify a set of mutations to apply to a given chart. The `externalEventMutations` should be given in the following form: The `target`, `eventKey`, and `childName` (when applicable) must always be specified. The `mutation` function will be called with the current props of the element specified by the `target`, `eventKey` and `childName` provided. The mutation function should return a mutation object for that element. The `callback` prop should be used to clear the `externalEventMutations` prop once the mutation has been applied. Clearing `externalEventMutations` is crucial for charts that animate. ```jsx live noInline function App() { const [state, setState] = React.useState({ externalMutations: undefined, }); function removeMutation() { setState({ externalMutations: undefined, }); } function clearClicks() { setState({ externalMutations: [ { childName: ["bar", "pie"], target: ["data"], eventKey: "all", mutation: () => ({ style: undefined, }), callback: removeMutation, }, ], }); } const buttonStyle = { backgroundColor: "black", color: "white", padding: "10px", marginTop: "10px", }; return (
{ return [ { childName: [ "pie", "bar", ], mutation: ( props, ) => { return { style: Object.assign( {}, props.style, { fill: "tomato", }, ), }; }, }, ]; }, }, }, ]} > } theme={VictoryTheme.clean} />
); } render(); ``` :::note External mutations are applied to the same state object that is used to control events in Victory, so depending on the order in which they are triggered, external event mutations may override mutations caused by internal Victory events or vice versa. ::: ================================================ FILE: website/docs/api/victory-single-labelable-props.mdx ================================================ --- title: VictorySingleLabelableProps --- A set of props available to components that can display multiple labels. ## Inherited Props ## Props --- ### label The `label` prop defines the label that will appear with the axis. ================================================ FILE: website/docs/api/victory-stack.mdx ================================================ --- title: VictoryStack --- `VictoryStack` is a wrapper component that renders a given set of children in a stacked layout. Like other wrapper components, `VictoryStack` also reconciles the domain and layout for all its children, and coordinates animations and shared events. ## Supported Components - [VictoryArea](/docs/api/victory-area) - [VictoryBar](/docs/api/victory-bar) - [VictoryCandlestick](/docs/api/victory-candlestick) - [VictoryErrorBar](/docs/api/victory-error-bar) - [VictoryGroup](/docs/api/victory-group) - [VictoryLine](/docs/api/victory-line) - [VictoryScatter](/docs/api/victory-scatter) - [VictoryHistogram](/docs/api/victory-histogram) - Only with other `VictoryHistogram` components ## Unsupported Components - [VictoryVoronoi](/docs/api/victory-axis) - [VictoryAxis](/docs/api/victory-axis) ## Example ```jsx live ``` ## Inherited Props ## Component Props --- ### categories `VictoryStack` uses the standard `categories` prop. [Read about it here](/docs/api/victory-datatable-props#categories) _note:_ When this prop is set, `VictoryGroup` controls the `categories` prop of its children. ```jsx categories={["dogs", "cats", "mice"]} ``` --- ### children Children supplied to `VictoryGroup` will be cloned and rendered with new props so that all children share common props such as `domain` and `scale`. --- ### eventKey `VictoryStack` uses the standard `eventKey` prop to specify how event targets are addressed. **This prop is not commonly used.** [Read about the `eventKey` prop in more detail here](/docs/guides/events) --- ### events `VictoryStack` uses the standard `events` prop. [Read about it in more detail here](/docs/guides/events) See the [Events Guide][] for more information on defining events. _note:_ `VictoryStack` coordinates events between children using the `VictorySharedEvents` and the `sharedEvents` prop ```jsx live { return [ { childName: "area-2", target: "data", mutation: (props) => ({ style: Object.assign( {}, props.style, { fill: "gold" }, ), }), }, { childName: "area-3", target: "data", mutation: (props) => ({ style: Object.assign( {}, props.style, { fill: "orange" }, ), }), }, { childName: "area-4", target: "data", mutation: (props) => ({ style: Object.assign( {}, props.style, { fill: "red" }, ), }), }, ]; }, }, }, ]} > ``` --- ### style `VictoryStack` uses the standard `style` prop. [Read about it here](/docs/guides/themes) Styles on children of `VictoryGroup` will override styles set on the `VictoryGroup` component. ```jsx live ``` --- ### xOffset The `xOffset` prop is used for grouping stacks of bars. This prop will be set by the `VictoryGroup` component wrapper, or can be set manually. ================================================ FILE: website/docs/api/victory-style-interface.mdx ================================================ --- title: VictoryStyleInterface --- Defines the style of the component using valid svg styles. However, `width`, `height`, and `padding` should be specified via props as they determine relative layout for components in [VictoryChart](/docs/api/victory-chart). :::info For more information about themes and styles, see the [themes guide](/docs/guides/themes). ::: ## Example Style attributes can be defined as an object of `CSSProperties`. ```jsx style={{ data: { fill: "tomato", opacity: 0.7 }, labels: { fontSize: 12 }, parent: { border: "1px solid #ccc" } }} ``` Or they may be defined as a function of the props for whatever element it applies to. ```jsx style={{ data: { fill: ({ datum }) => datum.y > 0 ? "green" : "red" }, labels: { fontSize: ({ text }) => text.length > 10 ? 8 : 12 }, parent: { border: "1px solid #ccc" } }} ``` ```jsx live datum.x} /> ``` ## Type Signature ```ts type StringOrNumberOrCallback = | string | number | ((props: any) => string | number); type VictoryStyleObject = { [K in keyof React.CSSProperties]: StringOrNumberOrCallback; }; type LabelProps = React.CSSProperties & { angle?: number; verticalAnchor?: VerticalAnchorType; }; type VictoryLabelStyleObject = { [K in keyof LabelProps]: StringOrNumberOrCallback; }; interface VictoryStyleInterface { parent?: VictoryStyleObject; data?: VictoryStyleObject; labels?: | VictoryLabelStyleObject | VictoryLabelStyleObject[]; border?: VictoryStyleObject; } ``` ## Caveats :::note When a component is rendered as a child of another Victory component, or within a custom `` element with `standalone={false}`, parent styles will be applied to the enclosing `` tag. Many styles that can be applied to a parent `` will not be expressed when applied to a ``. ::: :::note The `style` prop used by `VictoryAxis` has a different format than the standard `style` prop. ::: :::note custom `angle` and `verticalAnchor` properties may be included in labels styles. ::: ================================================ FILE: website/docs/api/victory-theme.mdx ================================================ --- title: VictoryTheme --- Allows you to create a consistent look across all of your chart elements, either by using one of the included themes or by creating your own. By default, Victory components use the `grayscale` theme. Victory includes several [prebuilt](https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-theme) themes that can be used. :::info For more information about themes and styles, see the [themes guide](/docs/guides/themes). ::: ## Types This is roughly what the `VictoryTheme` object looks like, see the [source code](https://github.com/FormidableLabs/victory/blob/main/packages/victory-core/src/victory-theme/types.ts#L90) for the full definition. :::note The `dependentAxis` and `independentAxis`, `polarDependentAxis`, and `polarIndependentAxis` will be merged with any props and styles supplied in the `axis` namespace. ::: ```ts interface VictoryThemeDefinition { palette: {...props}; area: {...props} axis: {...props}, dependentAxis: {...props}, independentAxis: {...props}, polarDependentAxis: {...props}, polarIndependentAxis: {...props}, bar: {...props}, candlestick: {...props}, chart: {...props}, errorbar: {...props}, histogram: {...props}, group: {...props}, legend: {...props}, line: {...props}, pie: {...props}, scatter: {...props}, stack: {...props}, tooltip: {...props}, voronoi: {...props} } ``` ### `palette` The `palette` property defines a collection of colors that can be used across all chart elements. Each palette contains predefined color arrays that you can use directly or customize as needed. #### Properties | Property | Type | Description | | ------------- | ---------- | --------------------------------------------------------------------------------------------- | | `grayscale` | `string[]` | Shades of gray, ideal for minimalist or monochrome chart designs | | `qualitative` | `string[]` | Used for categorical data, containing distinct colors that are easy to differentiate visually | | `heatmap` | `string[]` | A gradient-based color scheme often used for heatmaps or data density visualizations | | `warm` | `string[]` | Warm colors like reds, oranges, and yellows | | `cool` | `string[]` | Cool colors such as blues, purples, and greens | | `red` | `string[]` | Various shades of red | | `blue` | `string[]` | Various shades of blue | | `green` | `string[]` | Various shades of green | ## Example ```jsx live ``` ================================================ FILE: website/docs/api/victory-tooltip.mdx ================================================ --- title: VictoryTooltip --- `VictoryTooltip` renders a tooltip component with a set of default events. When `VictoryTooltip` is used as a label component for any Victory component that renders data, it will attach events to rendered data components that will activate the tooltip when hovered or focused. `VictoryTooltip` renders text as well as a configurable [Flyout](/docs/api/victory-primitives#flyout) container. :::info For examples of `VictoryTooltip` in action, visit the [tooltips](/docs/guides/tooltips) guide. ::: :::note When providing tooltips for `VictoryLine` or `VictoryArea`, it is necessary to use [`VictoryVoronoiContainer`](/docs/api/victory-voronoi-container), as these components only render a single element for the entire dataset. ::: ```jsx live } data={[ { x: 2, y: 5, label: "right-side-up", }, { x: 4, y: -6, label: "upside-down", }, { x: 6, y: 4, label: "tiny" }, { x: 8, y: -5, label: "or a little \n BIGGER", }, { x: 10, y: 7, label: "automatically", }, ]} /> ``` ## Inherited Props ## Component Props --- ### active The `active` prop specifies whether the tooltip component should be displayed. --- ### activateData When true, tooltip events will set the `active` prop on both data and label elements. --- ### angle The `angle` prop specifies the angle to rotate the tooltip around its origin point. --- ### center The `center` prop determines the position of the center of the tooltip flyout. This prop should be given as an object that describes the desired x and y svg coordinates of the center of the tooltip. This prop is useful for positioning the flyout of a tooltip _independent from_ the pointer. When `VictoryTooltip` is used with `VictoryVoronoiContainer`, the `center` prop is what enables the `mouseFollowTooltips` option. When this prop is set, non-zero `pointerLength` values will no longer be respected. ```jsx live "HELLO"} labelComponent={ } /> ``` --- ### centerOffset The `centerOffset` prop determines the position of the center of the tooltip flyout _in relation to_ the flyout pointer. This prop should be given as an object of x and y, where each is either a numeric offset value or a function that returns a numeric value. When this prop is set, non-zero `pointerLength` values will no longer be respected. ```jsx live `x: ${datum.x}, y: ${datum.y}` } labelComponent={ } /> ``` --- ### constrainToVisibleArea The `constrainToVisibleArea` prop determines whether to coerce tooltips so that they fit within the visible area of the chart. When this prop is set to true, tooltip pointers will still point to the correct data point, but the center of the tooltip will be shifted to fit within the overall width and height of the svg Victory renders. ```jsx live "These labels just go on, and on, and on..." } labelComponent={ } /> ``` --- ### cornerRadius The `cornerRadius` prop determines the corner radius of the flyout container. This prop may be given as a positive number or a function of datum. ```jsx live `y: ${datum.y}` } labelComponent={ datum.x * 2 } /> } /> ``` --- ### data Victory components can pass a `data` prop to their label component. This can be useful in custom components that need to make use of the entire dataset. --- ### datum Victory components can pass a `datum` prop to their label component. This can be used to calculate functional styles, and determine text. --- ### dx The `dx` prop defines a horizontal shift from the `x` coordinate. --- ### dy The `dy` prop defines a vertical shift from the `y` coordinate. --- ### events The `events` prop attaches arbitrary event handlers to the label component. This prop should be given as an object of event names and corresponding event handlers. When events are provided via Victory's event system, event handlers will be called with the event, the props of the component it is attached to, and an eventKey. _examples:_ `events={{onClick: (evt) => alert("x: " + evt.clientX)}}` --- ### flyoutComponent The `flyoutComponent` prop takes a component instance which will be used to create the flyout path for each tooltip. The new element created from the passed `flyoutComponent` will be supplied with the following properties: x, y, dx, dy, index, datum, cornerRadius, pointerLength, pointerWidth, width, height, orientation, style, and events. Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within the custom component itself. If `flyoutComponent` is omitted, a default [Flyout][] component will be created with props described above. _examples:_ `flyoutComponent={}`, `flyoutComponent={}` --- ### flyoutHeight The `flyoutHeight` prop defines the height of the tooltip flyout. This prop may be given as a positive number or a function of datum. If this prop is not set, `flyoutHeight` will be determined based on an [approximate text size][] calculated from the `text` and `style` props provided to `VictoryTooltip`. ```jsx live `y: ${datum.y}` } labelComponent={ } /> ``` --- ### flyoutPadding The `flyoutPadding` prop may be used to adjust the spacing between the tooltip label and the edges of the flyout outline. This prop may be given as a single number, an object with values for "top", "bottom", "left" and "right", or as a function that returns one of these. ```jsx live datum.x % 2 === 0 ? [ `x: ${datum.x}`, `y: ${datum.y}`, ] : [`y: ${datum.y}`] } labelComponent={ text.length > 1 ? { top: 0, bottom: 0, left: 7, right: 7, } : 7 } /> } /> ``` --- ### flyoutStyle The `style` prop applies SVG style properties to the rendered flyout container. These props will be passed to the `flyoutComponent`. ```jsx live `y: ${datum.y}` } labelComponent={ } /> ``` --- ### flyoutWidth The `flyoutWidth` prop defines the width of the tooltip flyout. This prop may be given as a positive number or a function of datum. If this prop is not set, `flyoutWidth` will be determined based on an [approximate text size][] calculated from the `text` and `style` props provided to `VictoryTooltip`. ```jsx live `y: ${datum.y}` } labelComponent={ } /> ``` --- ### groupComponent The `groupComponent` prop takes a component instance which will be used to create group elements for use within container elements. This prop defaults to a `` tag. --- ### height This prop refers to the height of the `svg` that `VictoryLabel` is rendered within. **This prop is passed from parents of `VictoryLabel`, and should not be set manually. In versions before `^33.0.0` this prop referred to the height of the tooltip flyout. Please use `flyoutHeight` instead** --- ### horizontal The `horizontal` prop determines whether to plot the flyouts to the left / right of the (x, y) coordinate rather than top / bottom. This is useful when an orientation prop is not provided, and data will determine the default orientation. _i.e._ negative values result in a left orientation and positive values will result in a right orientation by default. --- ### index The `index` prop represents the index of the datum in the data array. --- ### pointerOrientation This prop determines which side of the tooltip flyout the pointer should originate on. When this prop is not set, it will be determined based on the overall `orientation` of the flyout in relation to its data point, and any `center` or `centerOffset` values. ```jsx live datum.y} labelComponent={ } /> ``` --- ### pointerWidth The `pointerWidth` prop determines the width of the base of the triangular pointer extending from the flyout. This prop may be given as a positive number or a function of datum. ```jsx live `y: ${datum.y}` } labelComponent={ } /> ``` --- ### renderInPortal When `renderInPortal` is true, rendered tooltips will be wrapped in [VictoryPortal][] and rendered within the [Portal][] element within [VictoryContainer][]. _Note:_ This prop should be set to _false_ when using a custom container element. --- ### style The `style` prop applies SVG style properties to the rendered `` element. ```jsx live `y: ${datum.y}` } labelComponent={ } /> ``` --- ### text The `text` prop defines the text `VictoryTooltip` will render. The `text` prop may be given as a string, number, or function of `datum`. When [VictoryLabel][] is used as the `labelComponent`, strings may include newline characters, which VictoryLabel will split in to separate `` elements. --- ### width This prop refers to the width of the `svg` that `VictoryLabel` is rendered within. **This prop is passed from parents of `VictoryLabel`, and should not be set manually. In versions before `^33.0.0` this prop referred to the width of the tooltip flyout. Please use `flyoutWidth` instead** --- ### x The `x` prop defines the x coordinate to use as a basis for positioning the tooltip element. --- ### y The `y` prop defines the y coordinate to use as a basis for positioning the tooltip element. ================================================ FILE: website/docs/api/victory-transition.mdx ================================================ --- title: VictoryTransition --- `VictoryTransition` wraps a single child component in `VictoryAnimation`. In addition to animating the child component, it will also handle transitions for any entering and exiting nodes via the `onEnter` and `onExit` and `onLoad` transitions defined on its `animate` prop. :::info This component is not typically used. Visit the [Animation](/docs/guides/animations) guide for examples of how to add animation to charts. ::: ## Component Props --- ### children `VictoryTransition` adds transitions to a single child. --- ### animate The `animate` prop specifies props for VictoryAnimation and VictoryTransition to use. The animate prop may be used to specify the duration, delay and easing of an animation as well as the behavior of `onEnter` and `onExit` and `onLoad` transitions. Each Victory component defines its own default transitions, but these may be modified, or overwritten with the `animate` prop. _examples:_ `animate={{duration: 2000, onLoad: {duration: 1000}, onEnter: {duration: 500, before: () => ({y: 0})}}}` --- ### animationWhitelist The `animationWhitelist` defines a list of props to animate on the child. This prop should be given as an array of strings. ================================================ FILE: website/docs/api/victory-voronoi-container.mdx ================================================ --- title: VictoryVoronoiContainer --- Adds the ability to associate a mouse position with the data point(s) closest to it. :::info For examples of `VictoryVoronoiContainer` in action, visit the [containers](/docs/guides/containers) guide. ::: ## Inherited Props ## Component Props --- ### activateData When the `activateData` prop is set to true, the `active` prop will be set to true on all data components within a voronoi area. When this prop is set to false, the `onActivated` and `onDeactivated` callbacks will still fire, but no mutations to data components will occur via Victory's event system. --- ### activateLabels When the `activateLabels` prop is set to true, the `active` prop will be set to true on all labels corresponding to points within a voronoi area. When this prop is set to false, the `onActivated` and `onDeactivated` callbacks will still fire, but no mutations to label components will occur via Victory's event system. Labels defined directly on `VictoryVoronoiContainer` via the `labels` prop will still appear when this prop is set to false. --- ### disable When the `disable` prop is set to `true`, `VictoryVoronoiContainer` events will not fire. --- ### labels When a `labels` prop is provided to `VictoryVoronoiContainer` it will render a label component rather than activating labels on the child components it renders. This is useful for creating multi- point tooltips. This prop should be given as a function which will be called once for each active point. The `labels` function will be called with the props that correspond to the active label. _example:_ `labels={({ datum }) => "y: " + datum.y}` --- ### labelComponent The `labelComponent` prop specified the component that will be rendered when `labels` are defined on `VictoryVoronoiContainer`. If the `labels` prop is omitted, no label component will be rendered. ```jsx live "Long, verbose labels" } labelComponent={ } /> } theme={VictoryTheme.clean} > ``` --- ### mouseFollowTooltips When the `mouseFollowTooltip` prop is set on `VictoryVoronoiContainer`, The position of the center of the tooltip follows the position of the mouse. ```jsx live `y: ${datum.y}` } /> } theme={VictoryTheme.clean} > ``` --- ### onActivated The `onActivated` prop accepts a function to be called whenever new data points are activated. The function is called with the parameters `points` (an array of active data objects) and `props` (the props used by `VictoryVoronoiContainer`). _example:_ `onActivated={(points, props) => filterList(points, props)}` --- ### onDeactivated The `onDeactivated` prop accepts a function to be called whenever points are deactivated. The function is called with the parameters `points` (an array of the newly-deactivated data objects) and `props` (the props used by `VictoryVoronoiContainer`). _example:_ `onDeactivated={(points, props) => removeFromList(points, props)}` --- ### radius When the `radius` prop is set, the voronoi areas associated with each data point will be no larger than the given radius. This prop should be given as a number. --- ### voronoiBlacklist The `voronoiBlacklist` prop is used to specify a list of components to ignore when calculating a shared voronoi diagram. Components with a `name` prop matching an element in the `voronoiBlacklist` array will be ignored by `VictoryVoronoiContainer`. Ignored components will never be flagged as active, and will not contribute data to shared tooltips or labels. _example:_ `voronoiBlacklist={["redPoints"]}` ```jsx live `y: ${datum.y}` } /> } theme={VictoryTheme.clean} > ``` --- ### voronoiDimension When the `voronoiDimension` prop is set, voronoi selection will only take the given dimension into account. For example, when `dimension` is set to "x", all data points matching a particular x mouse position will be activated regardless of y value. When this prop is not given, voronoi selection is determined by both x and y values. _example:_ `voronoiDimension="x"` ```jsx live `y: ${datum.y}` } /> } theme={VictoryTheme.clean} > ``` --- ### voronoiPadding When the `voronoiPadding` prop is given, the area of the chart that will trigger voronoi events is reduced by the given padding on every side. By default, no padding is applied, and the entire range of a given chart may trigger voronoi events. This prop should be given as a number. ================================================ FILE: website/docs/api/victory-voronoi.mdx ================================================ --- title: VictoryVoronoi --- :::info For examples of `VictoryVoronoi` in action, visit the [Scatter Chart](/docs/charts/voronoi) examples. ::: ## Inherited Props ## Component Props --- ### events `VictoryVoronoi` uses the standard `events` prop. [Read about it in more detail here](/docs/guides/events) See the [Events Guide][] for more information on defining events. ```jsx live

Click a cell below

{ return [ { target: "data", mutation: (props) => { const fill = props.style && props.style.fill; return fill === "black" ? null : { style: { fill: "black", }, }; }, }, ]; }, }, }, ]} data={sampleData} theme={VictoryTheme.clean} />
``` --- ### size The size prop determines the maximum size of each voronoi area. When this prop is given, a circular area of the specified size will be rendered, and clipped where it would overlap with other voronoi areas. If this prop is not given, the entire voronoi area will be used. --- ### style `VictoryVoronoi` uses the standard `style` prop. [Read about it in detail here](/docs/guides/themes) ```jsx live datum.x} /> ``` ================================================ FILE: website/docs/api/victory-zoom-container.mdx ================================================ --- title: VictoryZoomContainer --- Provides pan and zoom behavior for any Victory component that works with an x, y axis, :::info For examples of `VictoryZoomContainer` in action, visit the [containers](/docs/guides/containers) guide. ::: ## Inherited Props ## Component Props --- ### allowPan The optional `allowPan` prop accepts a boolean that enables the panning functionality. Zooming will still be enabled when the `allowPan` prop is set to false. --- ### allowZoom The optional `allowZoom` prop accepts a boolean that enables the zoom functionality. Panning will still be enabled when the `allowZoom` prop is set to false. --- ### clipContainerComponent `VictoryZoomContainer` works by clipping data outside of a given domain. `VictoryZoomContainer` uses `VictoryClipContainer` by default. This prop should not be replaced with a custom component, but you may want to set props on `VictoryClipContainer`, such as `clipPadding` _example:_ `clipContainerComponent={}` --- ### disable When the `disable` prop is set to `true`, `VictoryZoomContainer` events will not fire. --- ### downsample The `downsample` prop limits the number of points that will be displayed. This prop may be given as a boolean or a number corresponding to the maximum number of points. When given as a boolean, the maximum number of points that will be plotted is 150. --- ### minimumZoom The `minimumZoom` prop sets a minimum domain extent for the zoomed chart. When the difference between the maximum and minimum of a zoomed domain is equal to the `minimumZoom` in either dimension, the component will stop responding to events that would normally trigger zooming in. Zooming out and panning will still be enabled. When this prop is not specified, the default minimum zoom will cover 1 / 1000th of the original domain. This prop should be given as an object with numeric values for x and y. _example:_ `minimumZoom={{x: 1, y: 0.01}}` --- ### onZoomDomainChange The optional `onZoomDomainChange` prop accepts a function to be called on each update to the visible domain. The function accepts the parameters `domain` (the updated domain) and `props` (the props used by `VictoryZoomContainer`). _example:_ `onZoomDomainChange={(domain, props) => handleDomainChange(domain, props)}` --- ### zoomDomain The `zoomDomain` prop describes the zoomed state. This prop is an object that specifies separate arrays for x and y. Each array is a tuple that describes the minimum and maximum values to render. If this prop is not provided initially, the chart will render without an initial zoom, displaying the entire dataset. Updates to `zoomDomain` will trigger a re-render of the chart with the new domain. _example:_ `zoomDomain={{x: [0, 100]}}` --- ### zoomDimension When the `zoomDimension` prop is set, panning and zooming will be restricted to the given dimension (either x or y), and the domain of the other dimension will remain static. When this prop is not specified, both x and y dimensions will pan and zoom. _example:_ `zoomDimension="x"` ================================================ FILE: website/docs/charts/_category_.json ================================================ { "label": "Chart Types", "position": 1, "link": null } ================================================ FILE: website/docs/charts/area.mdx ================================================ --- title: Area --- Area charts are used for visualizing trends in data over a continuous interval while emphasizing the magnitude of the values. ## Basic See the [full API here](/docs/api/victory-area). Typically composed with [`VictoryChart`](/docs/api/victory-chart) to create full charts. ```jsx live ``` ## Area Charts - Horizontal Area charts can be rendered with a flipped axis by setting the `horizontal` prop to `true`. This prop can be applied to either `VictoryChart` or `VictoryArea`. ```jsx live ``` ## Area Charts - Interpolation Area charts can use interpolation to smooth the line between data points. See the [full list of interpolation options](/docs/api/victory-area#interpolation). ```jsx live ``` ## Area Charts - Null Data Area charts can handle null data points by setting the `data` prop to an array of objects with `x` and `y` values. Null data points will be skipped. ```jsx live ``` ## Area Charts - Discontinuous Scale Area charts can be rendered with a discontinuous scale by using the `scaleDiscontinuous` plugin from `@d3fc/d3fc-discontinuous-scale`. ```jsx live noInline function App() { const data = [ { x: new Date(2021, 5, 1), y: 8 }, { x: new Date(2021, 5, 2), y: 10 }, { x: new Date(2021, 5, 3), y: 7 }, { x: new Date(2021, 5, 4), y: 4 }, { x: new Date(2021, 5, 7), y: 6 }, { x: new Date(2021, 5, 8), y: 3 }, { x: new Date(2021, 5, 9), y: 7 }, { x: new Date(2021, 5, 10), y: 9 }, { x: new Date(2021, 5, 11), y: 6 }, ]; // scaleDiscontinuous and discontinuitySkipWeekends are both // plugins imported from @d3fc/d3fc-discontinuous-scale const discontinuousScale = scaleDiscontinuous( d3Scale.scaleTime(), ).discontinuityProvider( discontinuitySkipWeekends(), ); return ( ); } render(); ``` ## Area Charts - Plotting Area charts can render math functions as data by supplying a function to the `y` prop. ```jsx live Math.sin(d)} /> ``` ## Area Charts - Combined Area charts can be combined into the same chart. Note that the order of the components will determine the rendering order. ```jsx live ``` ## Area Charts - Baseline Area charts can be rendered with a baseline for dependent values by setting the `y0` property on each data point. ```jsx live ``` ## Area Charts - Stacked Area charts can be stacked using the `VictoryStack` component. This will automatically adjust the `y0` property for each data point and apply a `colorScale`. ```jsx live ``` ## Area Charts - Labels Add labels to charts by setting the `labels` prop to the name of a property in the dataset, or a function that returns the label value. You can customize the display of the labels by using the [`labelComponent`](/docs/api/victory-area#labelcomponent) prop. ```jsx live datum.y} /> ``` ## Area Charts - Tooltips `VictoryArea` only renders a single element to represent an entire dataset, so replacing its `labelComponent` with `VictoryTooltip` won't work as expected. Use `VictoryVoronoiContainer` to associate mouse position with the nearest data points. ```jsx live } > } labels={({ datum }) => datum.y} /> ``` ## Area Charts - Animation Charts can be animated by setting the `animate` prop. See the [animations](/docs/guides/animations) guide for more information. ```jsx live noInline function App() { const [state, setState] = React.useState({ data: getData() }); React.useState(() => { const setStateInterval = window.setInterval(() => { setState({ data: getData() }); }, 4000); return () => { window.clearInterval( setStateInterval, ); }; }, []); return ( {state.data.map((data, i) => { return ( ); })} ); } function getData() { return _.range(7).map(() => { return [ { x: 1, y: _.random(1, 5) }, { x: 2, y: _.random(1, 10) }, { x: 3, y: _.random(2, 10) }, { x: 4, y: _.random(2, 10) }, { x: 5, y: _.random(2, 15) }, ]; }); } render(); ``` ## Area Charts - Styles Chart styling can be customized by using the theme or overriding the style prop on the component. ```jsx live datum.y} style={{ data: { fill: "#c43a31", fillOpacity: 0.4, stroke: "#c43a31", strokeWidth: 2, }, labels: { fontSize: 12, fill: "#c43a31", }, }} /> ``` ## Area Charts - Events Events can be handled by passing an array of event objects to the `events` prop on the `VictoryArea` component. `VictoryArea` uses the special `all` key for the target prop to attach events to all data points. See the [events](/docs/guides/events) guide for more information. ```jsx live noInline function App() { const toggleFillColor = (fill) => { if (fill === "black") { return null; } else { return { style: { fill: "black", }, }; } }; return ( datum.y} events={[ { target: "data", eventHandlers: { onClick: () => { return [ { eventKey: "all", mutation: (props) => toggleFillColor( props.style ?.fill, ), }, ]; }, }, }, ]} /> ); } render(); ``` ## Polar Area Charts Area charts can be rendered in polar coordinates by setting the `polar` prop to `true` and using `VictoryPolarAxis` components. ```jsx live null} /> ``` ## Polar Area Charts - Radar Area charts can be rendered in polar coordinates with a radar shape by setting the `polar` prop to `true` and using `VictoryPolarAxis` components. ```jsx live noInline const characterData = [ { strength: 1, intelligence: 250, luck: 1, stealth: 40, charisma: 50, }, { strength: 2, intelligence: 300, luck: 2, stealth: 80, charisma: 90, }, { strength: 5, intelligence: 225, luck: 3, stealth: 60, charisma: 120, }, ]; function App() { const [state, setState] = React.useState({ data: processData(characterData), maxima: getMaxima(characterData), }); return ( {state.data.map((data, i) => { return ( ); })} {Object.keys(state.maxima).map( (key, i) => { return ( } labelPlacement="perpendicular" axisValue={i + 1} label={key} tickFormat={(t) => Math.ceil( t * state.maxima[key], ) } tickValues={[ 0.25, 0.5, 0.75, ]} /> ); }, )} ""} style={{ axis: { stroke: "none" }, grid: { stroke: "grey", opacity: 0.5, }, }} /> ); } function getMaxima(data) { const groupedData = Object.keys( data[0], ).reduce((memo, key) => { memo[key] = data.map((d) => d[key]); return memo; }, {}); return Object.keys( groupedData, ).reduce((memo, key) => { memo[key] = Math.max( ...groupedData[key], ); return memo; }, {}); } function processData(data) { const maxByGroup = getMaxima(data); const makeDataArray = (d) => { return Object.keys(d).map((key) => { return { x: key, y: d[key] / maxByGroup[key], }; }); }; return data.map((datum) => makeDataArray(datum), ); } render(); ``` ## Standalone Rendering Area charts can be rendered outside a VictoryChart. ```jsx live ``` They can also be embeded in other SVG components by using the `standalone` prop. ```jsx live
``` ================================================ FILE: website/docs/charts/bar.mdx ================================================ --- title: Bar --- Bar charts renders a dataset as series of bars and is used for comparing numeric values between different categories. ## Basic See the [full API here](/docs/api/victory-bar). Typically composed with [`VictoryChart`](/docs/api/victory-chart) to create full charts. ```jsx live ``` ## Bar Charts - Domain Bars in `VictoryBar` are centered around their corresponding value by default. You can move your bars away from your axis by setting a new domain, adding `domainPadding`, or changing how bars are aligned relative to their values with the `alignment` prop on `VictoryBar`. :::note Using `domainPadding` to adjust the bar position is the most common way to adjust the position of bars in a chart. ::: ```jsx live ``` :::note Setting the bar alignment using the `alignment` prop. ::: ```jsx live ``` :::note Setting the bar alignment using the `domain` prop. ::: ```jsx live ``` ## Bar Charts - Horizontal Bar charts can be rendered horizontally by setting the `horizontal` prop to `true`. This prop can be applied to either `VictoryChart` or `VictoryBar`. ```jsx live ``` ## Bar Charts - Labels Add labels to charts by setting the `labels` prop to the name of a property in the dataset, or a function that returns the label value. You can customize the display of the labels by using the [`labelComponent`](/docs/api/victory-area#labelcomponent) prop. ```jsx live datum.y} /> ``` ## Bar Charts - Tooltips Tooltips can be added by using a [`VictoryTooltip`](/docs/api/victory-tooltip) component as the `labelComponent`. ```jsx live } labels={({ datum }) => datum.y} /> ``` ## Bar Charts - Grouped Bar charts can be grouped to show how different subcategories compare to each other. ```jsx live noInline function App() { return ( ); } render(); ``` ## Bar Charts - Grouped Labels Bar chart labels for grouped bars can be rendered by setting the `labels` prop to the name of a property in the dataset, an array, or a function that returns the label value. ```jsx live noInline function App() { return ( datum.y } /> datum.y } /> datum.y } /> ); } render(); ``` ## Bar Charts - Combination Bar charts can be rendered with other chart types like `Line`. ```jsx live ``` ## Bar Charts - Animation Charts can be animated by setting the `animate` prop. See the [animations](/docs/guides/animations) guide for more information. ```jsx live noInline function App() { const [data, setData] = React.useState(getData()); React.useState(() => { const setStateInterval = window.setInterval(() => { setData(getData()); }, 4000); return () => { window.clearInterval( setStateInterval, ); }; }, []); return ( ); } function getData() { return [ { x: 1, y: _.random(1, 5) }, { x: 2, y: _.random(1, 10) }, { x: 3, y: _.random(2, 10) }, { x: 4, y: _.random(2, 10) }, { x: 5, y: _.random(2, 15) }, { x: 6, y: _.random(2, 8) }, { x: 7, y: _.random(2, 11) }, ]; } render(); ``` ## Bar Charts - Zoom & Pan Bar charts support pan and zoom behavior by using the `VictoryZoomContainer` component. See the [Pan & Zoom](/docs/guides/pan-and-zoom) guide for more information. ```jsx live } > ``` ## Bar Charts - Styles Chart styling can be customized by using the theme or overriding the style prop on the component. ```jsx live datum.y} style={{ data: { fill: "#c43a31", fillOpacity: 0.4, stroke: "#c43a31", strokeWidth: 2, }, labels: { fontSize: 12, fill: "#c43a31", }, }} /> ``` ## Bar Charts - Events Events can be handled by passing an array of event objects to the `events` prop on the component. Each event object should specify a `target` and an `eventHandlers` object. See the [events](/docs/guides/events) guide for more information. ```jsx live noInline function App() { const toggleFillColor = (fill) => { if (fill === "black") { return null; } else { return { style: { fill: "black", }, }; } }; return ( datum.y} events={[ { target: "data", eventHandlers: { onClick: () => { return [ { target: "data", mutation: (props) => toggleFillColor( props.style ?.fill, ), }, ]; }, }, }, ]} /> ); } render(); ``` ## Stacked Bar Charts Bar charts can be stacked to show how different categories contribute to the total. ```jsx live ``` ## Stacked Bar Charts - 100% While Victory does not support 100% stacked bar charts natively, you can achieve this by transforming your data. ```jsx live noInline const myDataset = [ [ { x: "a", y: 1 }, { x: "b", y: 2 }, { x: "c", y: 3 }, { x: "d", y: 2 }, { x: "e", y: 3 }, ], [ { x: "a", y: 2 }, { x: "b", y: 3 }, { x: "c", y: 7 }, { x: "d", y: 5 }, { x: "e", y: 3 }, ], [ { x: "a", y: 5 }, { x: "b", y: 2 }, { x: "c", y: 3 }, { x: "d", y: 4 }, { x: "e", y: 4 }, ], ]; function App() { // This is an example of a function you might // use to transform your data to make 100% data function transformData(dataset) { const totals = dataset[0].map( (data, i) => { return dataset.reduce( (memo, curr) => { return memo + curr[i].y; }, 0, ); }, ); return dataset.map((data) => { return data.map((datum, i) => { return { x: datum.x, y: (datum.y / totals[i]) * 100, }; }); }); } const dataset = transformData(myDataset); return (
{dataset.map((data, i) => { return ( ); })} `${tick}%` } />
); } render(); ``` ## Stacked Bar Charts - Grouped Bar charts can be stacked and grouped to show how different subcategories contribute to the total. ```jsx live noInline function App() { const getBarData = () => { return [1, 2, 3, 4, 5].map(() => { return [ { x: 1, y: Math.random() }, { x: 2, y: Math.random() }, { x: 3, y: Math.random() }, ]; }); }; return (
{getBarData().map( (data, index) => { return ( ); }, )} {getBarData().map( (data, index) => { return ( ); }, )} {getBarData().map( (data, index) => { return ( ); }, )}
); } render(); ``` ## Diverging Bar Charts Diverging bar are useful for showing how different categories compare to a common baseline. By default, Victory will calculate the domain based on the data provided. ```jsx live ``` ## Diverging Bar Charts - Horizontal Diverging bar are useful for showing how different categories compare to a common baseline. By default, Victory will calculate the domain based on the data provided. ```jsx live ``` ## Diverging Bar Charts - Grouped Diverging bar are useful for showing how different categories compare to a common baseline. By default, Victory will calculate the baseline based on the data provided. ```jsx live noInline function App() { return ( {getBarData().map( (data, index) => { return ( datum.y } /> ); }, )} ); } function getBarData() { return _.range(5).map(() => { return [ { x: "rabbits", y: _.random(-5, 5), }, { x: "cats", y: _.random(-10, 10), }, { x: "dogs", y: _.random(-15, 15), }, ]; }); } render(); ``` ## Diverging Bar Charts - Floating Diverging bar are useful for showing how different categories compare to a common baseline. ```jsx live noInline const dataA = [ { x: "Television", y: 38 }, { x: "Smartwatch", y: 37 }, { x: "Fitness Monitor", y: 25 }, { x: "Tablet", y: 19 }, { x: "Camera", y: 15 }, { x: "Laptop", y: 13 }, { x: "Phone", y: 12 }, ]; const dataB = dataA.map((point) => { const y = Math.round( point.y + 3 * (Math.random() - 0.75), ); return { ...point, y }; }); const width = 400; const height = 300; function App() { return ( -Math.abs(data.y) } labels={({ datum }) => `${Math.abs(datum.y)}%` } /> `${Math.abs(datum.y)}%` } /> } tickValues={dataA .map((point) => point.x) .reverse()} /> ); } render(); ``` ## Polar Bar Charts Bar charts can be rendered in polar coordinates by setting the `polar` prop to `true` and using `VictoryPolarAxis` components. ```jsx live null} /> ``` ## Polar Bar Charts - Stacked Bar charts can be rendered in polar coordinates by setting the `polar` prop to `true` and using `VictoryPolarAxis` components. ```jsx live noInline const directions = { 0: "E", 45: "NE", 90: "N", 135: "NW", 180: "W", 225: "SW", 270: "S", 315: "SE", }; const orange = { base: "#2D7FF9", highlight: "#2750AE", }; const red = { base: "#8B46FF", highlight: "#6B1CB0", }; const innerRadius = 30; function CompassCenter(props) { const { origin } = props; const circleStyle = { stroke: red.base, strokeWidth: 2, fill: orange.base, }; return ( ); } function CenterLabel(props) { const { datum, active, color } = props; const text = [ `${directions[datum._x]}`, `${Math.round(datum._y1)} mph`, ]; const baseStyle = { fill: color.highlight, textAnchor: "middle", }; const style = [ { ...baseStyle, fontSize: 18, fontWeight: "bold", }, { ...baseStyle, fontSize: 12 }, ]; return active ? ( ) : null; } function App() { const [state, setState] = React.useState({ wind: getWindData(), }); return ( { return [ { target: "labels", mutation: () => ({ active: true, }), }, { target: "data", mutation: () => ({ active: true, }), }, ]; }, onMouseOut: () => { return [ { target: "labels", mutation: () => ({ active: false, }), }, { target: "data", mutation: () => ({ active: false, }), }, ]; }, }, }, ]} > ""} /> +k)} tickFormat={_.values( directions, )} /> active ? orange.highlight : orange.base, width: 40, }, }} data={state.wind} x="windBearing" y="windSpeed" labels={() => ""} labelComponent={ } /> a ? red.highlight : red.base, width: 40, }, }} data={state.wind} x="windBearing" y={(d) => d.windGust - d.windSpeed } labels={() => ""} labelComponent={ } /> ); } function getWindData() { return _.keys(directions).map((d) => { const speed = Math.floor(_.random() * 17) + 4; return { windSpeed: speed, windGust: speed + _.random(2, 10), windBearing: +d, }; }); } render(); ``` ## Standalone Rendering Bar charts can be rendered outside a VictoryChart. ```jsx live ``` They can also be embeded in other SVG components by using the `standalone` prop. ```jsx live
``` ================================================ FILE: website/docs/charts/box-plot.mdx ================================================ --- title: Box Plot --- Box plots are used to show the distribution of a dataset. The box represents the interquartile range, the line in the middle is the median, and the whiskers represent the range of the data. ## Basic See the [full API here](/docs/api/victory-boxplot). Typically composed with [`VictoryChart`](/docs/api/victory-chart) to create full charts. ```jsx live ``` ## Box Plot - Horizontal Bar charts can be rendered horizontally by setting the `horizontal` prop to `true`. This prop can be applied to either `VictoryChart` or `VictoryBoxPlot`. ```jsx live ``` ## Box Plot - Labels Box plots can be labeled by setting the `labels` prop to `true`. By default this will show all labels. For more granular label control, use the individual `minLabels`, `maxLabels`, `medianLabels`, `q1Labels`, and `q3Labels` props. ```jsx live ``` Specific labels can be chosen by using the appropriate label prop. In this example, the `minLabels` prop is set to `true` to show only the minimum value of each box. ```jsx live ``` ## Box Plot - Animation Charts can be animated by setting the `animate` prop. See the [animations](/docs/guides/animations) guide for more information. ```jsx live noInline function App() { const [data, setData] = React.useState(getData()); React.useState(() => { const setStateInterval = window.setInterval(() => { setData(getData()); }, 4000); return () => { window.clearInterval( setStateInterval, ); }; }, []); return ( ); } function rr(start, end, count) { return _.range(count).map(() => _.random(start, end), ); } function getData() { return [ { x: 1, y: rr(1, 10, 4) }, { x: 2, y: rr(1, 10, 4) }, { x: 3, y: rr(1, 10, 4) }, { x: 4, y: rr(1, 10, 4) }, { x: 5, y: rr(1, 10, 4) }, { x: 6, y: rr(1, 10, 4) }, { x: 7, y: rr(1, 10, 4) }, { x: 8, y: rr(1, 10, 4) }, { x: 9, y: rr(1, 10, 4) }, ]; } render(); ``` ## Box Plot - Styles Chart styling can be customized by using the theme or overriding the style prop on the component. ```jsx live ``` ## Box Plot - Events Events can be handled by passing an array of event objects to the `events` prop on the component. Each event object should specify a `target` and an `eventHandlers` object. See the [events](/docs/guides/events) guide for more information. ```jsx live { return [ { target: "q1Labels", mutation: () => ({ text: "LABEL!", }), }, ]; }, }, }, { target: "q3", eventHandlers: { onClick: () => { return [ { mutation: (props) => { return { style: Object.assign( props.style, { fill: "tomato", }, ), }; }, }, ]; }, }, }, ]} /> ``` ## Box Plot - Tooltips Tooltips are not displayed by default on VictoryBoxPlot; to enable tooltips, use a custom events prop to handle interactions. ```jsx live noInline function App() { const activate = (label: string) => () => [ { target: label, mutation: () => ({ active: true, }), }, ]; const deactivate = (label: string) => () => [ { target: label, mutation: () => ({ active: undefined, }), }, ]; const dataTypes = [ "min", "max", "q1", "q3", "median", ]; const events = dataTypes.map( (dataType) => ({ target: dataType, eventHandlers: { onMouseOver: activate( `${dataType}Labels`, ), onFocus: activate( `${dataType}Labels`, ), onTouchStart: activate( `${dataType}Labels`, ), onMouseOut: deactivate( `${dataType}Labels`, ), onBlur: deactivate( `${dataType}Labels`, ), onTouchEnd: deactivate( `${dataType}Labels`, ), }, }), ); return ( } maxLabelComponent={ } q1LabelComponent={ } q3LabelComponent={ } medianLabelComponent={ } data={[ { x: "red", y: [5, 10, 9, 2] }, { x: "blue", y: [1, 15, 6, 8] }, { x: "green", y: [3, 5, 6, 9] }, { x: "yellow", y: [5, 20, 8, 12], }, { x: "white", y: [2, 11, 12, 13], }, ]} /> ); } render(); ``` ## Standalone Rendering Box Plot charts can be rendered outside a VictoryChart. ```jsx live ``` They can also be embeded in other SVG components by using the `standalone` prop. ```jsx live ``` ================================================ FILE: website/docs/charts/candlestick.mdx ================================================ --- title: Candlestick --- Candlesticks are used to visualize the movement of data over a time period by plotting the open, close, high, and low values of a dataset. ## Basic See the [full API here](/docs/api/victory-candlestick). Typically composed with [`VictoryChart`](/docs/api/victory-chart) to create full charts. ```jsx live ``` ## Candlestick - Horizontal Candlestick charts can be rendered horizontally by setting the `horizontal` prop to `true`. This prop can be applied to either `VictoryChart` or `VictoryBoxPlot`. ```jsx live ``` ## Candlestick - Labels Candlestick charts can be labeled by setting the `labels` prop to `true`. By default this will show all labels. It's also possible to control each label individually by using the specific label properties defined in the [VictoryCandlestick](/docs/api/victory-candlestick) API. ```jsx live ``` ## Candlestick - Label Functions The labels prop can also accept a function to customize the candlestick label. When using a function, the other labels will need to be set using their specific props in the [VictoryCandlestick](/docs/api/victory-candlestick) API. ```jsx live datum.x} openLabels={({ datum }) => `->${datum.open}` } data={[ { x: "3/1/23", open: 5, close: 10, high: 15, low: 0, }, { x: "3/2/23", open: 10, close: 15, high: 20, low: 5, }, { x: "3/3/23", open: 15, close: 20, high: 22, low: 10, }, { x: "3/4/23", open: 20, close: 10, high: 25, low: 7, }, { x: "3/5/23", open: 15, close: 8, high: 15, low: 5, }, ]} /> ``` ## Candlestick - Label Orientation The `labelOrientation` prop can be used to control the orientation of the labels. ```jsx live ``` ## Candlestick - Time Scales Candlestick charts can leverage d3-scale to handle time scales. The `x` prop can be set to a Date Object. ```jsx live `${t.getDate()}/${t.getMonth()}` } /> ``` ## Candlestick - Animation Candlestick charts can be animated with the `animate` prop. ```jsx live noInline function App() { const [data, setData] = React.useState(getData()); React.useState(() => { const setStateInterval = window.setInterval(() => { setData(getData()); }, 4000); return () => { window.clearInterval( setStateInterval, ); }; }, []); return ( ); } function getData() { return _.range(5).map((i) => { return { x: `3/${i + 1}/23`, open: _.random(5, 10), close: _.random(10, 20), high: _.random(20, 25), low: _.random(1, 5), }; }); } render(); ``` ## Candlestick - Styles Chart styling can be customized by using the theme or overriding the style prop on the component. ```jsx live "labels"} closeLabels={() => "close"} highLabels={() => "high"} lowLabels={() => "low"} openLabels={() => "open"} data={[ { x: "3/1/23", open: 5, close: 10, high: 15, low: 0, }, { x: "3/2/23", open: 10, close: 15, high: 20, low: 5, }, { x: "3/3/23", open: 15, close: 20, high: 22, low: 10, }, { x: "3/4/23", open: 20, close: 10, high: 25, low: 7, }, { x: "3/5/23", open: 10, close: 8, high: 15, low: 5, }, ]} style={{ data: { fill: "#c43a31", fillOpacity: 0.7, stroke: "#c43a31", strokeWidth: 3, }, labels: { fill: "tomato", padding: 2, }, closeLabels: { fill: "orange", padding: 2, }, highLabels: { fill: "blue", padding: 2, }, lowLabels: { fill: "teal", padding: 2, }, openLabels: { fill: "green", padding: 2, }, }} /> ``` ## Candlestick - Events Events can be handled by passing an array of event objects to the `events` prop on the component. Each event object should specify a `target` and an `eventHandlers` object. See the [events](/docs/guides/events) guide for more information. ```jsx live { return [ { target: "data", mutation: (props) => { const fill = props.style && props.style.fill; return fill === "#c43a31" ? null : { style: { fill: "#c43a31", }, }; }, }, ]; }, }, }, ]} data={sampleDataDates} /> ``` ## Candlestick - Brush and Zoom Candlestick charts support zoom and pan behavior by using the `VictoryZoomContainer` and `VictoryBrushContainer` components. See the [Pan & Zoom](/docs/guides/pan-and-zoom) and [Data Selection](/docs/guides/data-selection) guides for more information. ```jsx live noInline function App() { const sampleData = [ { x: 5, open: 5, close: 10, high: 15, low: 0, }, { x: 10, open: 10, close: 15, high: 20, low: 5, }, { x: 15, open: 15, close: 20, high: 22, low: 10, }, { x: 20, open: 20, close: 10, high: 25, low: 7, }, { x: 25, open: 10, close: 8, high: 15, low: 5, }, ]; const [state, setState] = React.useState({}); const handleZoom = (domain) => { setState({ selectedDomain: domain }); }; const handleBrush = (domain) => { setState({ zoomDomain: domain }); }; return (
} > } >
) } render(); ``` ## Standalone Rendering Box Plot charts can be rendered outside a VictoryChart. ```jsx live ``` They can also be embeded in other SVG components by using the `standalone` prop. ```jsx live ``` ================================================ FILE: website/docs/charts/error-bar.mdx ================================================ --- title: Error Bar --- Error Bars are used to represent the variability or uncertainty in a set of data and are often used with bar, line, and scatter plots. Error bars can be used to show standard deviation, standard error, confidence intervals, or any other statistical measure. ## Basics See the [full API here](/docs/api/victory-error-bar). Typically composed with [`VictoryChart`](/docs/api/victory-chart) to create full charts. ```jsx live ``` ## Error Bars - Horizontal Error Bars can be rendered horizontally by setting the `horizontal` prop to `true`. This prop can be applied to either `VictoryChart` or `VictoryBoxPlot`. ```jsx live ``` ## Error Bars - Error Formats Error bars can be customized by providing functions to the `errorX` and `errorY` props. These functions should return the error value for each data point. ```jsx live datum.error * datum.x } errorY={(datum) => datum.error * datum.y } /> ``` ## Error Bars - Labels Error Bars can be labeled by setting the `labels` prop to a function that returns a string. ```jsx live datum.y} data={[ { x: 15, y: 350, error: 0.2 }, { x: 20, y: 420, error: 0.05 }, { x: 25, y: 300, error: 0.1 }, { x: 30, y: 350, error: 0.2 }, { x: 35, y: 220, error: 0.15 }, ]} errorX={(datum) => datum.error * datum.x } errorY={(datum) => datum.error * datum.y } /> ``` ## Error Bars - Tooltips Tooltips can be added by using a [`VictoryTooltip`](/docs/api/victory-tooltip) component as the `labelComponent`. ```jsx live datum.y} data={[ { x: 15, y: 350, error: 0.2 }, { x: 20, y: 420, error: 0.05 }, { x: 25, y: 300, error: 0.1 }, { x: 30, y: 350, error: 0.2 }, { x: 35, y: 220, error: 0.15 }, ]} errorX={(datum) => datum.error * datum.x } errorY={(datum) => datum.error * datum.y } labelComponent={} /> ``` ## Error Bars - Combination Error Bars can be combined with other Victory components to create more complex visualizations. ```jsx live noInline const data = [ { x: 10, y1: 250, y2: 300, ey1: 0.1, ey2: 0.1, }, { x: 15, y1: 350, y2: 500, ey1: 0.1, ey2: 0.08, }, { x: 20, y1: 420, y2: 640, ey1: 0.05, ey2: 0.1, }, { x: 25, y1: 300, y2: 450, ey1: 0.1, ey2: 0.1, }, { x: 30, y1: 350, y2: 500, ey1: 0.1, ey2: 0.1, }, { x: 35, y1: 220, y2: 350, ey1: 0.15, ey2: 0.05, }, { x: 40, y1: 250, y2: 290, ey1: 0.1, ey2: 0.1, }, ]; const segments = ["y1", "y2"]; const colors = { y1: VictoryTheme.clean.palette.qualitative[1], y2: VictoryTheme.clean.palette.qualitative[4], }; function App() { return ( {segments.map((y) => ( datum[y] * datum[`e${y}`] } style={{ data: { stroke: colors[y], }, }} /> ))} ); } render(); ``` ## Error Bars - Styles Chart styling can be customized by using the theme or overriding the style prop on the component. ```jsx live datum.y} data={[ { x: 15, y: 350, error: 0.2 }, { x: 20, y: 420, error: 0.05 }, { x: 25, y: 300, error: 0.1 }, { x: 30, y: 350, error: 0.2 }, { x: 35, y: 220, error: 0.15 }, ]} errorX={(datum) => datum.error * datum.x } errorY={(datum) => datum.error * datum.y } style={{ data: { stroke: "#c43a31", strokeWidth: 5, }, labels: { fontSize: 15, fill: "#c43a31", }, }} /> ``` ## Error Bars - Events Events can be handled by passing an array of event objects to the `events` prop on the component. Each event object should specify a `target` and an `eventHandlers` object. See the [events](/docs/guides/events) guide for more information. ```jsx live datum.y} data={[ { x: 15, y: 350, error: 0.2 }, { x: 20, y: 420, error: 0.05 }, { x: 25, y: 300, error: 0.1 }, { x: 30, y: 350, error: 0.2 }, { x: 35, y: 220, error: 0.15 }, ]} errorX={(datum) => datum.error * datum.x } errorY={(datum) => datum.error * datum.y } events={[ { target: "data", eventHandlers: { onClick: () => { return [ { target: "data", mutation: (props) => { const stroke = props.style && props.style.stroke; return stroke === "#c43a31" ? null : { style: { stroke: "#c43a31", strokeWidth: 7, }, }; }, }, ]; }, }, }, ]} /> ``` ## Standalone Rendering Error Bars can be rendered outside a VictoryChart. ```jsx live datum.error * datum.x } errorY={(datum) => datum.error * datum.y } /> ``` They can also be embeded in other SVG components by using the `standalone` prop. ```jsx live datum.error * datum.x } errorY={(datum) => datum.error * datum.y } /> ``` ================================================ FILE: website/docs/charts/histogram.mdx ================================================ --- title: Histogram --- Renders a dataset as series of bars representing "bins", allowing the ability to view distribution of the data. The data passed in will be "binned" according to the `bin` prop that is provided (if any), allowing for flexibility in how these bins are determined. :::info Histograms are intended to be used with quantitative data. Please use [Bar Charts](/docs/charts/bar) for qualitative or categorical data. ::: ## Basic See the [full API here](/docs/api/victory-histogram). Typically composed with [`VictoryChart`](/docs/api/victory-chart) to create full charts. ```jsx live ``` ## Histogram - Date Bins Histograms can be used to visualize the distribution of data over time. `VictoryHistogram` will automatically bin data by date if the `x` prop is a date. Internally it uses `d3` to bin the data, so the behaviour reflects that of the [`d3.bin`](https://observablehq.com/@d3/d3-bin) function. ```jsx live ``` ## Histogram - Labels Add labels to charts by setting the `labels` prop to the name of a property in the dataset, or a function that returns the label value. You can customize the display of the labels by using the [`labelComponent`](/docs/api/victory-histogram) prop. ```jsx live `Bin count:\n ${datum.y}` } /> ``` ## Histogram - Tooltips Tooltips can be added by using a [`VictoryTooltip`](/docs/api/victory-tooltip) component as the `labelComponent`. ```jsx live `Bin count:\n ${datum.y}` } labelComponent={} /> ``` ## Histogram - Horizontal Histograms can be rendered horizontally by setting the `horizontal` prop to `true`. ```jsx live ``` ## Histogram - Combination Histograms can be composed with other Victory components to create more complex visualizations. ```jsx live ``` ## Histogram - Stacked Histograms can be stacked to visualize the distribution of numerical data across different categories. ```jsx live noInline const startDate = new Date( "2020-01-01T00:00:00.000Z", ); const endDate = new Date( "2020-12-31T11:59:59.000Z", ); const genres = [ "pop", "rap", "hip-hop", "r&b", "indie", "alternative", ]; const listeningData = []; for (let i = 0; i < 100; i++) { listeningData.push({ day: new Date( _.random( startDate.getTime(), endDate.getTime(), ), ), genre: genres[ _.random(0, genres.length - 1) ], }); } const groupedData = _.groupBy( listeningData, ({ genre }) => genre, ); const sharedAxisStyles = { tickLabels: { fontSize: 13, }, axisLabel: { padding: 39, fontSize: 13, fontStyle: "italic", }, }; const App = () => { return ( datum.y > 0 ? `${datum.y} ${datum.binnedData[0].genre} songs` : null } /> } theme={VictoryTheme.clean} > {Object.entries( groupedData, ).map(([key, dataGroup]) => { return ( ); })} date.toLocaleString( "default", { month: "short" }, ) } style={sharedAxisStyles} /> ); }; render(); ``` ## Histogram - Animation Histogram charts can be animated by setting the `animate` prop. ```jsx live noInline function App() { const [data, setData] = React.useState(getData()); const [bins, setBins] = React.useState(getBins()); React.useState(() => { const setStateInterval = window.setInterval(() => { setData(getData()); setBins(getBins()); }, 4000); return () => { window.clearInterval( setStateInterval, ); }; }, []); return ( ); } function getData() { return _.range(100).map(() => ({ x: _.random(0, 300), })); } function getBins() { return _.range( 0, _.random(100, 300), 10, ); } render(); ``` ## Histogram - Styles Chart styling can be customized by using the theme or overriding the style prop on the component. ```jsx live datum.y === 3 ? "#000000" : "#c43a31", stroke: ({ index }) => +index % 2 === 0 ? "#000000" : "#c43a31", fillOpacity: 0.7, strokeWidth: 3, }, labels: { fontSize: 15, fill: ({ datum }) => datum.y === 3 ? "#000000" : "#c43a31", }, }} data={sampleHistogramData} labels={({ datum }) => datum.y} /> ``` ## Standalone Rendering Histogram charts can be rendered outside a VictoryChart. ```jsx live ``` They can also be embeded in other SVG components by using the `standalone` prop. ```jsx live
``` ================================================ FILE: website/docs/charts/line.mdx ================================================ --- title: Line --- Line charts are used for visualizing trends in data over a continuous interval. ## Basic See the [full API here](/docs/api/victory-line). Typically composed with [`VictoryChart`](/docs/api/victory-chart) to create full charts. ```jsx live ``` ## Line Charts - Horizontal Line charts can be rendered with a flipped axis by setting the `horizontal` prop to `true`. This prop can be applied to either `VictoryChart` or `VictoryLine`. ```jsx live ``` ## Line Charts - Interpolation Line charts can use interpolation to smooth the line between data points. See the [full list of interpolation options](/docs/api/victory-line#interpolation) in the common props section. ```jsx live noInline const data = [ { x: 0, y: 0 }, { x: 1, y: 2 }, { x: 2, y: 1 }, { x: 3, y: 4 }, { x: 4, y: 3 }, { x: 5, y: 5 }, ]; const cartesianInterpolations = [ "basis", "bundle", "cardinal", "catmullRom", "linear", "monotoneX", "monotoneY", "natural", "step", "stepAfter", "stepBefore", ]; const InterpolationSelect = ({ currentValue, values, onChange, }) => ( ); function App() { const [state, setState] = React.useState({ interpolation: "natural", }); return (
setState({ interpolation: event.target.value, }) } />
); } render(); ``` ## Line Charts - Sampling Line charts can be rendered with a specific number of samples across a range of values by setting the `samples` prop. ```jsx live Math.sin(5 * Math.PI * d.x) } /> Math.cos(5 * Math.PI * d.x) } /> ``` ## Line Charts - Null Data Line charts can handle null data points by setting the `data` prop to an array of objects with `x` and `y` values. Null data points will be skipped. ```jsx live ``` ## Line Charts - Discontinuous Scale Line charts can be rendered with a discontinuous scale by using the `scaleDiscontinuous` plugin from `@d3fc/d3fc-discontinuous-scale`. ```jsx live noInline function App() { const data = [ { x: new Date(2021, 5, 1), y: 8 }, { x: new Date(2021, 5, 2), y: 10 }, { x: new Date(2021, 5, 3), y: 7 }, { x: new Date(2021, 5, 4), y: 4 }, { x: new Date(2021, 5, 7), y: 6 }, { x: new Date(2021, 5, 8), y: 3 }, { x: new Date(2021, 5, 9), y: 7 }, { x: new Date(2021, 5, 10), y: 9 }, { x: new Date(2021, 5, 11), y: 6 }, ]; // scaleDiscontinuous and discontinuitySkipWeekends are both // plugins imported from @d3fc/d3fc-discontinuous-scale const discontinuousScale = scaleDiscontinuous( d3Scale.scaleTime(), ).discontinuityProvider( discontinuitySkipWeekends(), ); return ( ); } render(); ``` ## Line Charts - Combined Line charts can be combined into the same chart. Note that the order of the components will determine the rendering order. ```jsx live ``` ## Line Charts - Stacked Line charts can be stacked using the `VictoryStack` component. This will automatically adjust the baseline for each data point and apply a `colorScale`. ```jsx live ``` ## Line Charts - Labels Add labels to charts by setting the `labels` prop to the name of a property in the dataset, or a function that returns the label value. You can customize the display of the labels by using the [`labelComponent`](/docs/api/victory-line) prop. ```jsx live datum.y} /> ``` ## Line Charts - Tooltips `VictoryLine` only renders a single element to represent an entire dataset, so replacing its `labelComponent` with `VictoryTooltip` won't work as expected. Use `VictoryVoronoiContainer` to associate mouse position with the nearest data points. ```jsx live `y: ${datum.y}` } labelComponent={ } /> } > ``` ## Line Charts - Animation Charts can be animated by setting the `animate` prop. See the [animations](/docs/guides/animations) guide for more information. ```jsx live noInline function App() { const [data, setData] = React.useState(getData()); React.useState(() => { const setStateInterval = window.setInterval(() => { setData(getData()); }, 4000); return () => { window.clearInterval( setStateInterval, ); }; }, []); return ( ); } function getData() { return _.range(1, 30).map((i) => ({ x: i, y: _.random(1, 50), })); } render(); ``` ## Line Charts - Styles Chart styling can be customized by using the theme or overriding the style prop on the component. ```jsx live datum.y} style={{ data: { stroke: "#c43a31", strokeWidth: ({ data }) => data.length, }, labels: { fontSize: 15, fill: ({ datum }) => datum.x === 3 ? "#000000" : "#c43a31", }, }} /> ``` ## Line Charts - Events Events can be handled by passing an array of event objects to the `events` prop on the `VictoryLine` component. `VictoryLine` uses the special `all` key for the target prop to attach events to all data points. See the [events](/docs/guides/events) guide for more information. ```jsx live noInline function App() { const toggleFillColor = (stroke) => { if (stroke === "black") { return null; } else { return { style: { stroke: "black", strokeWidth: 5, }, }; } }; return ( datum.y} events={[ { target: "data", eventHandlers: { onClick: () => { return [ { eventKey: "all", mutation: (props) => toggleFillColor( props.style ?.stroke, ), }, ]; }, }, }, ]} /> ); } render(); ``` ## Polar Line Charts Line charts can be rendered in polar coordinates by setting the `polar` prop to `true` and using `VictoryPolarAxis` components. ```jsx live null} /> ``` ## Polar Line Charts - Cardioid Line charts can be rendered in polar coordinates with a cardioid shape by setting the `polar` prop to `true` and using `VictoryPolarAxis` components. ```jsx live noInline function App() { return ( ""} /> {[5, 4, 3, 2, 1].map((val, i) => { return ( val * (1 - Math.cos(d.x)) } /> ); })} ); } render(); ``` ## Standalone Rendering Bar charts can be rendered outside a VictoryChart. ```jsx live ``` They can also be embeded in other SVG components by using the `standalone` prop. ```jsx live
``` ================================================ FILE: website/docs/charts/pie.mdx ================================================ --- title: Pie --- Pie charts can be used to visually represent proportions of a whole for a limited number of categories. ## Basic See the [full API here](/docs/api/victory-pie). Typically composed with [`VictoryChart`](/docs/api/victory-chart) to create full charts. ```jsx live ``` ## Pie Chart - Semi Circular Pie charts can be rendered within a specific angle range. ```jsx live ``` ## Pie Chart - Donut Pie charts can be rendered as donuts by setting the `innerRadius` prop. ```jsx live ``` ## Pie Chart - Ring Pie charts can be rendered as rings by adjusting various radius props. ```jsx live ``` ## Pie Chart - Exploded Pie charts can be exploded to emphasize the categories. ```jsx live ``` ## Pie Chart - Ordered Pie chart slices can be ordered using the standard `categories` prop. ```jsx live ``` ## Pie Chart - Variable radius Pie charts can have variable radius by setting the `radius` prop to a function. ```jsx live datum.y + 75} data={[ { x: "Cats", y: 30 }, { x: "Dogs", y: 35 }, { x: "Birds", y: 25 }, { x: "Rabbits", y: 10 }, ]} theme={VictoryTheme.clean} /> ``` ## Pie Chart - Center Labels Pie charts can have center labels by providing a custom label. ```jsx live ``` ## Pie Chart - Independent Labels Pie chart data can use independent labels in the dataset. ```jsx live ``` ## Pie Chart - Label Position Pie chart labels can be positioned at different points along each slice by using the `labelPosition` prop. ```jsx live `${datum.l}\ndegrees` } data={[ { x: 1, y: 1, l: 0 }, { x: 2, y: 1, l: 45 }, { x: 3, y: 1, l: 90 }, { x: 4, y: 1, l: 135 }, { x: 5, y: 1, l: 180 }, { x: 6, y: 1, l: 225 }, { x: 7, y: 1, l: 270 }, { x: 8, y: 1, l: 315 }, ]} /> ``` ## Pie Chart - Label Indicator Pie charts can show a label indicator by setting the `labelIndicator` prop. ```jsx live ``` ## Pie Chart - Custom Label Indicator Pie charts can show a custom label indicator by setting the `labelIndicator` prop to a custom component. ```jsx live } labels data={[ { x: "Cats", y: 30 }, { x: "Dogs", y: 35 }, { x: "Birds", y: 25 }, { x: "Rabbits", y: 10 }, ]} /> ``` ## Pie Chart - Tooltips Tooltips can be added by using a [`VictoryTooltip`](/docs/api/victory-tooltip) component as the `labelComponent`. ```jsx live `${datum.l}\ndegrees` } labelComponent={} data={[ { x: 1, y: 1, l: 0 }, { x: 2, y: 1, l: 45 }, { x: 3, y: 1, l: 90 }, { x: 4, y: 1, l: 135 }, { x: 5, y: 1, l: 180 }, { x: 6, y: 1, l: 225 }, { x: 7, y: 1, l: 270 }, { x: 8, y: 1, l: 315 }, ]} /> ``` ## Pie Chart - Animation Charts can be animated by setting the `animate` prop. See the [animations](/docs/guides/animations) guide for more information. ```jsx live noInline function App() { const [data, setData] = React.useState(getData()); React.useState(() => { const setStateInterval = window.setInterval(() => { setData(getData()); }, 4000); return () => { window.clearInterval( setStateInterval, ); }; }, []); return ( ); } function getData() { const rand = () => Math.max( Math.floor(Math.random() * 10000), 1000, ); return [ { x: "5-13", y: rand() }, { x: "14-17", y: rand() }, { x: "18-24", y: rand() }, { x: "25-44", y: rand() }, { x: "45-64", y: rand() }, { x: "≥65", y: rand() }, ]; } render(); ``` ## Pie Chart - Styles Chart styling can be customized by using the theme or overriding the style prop on the component. ```jsx live ``` ## Pie Chart - Events Events can be handled by passing an array of event objects to the `events` prop on the component. Each event object should specify a `target` and an `eventHandlers` object. See the [events](/docs/guides/events) guide for more information. ```jsx live { return [ { target: "data", mutation: ({ style }) => { return style.fill === "#c43a31" ? null : { style: { fill: "#c43a31", }, }; }, }, { target: "labels", mutation: ({ text }) => { return text === "clicked" ? null : { text: "clicked" }; }, }, ]; }, }, }, ]} /> ``` ## Standalone Rendering Pie charts can also be embeded in other SVG components by using the `standalone` prop. ```jsx live ``` ================================================ FILE: website/docs/charts/scatter.mdx ================================================ --- title: Scatter --- Scatter charts render a dataset as a series of points. ## Basic See the [full API here](/docs/api/victory-scatter). Typically composed with [`VictoryChart`](/docs/api/victory-chart) to create full charts. ```jsx live ``` ## Scatter Charts - Labels Add labels to charts by setting the `labels` prop to the name of a property in the dataset, or a function that returns the label value. You can customize the display of the labels by using the [`labelComponent`](/docs/api/victory-area#labelcomponent) prop. `VictoryScatter` will also preferentially use the `label` property from the data object. ```jsx live datum.y} /> ``` #### Custom Labels Custom labels can be rendered by using the `labelComponent` prop. See the [VictoryLabel](/docs/api/victory-label) API for more information. ```jsx live datum.y} labelComponent={ } /> ``` ## Scatter Charts - Tooltips Tooltips can be added by using a [`VictoryTooltip`](/docs/api/victory-tooltip) component as the `labelComponent`. ```jsx live datum.y} labelComponent={ } /> ``` ## Scatter Charts - Axis Scatter charts support all four quadrants by default. You can customize the domain of the chart by setting the `domain` prop on `VictoryChart` or `VictoryScatter`. ```jsx live ``` ## Scatter Charts - Horizontal Scatter charts can be rendered with a flipped axis by setting the `horizontal` prop to `true`. This prop can be applied to either `VictoryChart` or `VictoryLine`. ```jsx live ``` ## Scatter Charts - Null Data Scatter charts can handle null data points by setting the `data` prop to an array of objects with `x` and `y` values. Null data points will be skipped. ```jsx live ``` ## Scatter Charts - Bubble Scatter charts can render a dynamic bubble size by setting the `bubbleProperty` to a property of the data object. ```jsx live ``` ## Scatter Charts - Symbols Scatter chart bubbles can be customized by setting the `symbol` prop. ```jsx live datum.y} /> ``` ## Scatter Charts - Custom Icons Scatter chart bubbles can also leverage SVG elements such as those from icon libraries like [react-icons](https://react-icons.github.io/react-icons/) by using the `dataComponent` property. ```jsx live noInline const { FaSun } = reactIconsFa; const CustomIcon = (props) => { return ( ); }; function App() { return ( } /> ); } render(); ``` ## Scatter Charts - Animation Scatter charts support all four quadrants by default. You can customize the domain of the chart by setting the `domain` prop on `VictoryChart` or `VictoryScatter`. ```jsx live noInline function App() { const [data, setData] = React.useState(getData()); React.useState(() => { const setStateInterval = window.setInterval(() => { setData(getData()); }, 4000); return () => { window.clearInterval( setStateInterval, ); }; }, []); return ( } data={data} style={{ data: { fill: ({ datum }) => datum.fill, opacity: ({ datum }) => datum.opacity, }, }} animate={{ onExit: { duration: 500, before: () => ({ opacity: 0.3, }), }, onEnter: { duration: 500, before: () => ({ opacity: 0.3, }), after: (datum) => ({ opacity: datum.opacity || 1, }), }, }} /> ); } function getData() { const colors = [ "violet", "cornflowerblue", "gold", "orange", "turquoise", "tomato", "greenyellow", ]; const symbols = [ "circle", "star", "square", "triangleUp", "triangleDown", "diamond", "plus", ]; const elementNum = _.random(10, 40); return _.range(elementNum).map( (index) => { const scaledIndex = Math.floor( index % 7, ); return { x: _.random(10, 50), y: _.random(2, 100), size: _.random(8) + 3, symbol: symbols[scaledIndex], fill: colors[_.random(0, 6)], opacity: 1, }; }, ); } render(); ``` ## Scatter Charts - Styles Chart styling can be customized by using the theme or overriding the style prop on the component. ```jsx live datum.x === 3 ? "#000000" : "#c43a31", stroke: ({ datum }) => datum.x === 3 ? "#000000" : "#c43a31", fillOpacity: 0.7, strokeWidth: 3, }, labels: { fontSize: 15, fill: ({ datum }) => datum.x === 3 ? "#000000" : "#c43a31", }, }} /> ``` ## Scatter Charts - Events Events can be handled by passing an array of event objects to the `events` prop on the component. Each event object should specify a `target` and an `eventHandlers` object. See the [events](/docs/guides/events) guide for more information. ```jsx live { return [ { target: "data", mutation: (props) => { const fill = props.style && props.style.fill; return fill === "black" ? null : { style: { fill: "black", }, }; }, }, ]; }, }, }, ]} /> ``` ## Polar Scatter Charts Line charts can be rendered in polar coordinates by setting the `polar` prop to `true` and using `VictoryPolarAxis` components. ```jsx live null} /> ``` ## Standalone Rendering Scatter charts can be rendered outside a VictoryChart. ```jsx live ``` They can also be embeded in other SVG components by using the `standalone` prop. ```jsx live
``` ================================================ FILE: website/docs/charts/voronoi.mdx ================================================ --- title: Voronoi --- Voronoi charts are used for visualizing proximity and influence by dividing space into distinct regions based on the distance to a set of points. ## Basic See the [full API here](/docs/api/victory-voronoi). Typically composed with [`VictoryChart`](/docs/api/victory-chart) to create full charts. ```jsx live ``` ## Voronoi - Labels Voronoi charts can be used to display labels using a `label` property in the dataset or by specifying a function to the `labels` prop. ```jsx live ``` ```jsx live `y: ${datum.y}` } /> ``` ## Voronoi - Tooltips Tooltips can be added by using a [`VictoryTooltip`](/docs/api/victory-tooltip) component as the `labelComponent`. ```jsx live datum.y} labelComponent={} /> ``` ## Voronoi - Circles Voronoi charts can also be used to display circles around data points. ```jsx live noInline const data = [ { x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 5 }, { x: 4, y: 4 }, { x: 5, y: 7 }, ]; function App() { return ( ); } render(); ``` ## Voronoi - Animation Charts can be animated by setting the `animate` prop. See the [animations](/docs/guides/animations) guide for more information. ```jsx live noInline function App() { const [data, setData] = React.useState(getData()); React.useState(() => { const setStateInterval = window.setInterval(() => { setData(getData()); }, 4000); return () => { window.clearInterval( setStateInterval, ); }; }, []); return ( ); } function getData() { return _.range(20).map((i: number) => { return { x: _.random(600), y: _.random(600), i, }; }); } render(); ``` ## Voronoi - Styles Chart styling can be customized by using the theme or overriding the style prop on the component. ```jsx live ``` ## Voronoi - Events Events can be handled by passing an array of event objects to the `events` prop on the component. Each event object should specify a `target` and an `eventHandlers` object. See the [events](/docs/guides/events) guide for more information. ```jsx live { return [ { target: "data", mutation: (props) => { const fill = props.style && props.style.fill; return fill === "white" ? null : { style: { fill: "white", }, }; }, }, ]; }, }, }, ]} data={sampleData} /> ``` ## Standalone Rendering Area charts can be rendered outside a VictoryChart. ```jsx live ``` They can also be embeded in other SVG components by using the `standalone` prop. ```jsx live ``` ================================================ FILE: website/docs/examples/_category_.json ================================================ { "label": "Advanced Examples", "position": 4, "link": null } ================================================ FILE: website/docs/examples/anim-happy-holidays.mdx ================================================ --- title: Animation - Happy Holidays description: Happy Holidays from the Victory Team! image: https://res.cloudinary.com/formidablelabs/image/upload/v1703017786/dotcom/victory/santa.gif --- ```jsx live noInline const frameRate = 100; const frameHeight = 64; const frameWidth = 64; const sceneHeight = 512; const sceneWidth = 512; const cellStyle = { data: { fill: (args) => args.datum.fill } }; const domain = { x: [0, frameWidth], y: [0, frameHeight], }; const santaData = [{"x":0,"y":64,"fill":"#0043bf"},{"x":0,"y":63,"fill":"#0043bf"},{"x":0,"y":62,"fill":"#0043bf"},{"x":0,"y":61,"fill":"#0043bf"},{"x":0,"y":60,"fill":"#0043bf"},{"x":0,"y":59,"fill":"#0043bf"},{"x":0,"y":58,"fill":"#0043bf"},{"x":0,"y":57,"fill":"#0043bf"},{"x":0,"y":56,"fill":"#0043bf"},{"x":0,"y":55,"fill":"#0043bf"},{"x":0,"y":54,"fill":"#0043bf"},{"x":0,"y":53,"fill":"#0043bf"},{"x":0,"y":52,"fill":"#0043bf"},{"x":0,"y":51,"fill":"#0043bf"},{"x":0,"y":50,"fill":"#0043bf"},{"x":0,"y":49,"fill":"#0043bf"},{"x":0,"y":48,"fill":"#0043bf"},{"x":0,"y":47,"fill":"#0043bf"},{"x":0,"y":46,"fill":"#0043bf"},{"x":0,"y":45,"fill":"#0043bf"},{"x":0,"y":44,"fill":"#0043bf"},{"x":0,"y":43,"fill":"#0043bf"},{"x":0,"y":42,"fill":"#0043bf"},{"x":0,"y":41,"fill":"#0043bf"},{"x":0,"y":40,"fill":"#0043bf"},{"x":0,"y":39,"fill":"#0043bf"},{"x":0,"y":38,"fill":"#0043bf"},{"x":0,"y":37,"fill":"#0043bf"},{"x":0,"y":36,"fill":"#0043bf"},{"x":0,"y":35,"fill":"#0043bf"},{"x":0,"y":34,"fill":"#0043bf"},{"x":0,"y":33,"fill":"#0043bf"},{"x":0,"y":32,"fill":"#0043bf"},{"x":0,"y":31,"fill":"#0043bf"},{"x":0,"y":30,"fill":"#0043bf"},{"x":0,"y":29,"fill":"#0043bf"},{"x":0,"y":28,"fill":"#0043bf"},{"x":0,"y":27,"fill":"#0043bf"},{"x":0,"y":26,"fill":"#0043bf"},{"x":0,"y":25,"fill":"#0043bf"},{"x":0,"y":24,"fill":"#0043bf"},{"x":0,"y":23,"fill":"#0043bf"},{"x":0,"y":22,"fill":"#0043bf"},{"x":0,"y":21,"fill":"#0043bf"},{"x":0,"y":20,"fill":"#0043bf"},{"x":0,"y":19,"fill":"#0043bf"},{"x":0,"y":18,"fill":"#0043bf"},{"x":0,"y":17,"fill":"#0043bf"},{"x":0,"y":16,"fill":"#0043bf"},{"x":0,"y":15,"fill":"#0043bf"},{"x":0,"y":14,"fill":"#b1c2d1"},{"x":0,"y":13,"fill":"#adbecd"},{"x":0,"y":12,"fill":"#e0e1d4"},{"x":0,"y":11,"fill":"#e0e1d4"},{"x":0,"y":10,"fill":"#e0e1d4"},{"x":0,"y":9,"fill":"#e0e1d4"},{"x":0,"y":8,"fill":"#e0e1d4"},{"x":0,"y":7,"fill":"#e0e1d4"},{"x":0,"y":6,"fill":"#e0e1d4"},{"x":0,"y":5,"fill":"#e0e1d4"},{"x":0,"y":4,"fill":"#e0e1d4"},{"x":0,"y":3,"fill":"#e0e1d4"},{"x":0,"y":2,"fill":"#e0e1d4"},{"x":0,"y":1,"fill":"#e0e1d4"},{"x":1,"y":64,"fill":"#0043bf"},{"x":1,"y":63,"fill":"#0043bf"},{"x":1,"y":62,"fill":"#0043bf"},{"x":1,"y":61,"fill":"#0043bf"},{"x":1,"y":60,"fill":"#0043bf"},{"x":1,"y":59,"fill":"#0043bf"},{"x":1,"y":58,"fill":"#0043bf"},{"x":1,"y":57,"fill":"#0043bf"},{"x":1,"y":56,"fill":"#0043bf"},{"x":1,"y":55,"fill":"#0043bf"},{"x":1,"y":54,"fill":"#0043bf"},{"x":1,"y":53,"fill":"#0043bf"},{"x":1,"y":52,"fill":"#0043bf"},{"x":1,"y":51,"fill":"#0043bf"},{"x":1,"y":50,"fill":"#0043bf"},{"x":1,"y":49,"fill":"#0043bf"},{"x":1,"y":48,"fill":"#0043bf"},{"x":1,"y":47,"fill":"#0043bf"},{"x":1,"y":46,"fill":"#0043bf"},{"x":1,"y":45,"fill":"#0043bf"},{"x":1,"y":44,"fill":"#0043bf"},{"x":1,"y":43,"fill":"#0043bf"},{"x":1,"y":42,"fill":"#0043bf"},{"x":1,"y":41,"fill":"#0043bf"},{"x":1,"y":40,"fill":"#0043bf"},{"x":1,"y":39,"fill":"#0043bf"},{"x":1,"y":38,"fill":"#0043bf"},{"x":1,"y":37,"fill":"#0043bf"},{"x":1,"y":36,"fill":"#0043bf"},{"x":1,"y":35,"fill":"#0043bf"},{"x":1,"y":34,"fill":"#0043bf"},{"x":1,"y":33,"fill":"#0043bf"},{"x":1,"y":32,"fill":"#0043bf"},{"x":1,"y":31,"fill":"#0043bf"},{"x":1,"y":30,"fill":"#0043bf"},{"x":1,"y":29,"fill":"#0043bf"},{"x":1,"y":28,"fill":"#0043bf"},{"x":1,"y":27,"fill":"#0043bf"},{"x":1,"y":26,"fill":"#0043bf"},{"x":1,"y":25,"fill":"#0043bf"},{"x":1,"y":24,"fill":"#0043bf"},{"x":1,"y":23,"fill":"#0043bf"},{"x":1,"y":22,"fill":"#0043bf"},{"x":1,"y":21,"fill":"#0043bf"},{"x":1,"y":20,"fill":"#0043bf"},{"x":1,"y":19,"fill":"#0043bf"},{"x":1,"y":18,"fill":"#0043bf"},{"x":1,"y":17,"fill":"#0043bf"},{"x":1,"y":16,"fill":"#0043bf"},{"x":1,"y":15,"fill":"#0043bf"},{"x":1,"y":14,"fill":"#b1c2d1"},{"x":1,"y":13,"fill":"#adbecd"},{"x":1,"y":12,"fill":"#e0e1d4"},{"x":1,"y":11,"fill":"#e0e1d4"},{"x":1,"y":10,"fill":"#e0e1d4"},{"x":1,"y":9,"fill":"#e0e1d4"},{"x":1,"y":8,"fill":"#e0e1d4"},{"x":1,"y":7,"fill":"#e0e1d4"},{"x":1,"y":6,"fill":"#e0e1d4"},{"x":1,"y":5,"fill":"#e0e1d4"},{"x":1,"y":4,"fill":"#e0e1d4"},{"x":1,"y":3,"fill":"#e0e1d4"},{"x":1,"y":2,"fill":"#e0e1d4"},{"x":1,"y":1,"fill":"#e0e1d4"},{"x":2,"y":64,"fill":"#0043bf"},{"x":2,"y":63,"fill":"#0043bf"},{"x":2,"y":62,"fill":"#ffffff"},{"x":2,"y":61,"fill":"#ffffff"},{"x":2,"y":60,"fill":"#0043bf"},{"x":2,"y":59,"fill":"#0043bf"},{"x":2,"y":58,"fill":"#0043bf"},{"x":2,"y":57,"fill":"#0043bf"},{"x":2,"y":56,"fill":"#0043bf"},{"x":2,"y":55,"fill":"#0043bf"},{"x":2,"y":54,"fill":"#0043bf"},{"x":2,"y":53,"fill":"#0043bf"},{"x":2,"y":52,"fill":"#0043bf"},{"x":2,"y":51,"fill":"#0043bf"},{"x":2,"y":50,"fill":"#0043bf"},{"x":2,"y":49,"fill":"#0043bf"},{"x":2,"y":48,"fill":"#0043bf"},{"x":2,"y":47,"fill":"#0043bf"},{"x":2,"y":46,"fill":"#0043bf"},{"x":2,"y":45,"fill":"#0043bf"},{"x":2,"y":44,"fill":"#0043bf"},{"x":2,"y":43,"fill":"#0043bf"},{"x":2,"y":42,"fill":"#0043bf"},{"x":2,"y":41,"fill":"#0043bf"},{"x":2,"y":40,"fill":"#0043bf"},{"x":2,"y":39,"fill":"#0043bf"},{"x":2,"y":38,"fill":"#0043bf"},{"x":2,"y":37,"fill":"#0043bf"},{"x":2,"y":36,"fill":"#0043bf"},{"x":2,"y":35,"fill":"#0043bf"},{"x":2,"y":34,"fill":"#0043bf"},{"x":2,"y":33,"fill":"#0043bf"},{"x":2,"y":32,"fill":"#0043bf"},{"x":2,"y":31,"fill":"#0043bf"},{"x":2,"y":30,"fill":"#0043bf"},{"x":2,"y":29,"fill":"#0043bf"},{"x":2,"y":28,"fill":"#0043bf"},{"x":2,"y":27,"fill":"#0043bf"},{"x":2,"y":26,"fill":"#0043bf"},{"x":2,"y":25,"fill":"#0043bf"},{"x":2,"y":24,"fill":"#0043bf"},{"x":2,"y":23,"fill":"#0043bf"},{"x":2,"y":22,"fill":"#ffffff"},{"x":2,"y":21,"fill":"#ffffff"},{"x":2,"y":20,"fill":"#0043bf"},{"x":2,"y":19,"fill":"#0043bf"},{"x":2,"y":18,"fill":"#0043bf"},{"x":2,"y":17,"fill":"#0043bf"},{"x":2,"y":16,"fill":"#0043bf"},{"x":2,"y":15,"fill":"#0043bf"},{"x":2,"y":14,"fill":"#b3c4d3"},{"x":2,"y":13,"fill":"#b3c4d3"},{"x":2,"y":12,"fill":"#e0e1d4"},{"x":2,"y":11,"fill":"#e0e1d4"},{"x":2,"y":10,"fill":"#e0e1d4"},{"x":2,"y":9,"fill":"#e0e1d4"},{"x":2,"y":8,"fill":"#e0e1d4"},{"x":2,"y":7,"fill":"#e0e1d4"},{"x":2,"y":6,"fill":"#e0e1d4"},{"x":2,"y":5,"fill":"#e0e1d4"},{"x":2,"y":4,"fill":"#e0e1d4"},{"x":2,"y":3,"fill":"#e0e1d4"},{"x":2,"y":2,"fill":"#e0e1d4"},{"x":2,"y":1,"fill":"#e0e1d4"},{"x":3,"y":64,"fill":"#0043bf"},{"x":3,"y":63,"fill":"#0043bf"},{"x":3,"y":62,"fill":"#ffffff"},{"x":3,"y":61,"fill":"#ffffff"},{"x":3,"y":60,"fill":"#0043bf"},{"x":3,"y":59,"fill":"#0043bf"},{"x":3,"y":58,"fill":"#0043bf"},{"x":3,"y":57,"fill":"#0043bf"},{"x":3,"y":56,"fill":"#0043bf"},{"x":3,"y":55,"fill":"#0043bf"},{"x":3,"y":54,"fill":"#0043bf"},{"x":3,"y":53,"fill":"#0043bf"},{"x":3,"y":52,"fill":"#0043bf"},{"x":3,"y":51,"fill":"#0043bf"},{"x":3,"y":50,"fill":"#0043bf"},{"x":3,"y":49,"fill":"#0043bf"},{"x":3,"y":48,"fill":"#0043bf"},{"x":3,"y":47,"fill":"#0043bf"},{"x":3,"y":46,"fill":"#0043bf"},{"x":3,"y":45,"fill":"#0043bf"},{"x":3,"y":44,"fill":"#0043bf"},{"x":3,"y":43,"fill":"#0043bf"},{"x":3,"y":42,"fill":"#0043bf"},{"x":3,"y":41,"fill":"#0043bf"},{"x":3,"y":40,"fill":"#0043bf"},{"x":3,"y":39,"fill":"#0043bf"},{"x":3,"y":38,"fill":"#0043bf"},{"x":3,"y":37,"fill":"#0043bf"},{"x":3,"y":36,"fill":"#0043bf"},{"x":3,"y":35,"fill":"#0043bf"},{"x":3,"y":34,"fill":"#0043bf"},{"x":3,"y":33,"fill":"#0043bf"},{"x":3,"y":32,"fill":"#0043bf"},{"x":3,"y":31,"fill":"#0043bf"},{"x":3,"y":30,"fill":"#0043bf"},{"x":3,"y":29,"fill":"#0043bf"},{"x":3,"y":28,"fill":"#0043bf"},{"x":3,"y":27,"fill":"#0043bf"},{"x":3,"y":26,"fill":"#0043bf"},{"x":3,"y":25,"fill":"#0043bf"},{"x":3,"y":24,"fill":"#0043bf"},{"x":3,"y":23,"fill":"#0043bf"},{"x":3,"y":22,"fill":"#ffffff"},{"x":3,"y":21,"fill":"#ffffff"},{"x":3,"y":20,"fill":"#0043bf"},{"x":3,"y":19,"fill":"#0043bf"},{"x":3,"y":18,"fill":"#0043bf"},{"x":3,"y":17,"fill":"#0043bf"},{"x":3,"y":16,"fill":"#0043bf"},{"x":3,"y":15,"fill":"#0043bf"},{"x":3,"y":14,"fill":"#b3c4d3"},{"x":3,"y":13,"fill":"#b3c4d3"},{"x":3,"y":12,"fill":"#e0e1d4"},{"x":3,"y":11,"fill":"#e0e1d4"},{"x":3,"y":10,"fill":"#e0e1d4"},{"x":3,"y":9,"fill":"#e0e1d4"},{"x":3,"y":8,"fill":"#e0e1d4"},{"x":3,"y":7,"fill":"#e0e1d4"},{"x":3,"y":6,"fill":"#e0e1d4"},{"x":3,"y":5,"fill":"#e0e1d4"},{"x":3,"y":4,"fill":"#e0e1d4"},{"x":3,"y":3,"fill":"#e0e1d4"},{"x":3,"y":2,"fill":"#e0e1d4"},{"x":3,"y":1,"fill":"#e0e1d4"},{"x":4,"y":64,"fill":"#0043bf"},{"x":4,"y":63,"fill":"#0043bf"},{"x":4,"y":62,"fill":"#0043bf"},{"x":4,"y":61,"fill":"#0043bf"},{"x":4,"y":60,"fill":"#0043bf"},{"x":4,"y":59,"fill":"#0043bf"},{"x":4,"y":58,"fill":"#0043bf"},{"x":4,"y":57,"fill":"#0043bf"},{"x":4,"y":56,"fill":"#0043bf"},{"x":4,"y":55,"fill":"#0043bf"},{"x":4,"y":54,"fill":"#0043bf"},{"x":4,"y":53,"fill":"#0043bf"},{"x":4,"y":52,"fill":"#0043bf"},{"x":4,"y":51,"fill":"#0043bf"},{"x":4,"y":50,"fill":"#ffffff"},{"x":4,"y":49,"fill":"#ffffff"},{"x":4,"y":48,"fill":"#0043bf"},{"x":4,"y":47,"fill":"#0043bf"},{"x":4,"y":46,"fill":"#0043bf"},{"x":4,"y":45,"fill":"#0043bf"},{"x":4,"y":44,"fill":"#0043bf"},{"x":4,"y":43,"fill":"#0043bf"},{"x":4,"y":42,"fill":"#0043bf"},{"x":4,"y":41,"fill":"#0043bf"},{"x":4,"y":40,"fill":"#ffffff"},{"x":4,"y":39,"fill":"#ffffff"},{"x":4,"y":38,"fill":"#0043bf"},{"x":4,"y":37,"fill":"#0043bf"},{"x":4,"y":36,"fill":"#0043bf"},{"x":4,"y":35,"fill":"#0043bf"},{"x":4,"y":34,"fill":"#0043bf"},{"x":4,"y":33,"fill":"#0043bf"},{"x":4,"y":32,"fill":"#0043bf"},{"x":4,"y":31,"fill":"#0043bf"},{"x":4,"y":30,"fill":"#0043bf"},{"x":4,"y":29,"fill":"#0043bf"},{"x":4,"y":28,"fill":"#0043bf"},{"x":4,"y":27,"fill":"#0043bf"},{"x":4,"y":26,"fill":"#0043bf"},{"x":4,"y":25,"fill":"#0043bf"},{"x":4,"y":24,"fill":"#0043bf"},{"x":4,"y":23,"fill":"#0043bf"},{"x":4,"y":22,"fill":"#0043bf"},{"x":4,"y":21,"fill":"#0043bf"},{"x":4,"y":20,"fill":"#0043bf"},{"x":4,"y":19,"fill":"#0043bf"},{"x":4,"y":18,"fill":"#0043bf"},{"x":4,"y":17,"fill":"#0043bf"},{"x":4,"y":16,"fill":"#99b2d1"},{"x":4,"y":15,"fill":"#99b2d1"},{"x":4,"y":14,"fill":"#ced6d5"},{"x":4,"y":13,"fill":"#ced6d5"},{"x":4,"y":12,"fill":"#e0e1d4"},{"x":4,"y":11,"fill":"#e0e1d4"},{"x":4,"y":10,"fill":"#e0e1d4"},{"x":4,"y":9,"fill":"#e0e1d4"},{"x":4,"y":8,"fill":"#e0e1d4"},{"x":4,"y":7,"fill":"#e0e1d4"},{"x":4,"y":6,"fill":"#e0e1d4"},{"x":4,"y":5,"fill":"#e0e1d4"},{"x":4,"y":4,"fill":"#e0e1d4"},{"x":4,"y":3,"fill":"#e0e1d4"},{"x":4,"y":2,"fill":"#e0e1d4"},{"x":4,"y":1,"fill":"#e0e1d4"},{"x":5,"y":64,"fill":"#0043bf"},{"x":5,"y":63,"fill":"#0043bf"},{"x":5,"y":62,"fill":"#0043bf"},{"x":5,"y":61,"fill":"#0043bf"},{"x":5,"y":60,"fill":"#0043bf"},{"x":5,"y":59,"fill":"#0043bf"},{"x":5,"y":58,"fill":"#0043bf"},{"x":5,"y":57,"fill":"#0043bf"},{"x":5,"y":56,"fill":"#0043bf"},{"x":5,"y":55,"fill":"#0043bf"},{"x":5,"y":54,"fill":"#0043bf"},{"x":5,"y":53,"fill":"#0043bf"},{"x":5,"y":52,"fill":"#0043bf"},{"x":5,"y":51,"fill":"#0043bf"},{"x":5,"y":50,"fill":"#ffffff"},{"x":5,"y":49,"fill":"#ffffff"},{"x":5,"y":48,"fill":"#0043bf"},{"x":5,"y":47,"fill":"#0043bf"},{"x":5,"y":46,"fill":"#0043bf"},{"x":5,"y":45,"fill":"#0043bf"},{"x":5,"y":44,"fill":"#0043bf"},{"x":5,"y":43,"fill":"#0043bf"},{"x":5,"y":42,"fill":"#0043bf"},{"x":5,"y":41,"fill":"#0043bf"},{"x":5,"y":40,"fill":"#ffffff"},{"x":5,"y":39,"fill":"#ffffff"},{"x":5,"y":38,"fill":"#0043bf"},{"x":5,"y":37,"fill":"#0043bf"},{"x":5,"y":36,"fill":"#0043bf"},{"x":5,"y":35,"fill":"#0043bf"},{"x":5,"y":34,"fill":"#0043bf"},{"x":5,"y":33,"fill":"#0043bf"},{"x":5,"y":32,"fill":"#0043bf"},{"x":5,"y":31,"fill":"#0043bf"},{"x":5,"y":30,"fill":"#0043bf"},{"x":5,"y":29,"fill":"#0043bf"},{"x":5,"y":28,"fill":"#0043bf"},{"x":5,"y":27,"fill":"#0043bf"},{"x":5,"y":26,"fill":"#0043bf"},{"x":5,"y":25,"fill":"#0043bf"},{"x":5,"y":24,"fill":"#0043bf"},{"x":5,"y":23,"fill":"#0043bf"},{"x":5,"y":22,"fill":"#0043bf"},{"x":5,"y":21,"fill":"#0043bf"},{"x":5,"y":20,"fill":"#0043bf"},{"x":5,"y":19,"fill":"#0043bf"},{"x":5,"y":18,"fill":"#0043bf"},{"x":5,"y":17,"fill":"#0043bf"},{"x":5,"y":16,"fill":"#99b2d1"},{"x":5,"y":15,"fill":"#99b2d1"},{"x":5,"y":14,"fill":"#ced6d5"},{"x":5,"y":13,"fill":"#ced6d5"},{"x":5,"y":12,"fill":"#e0e1d4"},{"x":5,"y":11,"fill":"#e0e1d4"},{"x":5,"y":10,"fill":"#e0e1d4"},{"x":5,"y":9,"fill":"#e0e1d4"},{"x":5,"y":8,"fill":"#e0e1d4"},{"x":5,"y":7,"fill":"#e0e1d4"},{"x":5,"y":6,"fill":"#e0e1d4"},{"x":5,"y":5,"fill":"#e0e1d4"},{"x":5,"y":4,"fill":"#e0e1d4"},{"x":5,"y":3,"fill":"#e0e1d4"},{"x":5,"y":2,"fill":"#e0e1d4"},{"x":5,"y":1,"fill":"#e0e1d4"},{"x":6,"y":64,"fill":"#0043bf"},{"x":6,"y":63,"fill":"#0043bf"},{"x":6,"y":62,"fill":"#0043bf"},{"x":6,"y":61,"fill":"#0043bf"},{"x":6,"y":60,"fill":"#0043bf"},{"x":6,"y":59,"fill":"#0043bf"},{"x":6,"y":58,"fill":"#0043bf"},{"x":6,"y":57,"fill":"#0043bf"},{"x":6,"y":56,"fill":"#0043bf"},{"x":6,"y":55,"fill":"#0043bf"},{"x":6,"y":54,"fill":"#0043bf"},{"x":6,"y":53,"fill":"#0043bf"},{"x":6,"y":52,"fill":"#0043bf"},{"x":6,"y":51,"fill":"#0043bf"},{"x":6,"y":50,"fill":"#0043bf"},{"x":6,"y":49,"fill":"#0043bf"},{"x":6,"y":48,"fill":"#0043bf"},{"x":6,"y":47,"fill":"#0043bf"},{"x":6,"y":46,"fill":"#0043bf"},{"x":6,"y":45,"fill":"#0043bf"},{"x":6,"y":44,"fill":"#0043bf"},{"x":6,"y":43,"fill":"#0043bf"},{"x":6,"y":42,"fill":"#0043bf"},{"x":6,"y":41,"fill":"#0043bf"},{"x":6,"y":40,"fill":"#0043bf"},{"x":6,"y":39,"fill":"#0043bf"},{"x":6,"y":38,"fill":"#0043bf"},{"x":6,"y":37,"fill":"#0043bf"},{"x":6,"y":36,"fill":"#0043bf"},{"x":6,"y":35,"fill":"#0043bf"},{"x":6,"y":34,"fill":"#0043bf"},{"x":6,"y":33,"fill":"#0043bf"},{"x":6,"y":32,"fill":"#0043bf"},{"x":6,"y":31,"fill":"#0043bf"},{"x":6,"y":30,"fill":"#ffffff"},{"x":6,"y":29,"fill":"#ffffff"},{"x":6,"y":28,"fill":"#0043bf"},{"x":6,"y":27,"fill":"#0043bf"},{"x":6,"y":26,"fill":"#0043bf"},{"x":6,"y":25,"fill":"#0043bf"},{"x":6,"y":24,"fill":"#0043bf"},{"x":6,"y":23,"fill":"#0043bf"},{"x":6,"y":22,"fill":"#0043bf"},{"x":6,"y":21,"fill":"#0043bf"},{"x":6,"y":20,"fill":"#0043bf"},{"x":6,"y":19,"fill":"#0043bf"},{"x":6,"y":18,"fill":"#0043bf"},{"x":6,"y":17,"fill":"#0043bf"},{"x":6,"y":16,"fill":"#b7c7d4"},{"x":6,"y":15,"fill":"#b7c7d4"},{"x":6,"y":14,"fill":"#e0e1d4"},{"x":6,"y":13,"fill":"#e0e1d4"},{"x":6,"y":12,"fill":"#e0e1d4"},{"x":6,"y":11,"fill":"#e0e1d4"},{"x":6,"y":10,"fill":"#e0e1d4"},{"x":6,"y":9,"fill":"#e0e1d4"},{"x":6,"y":8,"fill":"#e0e1d4"},{"x":6,"y":7,"fill":"#e0e1d4"},{"x":6,"y":6,"fill":"#e0e1d4"},{"x":6,"y":5,"fill":"#e0e1d4"},{"x":6,"y":4,"fill":"#e0e1d4"},{"x":6,"y":3,"fill":"#e0e1d4"},{"x":6,"y":2,"fill":"#e0e1d4"},{"x":6,"y":1,"fill":"#e0e1d4"},{"x":7,"y":64,"fill":"#0043bf"},{"x":7,"y":63,"fill":"#0043bf"},{"x":7,"y":62,"fill":"#0043bf"},{"x":7,"y":61,"fill":"#0043bf"},{"x":7,"y":60,"fill":"#0043bf"},{"x":7,"y":59,"fill":"#0043bf"},{"x":7,"y":58,"fill":"#0043bf"},{"x":7,"y":57,"fill":"#0043bf"},{"x":7,"y":56,"fill":"#0043bf"},{"x":7,"y":55,"fill":"#0043bf"},{"x":7,"y":54,"fill":"#0043bf"},{"x":7,"y":53,"fill":"#0043bf"},{"x":7,"y":52,"fill":"#0043bf"},{"x":7,"y":51,"fill":"#0043bf"},{"x":7,"y":50,"fill":"#0043bf"},{"x":7,"y":49,"fill":"#0043bf"},{"x":7,"y":48,"fill":"#0043bf"},{"x":7,"y":47,"fill":"#0043bf"},{"x":7,"y":46,"fill":"#0043bf"},{"x":7,"y":45,"fill":"#0043bf"},{"x":7,"y":44,"fill":"#0043bf"},{"x":7,"y":43,"fill":"#0043bf"},{"x":7,"y":42,"fill":"#0043bf"},{"x":7,"y":41,"fill":"#0043bf"},{"x":7,"y":40,"fill":"#0043bf"},{"x":7,"y":39,"fill":"#0043bf"},{"x":7,"y":38,"fill":"#0043bf"},{"x":7,"y":37,"fill":"#0043bf"},{"x":7,"y":36,"fill":"#0043bf"},{"x":7,"y":35,"fill":"#0043bf"},{"x":7,"y":34,"fill":"#0043bf"},{"x":7,"y":33,"fill":"#0043bf"},{"x":7,"y":32,"fill":"#0043bf"},{"x":7,"y":31,"fill":"#0043bf"},{"x":7,"y":30,"fill":"#ffffff"},{"x":7,"y":29,"fill":"#ffffff"},{"x":7,"y":28,"fill":"#0043bf"},{"x":7,"y":27,"fill":"#0043bf"},{"x":7,"y":26,"fill":"#0043bf"},{"x":7,"y":25,"fill":"#0043bf"},{"x":7,"y":24,"fill":"#0043bf"},{"x":7,"y":23,"fill":"#0043bf"},{"x":7,"y":22,"fill":"#0043bf"},{"x":7,"y":21,"fill":"#0043bf"},{"x":7,"y":20,"fill":"#0043bf"},{"x":7,"y":19,"fill":"#0043bf"},{"x":7,"y":18,"fill":"#0043bf"},{"x":7,"y":17,"fill":"#0043bf"},{"x":7,"y":16,"fill":"#b7c7d4"},{"x":7,"y":15,"fill":"#b7c7d4"},{"x":7,"y":14,"fill":"#e0e1d4"},{"x":7,"y":13,"fill":"#e0e1d4"},{"x":7,"y":12,"fill":"#e0e1d4"},{"x":7,"y":11,"fill":"#e0e1d4"},{"x":7,"y":10,"fill":"#e0e1d4"},{"x":7,"y":9,"fill":"#e0e1d4"},{"x":7,"y":8,"fill":"#e0e1d4"},{"x":7,"y":7,"fill":"#e0e1d4"},{"x":7,"y":6,"fill":"#e0e1d4"},{"x":7,"y":5,"fill":"#e0e1d4"},{"x":7,"y":4,"fill":"#e0e1d4"},{"x":7,"y":3,"fill":"#e0e1d4"},{"x":7,"y":2,"fill":"#e0e1d4"},{"x":7,"y":1,"fill":"#e0e1d4"},{"x":8,"y":64,"fill":"#0043bf"},{"x":8,"y":63,"fill":"#0043bf"},{"x":8,"y":62,"fill":"#0043bf"},{"x":8,"y":61,"fill":"#0043bf"},{"x":8,"y":60,"fill":"#0043bf"},{"x":8,"y":59,"fill":"#0043bf"},{"x":8,"y":58,"fill":"#0043bf"},{"x":8,"y":57,"fill":"#0043bf"},{"x":8,"y":56,"fill":"#0043bf"},{"x":8,"y":55,"fill":"#0043bf"},{"x":8,"y":54,"fill":"#0043bf"},{"x":8,"y":53,"fill":"#0043bf"},{"x":8,"y":52,"fill":"#0043bf"},{"x":8,"y":51,"fill":"#0043bf"},{"x":8,"y":50,"fill":"#0043bf"},{"x":8,"y":49,"fill":"#0043bf"},{"x":8,"y":48,"fill":"#0043bf"},{"x":8,"y":47,"fill":"#0043bf"},{"x":8,"y":46,"fill":"#0043bf"},{"x":8,"y":45,"fill":"#0043bf"},{"x":8,"y":44,"fill":"#0043bf"},{"x":8,"y":43,"fill":"#0043bf"},{"x":8,"y":42,"fill":"#0043bf"},{"x":8,"y":41,"fill":"#0043bf"},{"x":8,"y":40,"fill":"#0043bf"},{"x":8,"y":39,"fill":"#0043bf"},{"x":8,"y":38,"fill":"#ffffff"},{"x":8,"y":37,"fill":"#ffffff"},{"x":8,"y":36,"fill":"#0043bf"},{"x":8,"y":35,"fill":"#0043bf"},{"x":8,"y":34,"fill":"#0043bf"},{"x":8,"y":33,"fill":"#0043bf"},{"x":8,"y":32,"fill":"#0043bf"},{"x":8,"y":31,"fill":"#0043bf"},{"x":8,"y":30,"fill":"#0043bf"},{"x":8,"y":29,"fill":"#0043bf"},{"x":8,"y":28,"fill":"#0043bf"},{"x":8,"y":27,"fill":"#0043bf"},{"x":8,"y":26,"fill":"#0043bf"},{"x":8,"y":25,"fill":"#0043bf"},{"x":8,"y":24,"fill":"#0043bf"},{"x":8,"y":23,"fill":"#0043bf"},{"x":8,"y":22,"fill":"#0043bf"},{"x":8,"y":21,"fill":"#0043bf"},{"x":8,"y":20,"fill":"#0043bf"},{"x":8,"y":19,"fill":"#0043bf"},{"x":8,"y":18,"fill":"#0043bf"},{"x":8,"y":17,"fill":"#0043bf"},{"x":8,"y":16,"fill":"#b3c4d3"},{"x":8,"y":15,"fill":"#b3c4d3"},{"x":8,"y":14,"fill":"#e0e1d4"},{"x":8,"y":13,"fill":"#e0e1d4"},{"x":8,"y":12,"fill":"#e0e1d4"},{"x":8,"y":11,"fill":"#e0e1d4"},{"x":8,"y":10,"fill":"#e0e1d4"},{"x":8,"y":9,"fill":"#e0e1d4"},{"x":8,"y":8,"fill":"#e0e1d4"},{"x":8,"y":7,"fill":"#e0e1d4"},{"x":8,"y":6,"fill":"#e0e1d4"},{"x":8,"y":5,"fill":"#e0e1d4"},{"x":8,"y":4,"fill":"#e0e1d4"},{"x":8,"y":3,"fill":"#e0e1d4"},{"x":8,"y":2,"fill":"#e0e1d4"},{"x":8,"y":1,"fill":"#e0e1d4"},{"x":9,"y":64,"fill":"#0043bf"},{"x":9,"y":63,"fill":"#0043bf"},{"x":9,"y":62,"fill":"#0043bf"},{"x":9,"y":61,"fill":"#0043bf"},{"x":9,"y":60,"fill":"#0043bf"},{"x":9,"y":59,"fill":"#0043bf"},{"x":9,"y":58,"fill":"#0043bf"},{"x":9,"y":57,"fill":"#0043bf"},{"x":9,"y":56,"fill":"#0043bf"},{"x":9,"y":55,"fill":"#0043bf"},{"x":9,"y":54,"fill":"#0043bf"},{"x":9,"y":53,"fill":"#0043bf"},{"x":9,"y":52,"fill":"#0043bf"},{"x":9,"y":51,"fill":"#0043bf"},{"x":9,"y":50,"fill":"#0043bf"},{"x":9,"y":49,"fill":"#0043bf"},{"x":9,"y":48,"fill":"#0043bf"},{"x":9,"y":47,"fill":"#0043bf"},{"x":9,"y":46,"fill":"#0043bf"},{"x":9,"y":45,"fill":"#0043bf"},{"x":9,"y":44,"fill":"#0043bf"},{"x":9,"y":43,"fill":"#0043bf"},{"x":9,"y":42,"fill":"#0043bf"},{"x":9,"y":41,"fill":"#0043bf"},{"x":9,"y":40,"fill":"#0043bf"},{"x":9,"y":39,"fill":"#0043bf"},{"x":9,"y":38,"fill":"#ffffff"},{"x":9,"y":37,"fill":"#ffffff"},{"x":9,"y":36,"fill":"#0043bf"},{"x":9,"y":35,"fill":"#0043bf"},{"x":9,"y":34,"fill":"#0043bf"},{"x":9,"y":33,"fill":"#0043bf"},{"x":9,"y":32,"fill":"#0043bf"},{"x":9,"y":31,"fill":"#0043bf"},{"x":9,"y":30,"fill":"#0043bf"},{"x":9,"y":29,"fill":"#0043bf"},{"x":9,"y":28,"fill":"#0043bf"},{"x":9,"y":27,"fill":"#0043bf"},{"x":9,"y":26,"fill":"#0043bf"},{"x":9,"y":25,"fill":"#0043bf"},{"x":9,"y":24,"fill":"#0043bf"},{"x":9,"y":23,"fill":"#0043bf"},{"x":9,"y":22,"fill":"#0043bf"},{"x":9,"y":21,"fill":"#0043bf"},{"x":9,"y":20,"fill":"#0043bf"},{"x":9,"y":19,"fill":"#0043bf"},{"x":9,"y":18,"fill":"#0043bf"},{"x":9,"y":17,"fill":"#0043bf"},{"x":9,"y":16,"fill":"#b3c4d3"},{"x":9,"y":15,"fill":"#b3c4d3"},{"x":9,"y":14,"fill":"#e0e1d4"},{"x":9,"y":13,"fill":"#e0e1d4"},{"x":9,"y":12,"fill":"#e0e1d4"},{"x":9,"y":11,"fill":"#e0e1d4"},{"x":9,"y":10,"fill":"#e0e1d4"},{"x":9,"y":9,"fill":"#e0e1d4"},{"x":9,"y":8,"fill":"#e0e1d4"},{"x":9,"y":7,"fill":"#e0e1d4"},{"x":9,"y":6,"fill":"#e0e1d4"},{"x":9,"y":5,"fill":"#e0e1d4"},{"x":9,"y":4,"fill":"#e0e1d4"},{"x":9,"y":3,"fill":"#e0e1d4"},{"x":9,"y":2,"fill":"#e0e1d4"},{"x":9,"y":1,"fill":"#e0e1d4"},{"x":10,"y":64,"fill":"#0043bf"},{"x":10,"y":63,"fill":"#0043bf"},{"x":10,"y":62,"fill":"#0043bf"},{"x":10,"y":61,"fill":"#0043bf"},{"x":10,"y":60,"fill":"#ffffff"},{"x":10,"y":59,"fill":"#ffffff"},{"x":10,"y":58,"fill":"#0043bf"},{"x":10,"y":57,"fill":"#0043bf"},{"x":10,"y":56,"fill":"#0043bf"},{"x":10,"y":55,"fill":"#0043bf"},{"x":10,"y":54,"fill":"#0043bf"},{"x":10,"y":53,"fill":"#0043bf"},{"x":10,"y":52,"fill":"#0043bf"},{"x":10,"y":51,"fill":"#0043bf"},{"x":10,"y":50,"fill":"#0043bf"},{"x":10,"y":49,"fill":"#0043bf"},{"x":10,"y":48,"fill":"#0043bf"},{"x":10,"y":47,"fill":"#0043bf"},{"x":10,"y":46,"fill":"#0043bf"},{"x":10,"y":45,"fill":"#0043bf"},{"x":10,"y":44,"fill":"#0043bf"},{"x":10,"y":43,"fill":"#0043bf"},{"x":10,"y":42,"fill":"#0043bf"},{"x":10,"y":41,"fill":"#0043bf"},{"x":10,"y":40,"fill":"#0043bf"},{"x":10,"y":39,"fill":"#0043bf"},{"x":10,"y":38,"fill":"#0043bf"},{"x":10,"y":37,"fill":"#0043bf"},{"x":10,"y":36,"fill":"#0043bf"},{"x":10,"y":35,"fill":"#0043bf"},{"x":10,"y":34,"fill":"#0043bf"},{"x":10,"y":33,"fill":"#0043bf"},{"x":10,"y":32,"fill":"#0043bf"},{"x":10,"y":31,"fill":"#0043bf"},{"x":10,"y":30,"fill":"#0043bf"},{"x":10,"y":29,"fill":"#0043bf"},{"x":10,"y":28,"fill":"#0043bf"},{"x":10,"y":27,"fill":"#0043bf"},{"x":10,"y":26,"fill":"#0043bf"},{"x":10,"y":25,"fill":"#0043bf"},{"x":10,"y":24,"fill":"#0043bf"},{"x":10,"y":23,"fill":"#0043bf"},{"x":10,"y":22,"fill":"#0043bf"},{"x":10,"y":21,"fill":"#0043bf"},{"x":10,"y":20,"fill":"#0043bf"},{"x":10,"y":19,"fill":"#0043bf"},{"x":10,"y":18,"fill":"#99b2d1"},{"x":10,"y":17,"fill":"#99b2d1"},{"x":10,"y":16,"fill":"#ced6d5"},{"x":10,"y":15,"fill":"#ced6d5"},{"x":10,"y":14,"fill":"#e0e1d4"},{"x":10,"y":13,"fill":"#e0e1d4"},{"x":10,"y":12,"fill":"#e0e1d4"},{"x":10,"y":11,"fill":"#e0e1d4"},{"x":10,"y":10,"fill":"#e0e1d4"},{"x":10,"y":9,"fill":"#e0e1d4"},{"x":10,"y":8,"fill":"#e0e1d4"},{"x":10,"y":7,"fill":"#e0e1d4"},{"x":10,"y":6,"fill":"#e0e1d4"},{"x":10,"y":5,"fill":"#e0e1d4"},{"x":10,"y":4,"fill":"#e0e1d4"},{"x":10,"y":3,"fill":"#e0e1d4"},{"x":10,"y":2,"fill":"#e0e1d4"},{"x":10,"y":1,"fill":"#e0e1d4"},{"x":11,"y":64,"fill":"#0043bf"},{"x":11,"y":63,"fill":"#0043bf"},{"x":11,"y":62,"fill":"#0043bf"},{"x":11,"y":61,"fill":"#0043bf"},{"x":11,"y":60,"fill":"#ffffff"},{"x":11,"y":59,"fill":"#ffffff"},{"x":11,"y":58,"fill":"#0043bf"},{"x":11,"y":57,"fill":"#0043bf"},{"x":11,"y":56,"fill":"#0043bf"},{"x":11,"y":55,"fill":"#0043bf"},{"x":11,"y":54,"fill":"#0043bf"},{"x":11,"y":53,"fill":"#0043bf"},{"x":11,"y":52,"fill":"#0043bf"},{"x":11,"y":51,"fill":"#0043bf"},{"x":11,"y":50,"fill":"#0043bf"},{"x":11,"y":49,"fill":"#0043bf"},{"x":11,"y":48,"fill":"#0043bf"},{"x":11,"y":47,"fill":"#0043bf"},{"x":11,"y":46,"fill":"#0043bf"},{"x":11,"y":45,"fill":"#0043bf"},{"x":11,"y":44,"fill":"#0043bf"},{"x":11,"y":43,"fill":"#0043bf"},{"x":11,"y":42,"fill":"#0043bf"},{"x":11,"y":41,"fill":"#0043bf"},{"x":11,"y":40,"fill":"#0043bf"},{"x":11,"y":39,"fill":"#0043bf"},{"x":11,"y":38,"fill":"#0043bf"},{"x":11,"y":37,"fill":"#0043bf"},{"x":11,"y":36,"fill":"#0043bf"},{"x":11,"y":35,"fill":"#0043bf"},{"x":11,"y":34,"fill":"#0043bf"},{"x":11,"y":33,"fill":"#0043bf"},{"x":11,"y":32,"fill":"#0043bf"},{"x":11,"y":31,"fill":"#0043bf"},{"x":11,"y":30,"fill":"#0043bf"},{"x":11,"y":29,"fill":"#0043bf"},{"x":11,"y":28,"fill":"#0043bf"},{"x":11,"y":27,"fill":"#0043bf"},{"x":11,"y":26,"fill":"#0043bf"},{"x":11,"y":25,"fill":"#0043bf"},{"x":11,"y":24,"fill":"#0043bf"},{"x":11,"y":23,"fill":"#0043bf"},{"x":11,"y":22,"fill":"#0043bf"},{"x":11,"y":21,"fill":"#0043bf"},{"x":11,"y":20,"fill":"#0043bf"},{"x":11,"y":19,"fill":"#0043bf"},{"x":11,"y":18,"fill":"#99b2d1"},{"x":11,"y":17,"fill":"#99b2d1"},{"x":11,"y":16,"fill":"#ced6d5"},{"x":11,"y":15,"fill":"#ced6d5"},{"x":11,"y":14,"fill":"#e0e1d4"},{"x":11,"y":13,"fill":"#e0e1d4"},{"x":11,"y":12,"fill":"#e0e1d4"},{"x":11,"y":11,"fill":"#e0e1d4"},{"x":11,"y":10,"fill":"#e0e1d4"},{"x":11,"y":9,"fill":"#e0e1d4"},{"x":11,"y":8,"fill":"#e0e1d4"},{"x":11,"y":7,"fill":"#e0e1d4"},{"x":11,"y":6,"fill":"#e0e1d4"},{"x":11,"y":5,"fill":"#e0e1d4"},{"x":11,"y":4,"fill":"#e0e1d4"},{"x":11,"y":3,"fill":"#e0e1d4"},{"x":11,"y":2,"fill":"#e0e1d4"},{"x":11,"y":1,"fill":"#e0e1d4"},{"x":12,"y":64,"fill":"#0043bf"},{"x":12,"y":63,"fill":"#0043bf"},{"x":12,"y":62,"fill":"#0043bf"},{"x":12,"y":61,"fill":"#0043bf"},{"x":12,"y":60,"fill":"#0043bf"},{"x":12,"y":59,"fill":"#0043bf"},{"x":12,"y":58,"fill":"#0043bf"},{"x":12,"y":57,"fill":"#0043bf"},{"x":12,"y":56,"fill":"#0043bf"},{"x":12,"y":55,"fill":"#0043bf"},{"x":12,"y":54,"fill":"#0043bf"},{"x":12,"y":53,"fill":"#0043bf"},{"x":12,"y":52,"fill":"#0043bf"},{"x":12,"y":51,"fill":"#0043bf"},{"x":12,"y":50,"fill":"#0043bf"},{"x":12,"y":49,"fill":"#0043bf"},{"x":12,"y":48,"fill":"#0043bf"},{"x":12,"y":47,"fill":"#0043bf"},{"x":12,"y":46,"fill":"#0043bf"},{"x":12,"y":45,"fill":"#0043bf"},{"x":12,"y":44,"fill":"#0043bf"},{"x":12,"y":43,"fill":"#0043bf"},{"x":12,"y":42,"fill":"#0043bf"},{"x":12,"y":41,"fill":"#0043bf"},{"x":12,"y":40,"fill":"#0043bf"},{"x":12,"y":39,"fill":"#0043bf"},{"x":12,"y":38,"fill":"#0043bf"},{"x":12,"y":37,"fill":"#0043bf"},{"x":12,"y":36,"fill":"#0043bf"},{"x":12,"y":35,"fill":"#0043bf"},{"x":12,"y":34,"fill":"#0043bf"},{"x":12,"y":33,"fill":"#0043bf"},{"x":12,"y":32,"fill":"#0043bf"},{"x":12,"y":31,"fill":"#0043bf"},{"x":12,"y":30,"fill":"#0043bf"},{"x":12,"y":29,"fill":"#0043bf"},{"x":12,"y":28,"fill":"#0043bf"},{"x":12,"y":27,"fill":"#0043bf"},{"x":12,"y":26,"fill":"#0043bf"},{"x":12,"y":25,"fill":"#0043bf"},{"x":12,"y":24,"fill":"#0043bf"},{"x":12,"y":23,"fill":"#0043bf"},{"x":12,"y":22,"fill":"#0043bf"},{"x":12,"y":21,"fill":"#0043bf"},{"x":12,"y":20,"fill":"#0043bf"},{"x":12,"y":19,"fill":"#0043bf"},{"x":12,"y":18,"fill":"#b7c7d4"},{"x":12,"y":17,"fill":"#b7c7d4"},{"x":12,"y":16,"fill":"#e0e1d4"},{"x":12,"y":15,"fill":"#e0e1d4"},{"x":12,"y":14,"fill":"#e0e1d4"},{"x":12,"y":13,"fill":"#e0e1d4"},{"x":12,"y":12,"fill":"#e0e1d4"},{"x":12,"y":11,"fill":"#e0e1d4"},{"x":12,"y":10,"fill":"#e0e1d4"},{"x":12,"y":9,"fill":"#e0e1d4"},{"x":12,"y":8,"fill":"#e0e1d4"},{"x":12,"y":7,"fill":"#e0e1d4"},{"x":12,"y":6,"fill":"#e0e1d4"},{"x":12,"y":5,"fill":"#e0e1d4"},{"x":12,"y":4,"fill":"#e0e1d4"},{"x":12,"y":3,"fill":"#e0e1d4"},{"x":12,"y":2,"fill":"#e0e1d4"},{"x":12,"y":1,"fill":"#e0e1d4"},{"x":13,"y":64,"fill":"#0043bf"},{"x":13,"y":63,"fill":"#0043bf"},{"x":13,"y":62,"fill":"#0043bf"},{"x":13,"y":61,"fill":"#0043bf"},{"x":13,"y":60,"fill":"#0043bf"},{"x":13,"y":59,"fill":"#0043bf"},{"x":13,"y":58,"fill":"#0043bf"},{"x":13,"y":57,"fill":"#0043bf"},{"x":13,"y":56,"fill":"#0043bf"},{"x":13,"y":55,"fill":"#0043bf"},{"x":13,"y":54,"fill":"#0043bf"},{"x":13,"y":53,"fill":"#0043bf"},{"x":13,"y":52,"fill":"#0043bf"},{"x":13,"y":51,"fill":"#0043bf"},{"x":13,"y":50,"fill":"#0043bf"},{"x":13,"y":49,"fill":"#0043bf"},{"x":13,"y":48,"fill":"#0043bf"},{"x":13,"y":47,"fill":"#0043bf"},{"x":13,"y":46,"fill":"#0043bf"},{"x":13,"y":45,"fill":"#0043bf"},{"x":13,"y":44,"fill":"#0043bf"},{"x":13,"y":43,"fill":"#0043bf"},{"x":13,"y":42,"fill":"#0043bf"},{"x":13,"y":41,"fill":"#0043bf"},{"x":13,"y":40,"fill":"#0043bf"},{"x":13,"y":39,"fill":"#0043bf"},{"x":13,"y":38,"fill":"#0043bf"},{"x":13,"y":37,"fill":"#0043bf"},{"x":13,"y":36,"fill":"#0043bf"},{"x":13,"y":35,"fill":"#0043bf"},{"x":13,"y":34,"fill":"#0043bf"},{"x":13,"y":33,"fill":"#0043bf"},{"x":13,"y":32,"fill":"#0043bf"},{"x":13,"y":31,"fill":"#0043bf"},{"x":13,"y":30,"fill":"#0043bf"},{"x":13,"y":29,"fill":"#0043bf"},{"x":13,"y":28,"fill":"#0043bf"},{"x":13,"y":27,"fill":"#0043bf"},{"x":13,"y":26,"fill":"#0043bf"},{"x":13,"y":25,"fill":"#0043bf"},{"x":13,"y":24,"fill":"#0043bf"},{"x":13,"y":23,"fill":"#0043bf"},{"x":13,"y":22,"fill":"#0043bf"},{"x":13,"y":21,"fill":"#0043bf"},{"x":13,"y":20,"fill":"#0043bf"},{"x":13,"y":19,"fill":"#0043bf"},{"x":13,"y":18,"fill":"#b7c7d4"},{"x":13,"y":17,"fill":"#b7c7d4"},{"x":13,"y":16,"fill":"#e0e1d4"},{"x":13,"y":15,"fill":"#e0e1d4"},{"x":13,"y":14,"fill":"#e0e1d4"},{"x":13,"y":13,"fill":"#e0e1d4"},{"x":13,"y":12,"fill":"#e0e1d4"},{"x":13,"y":11,"fill":"#e0e1d4"},{"x":13,"y":10,"fill":"#e0e1d4"},{"x":13,"y":9,"fill":"#e0e1d4"},{"x":13,"y":8,"fill":"#e0e1d4"},{"x":13,"y":7,"fill":"#e0e1d4"},{"x":13,"y":6,"fill":"#e0e1d4"},{"x":13,"y":5,"fill":"#e0e1d4"},{"x":13,"y":4,"fill":"#e0e1d4"},{"x":13,"y":3,"fill":"#e0e1d4"},{"x":13,"y":2,"fill":"#e0e1d4"},{"x":13,"y":1,"fill":"#e0e1d4"},{"x":14,"y":64,"fill":"#0043bf"},{"x":14,"y":63,"fill":"#0043bf"},{"x":14,"y":62,"fill":"#0043bf"},{"x":14,"y":61,"fill":"#0043bf"},{"x":14,"y":60,"fill":"#0043bf"},{"x":14,"y":59,"fill":"#0043bf"},{"x":14,"y":58,"fill":"#0043bf"},{"x":14,"y":57,"fill":"#0043bf"},{"x":14,"y":56,"fill":"#0043bf"},{"x":14,"y":55,"fill":"#0043bf"},{"x":14,"y":54,"fill":"#0043bf"},{"x":14,"y":53,"fill":"#0043bf"},{"x":14,"y":52,"fill":"#0043bf"},{"x":14,"y":51,"fill":"#0043bf"},{"x":14,"y":50,"fill":"#0043bf"},{"x":14,"y":49,"fill":"#0043bf"},{"x":14,"y":48,"fill":"#0043bf"},{"x":14,"y":47,"fill":"#0043bf"},{"x":14,"y":46,"fill":"#ffffff"},{"x":14,"y":45,"fill":"#ffffff"},{"x":14,"y":44,"fill":"#0043bf"},{"x":14,"y":43,"fill":"#0043bf"},{"x":14,"y":42,"fill":"#0043bf"},{"x":14,"y":41,"fill":"#0043bf"},{"x":14,"y":40,"fill":"#0043bf"},{"x":14,"y":39,"fill":"#0043bf"},{"x":14,"y":38,"fill":"#0043bf"},{"x":14,"y":37,"fill":"#0043bf"},{"x":14,"y":36,"fill":"#0043bf"},{"x":14,"y":35,"fill":"#0043bf"},{"x":14,"y":34,"fill":"#0043bf"},{"x":14,"y":33,"fill":"#0043bf"},{"x":14,"y":32,"fill":"#0043bf"},{"x":14,"y":31,"fill":"#0043bf"},{"x":14,"y":30,"fill":"#0043bf"},{"x":14,"y":29,"fill":"#0043bf"},{"x":14,"y":28,"fill":"#0043bf"},{"x":14,"y":27,"fill":"#0043bf"},{"x":14,"y":26,"fill":"#0043bf"},{"x":14,"y":25,"fill":"#0043bf"},{"x":14,"y":24,"fill":"#0043bf"},{"x":14,"y":23,"fill":"#0043bf"},{"x":14,"y":22,"fill":"#0043bf"},{"x":14,"y":21,"fill":"#0043bf"},{"x":14,"y":20,"fill":"#0043bf"},{"x":14,"y":19,"fill":"#0043bf"},{"x":14,"y":18,"fill":"#b3c4d3"},{"x":14,"y":17,"fill":"#b3c4d3"},{"x":14,"y":16,"fill":"#000000"},{"x":14,"y":15,"fill":"#000000"},{"x":14,"y":14,"fill":"#000000"},{"x":14,"y":13,"fill":"#000000"},{"x":14,"y":12,"fill":"#e0e1d4"},{"x":14,"y":11,"fill":"#e0e1d4"},{"x":14,"y":10,"fill":"#e0e1d4"},{"x":14,"y":9,"fill":"#e0e1d4"},{"x":14,"y":8,"fill":"#e0e1d4"},{"x":14,"y":7,"fill":"#e0e1d4"},{"x":14,"y":6,"fill":"#e0e1d4"},{"x":14,"y":5,"fill":"#e0e1d4"},{"x":14,"y":4,"fill":"#e0e1d4"},{"x":14,"y":3,"fill":"#e0e1d4"},{"x":14,"y":2,"fill":"#e0e1d4"},{"x":14,"y":1,"fill":"#e0e1d4"},{"x":15,"y":64,"fill":"#0043bf"},{"x":15,"y":63,"fill":"#0043bf"},{"x":15,"y":62,"fill":"#0043bf"},{"x":15,"y":61,"fill":"#0043bf"},{"x":15,"y":60,"fill":"#0043bf"},{"x":15,"y":59,"fill":"#0043bf"},{"x":15,"y":58,"fill":"#0043bf"},{"x":15,"y":57,"fill":"#0043bf"},{"x":15,"y":56,"fill":"#0043bf"},{"x":15,"y":55,"fill":"#0043bf"},{"x":15,"y":54,"fill":"#0043bf"},{"x":15,"y":53,"fill":"#0043bf"},{"x":15,"y":52,"fill":"#0043bf"},{"x":15,"y":51,"fill":"#0043bf"},{"x":15,"y":50,"fill":"#0043bf"},{"x":15,"y":49,"fill":"#0043bf"},{"x":15,"y":48,"fill":"#0043bf"},{"x":15,"y":47,"fill":"#0043bf"},{"x":15,"y":46,"fill":"#ffffff"},{"x":15,"y":45,"fill":"#ffffff"},{"x":15,"y":44,"fill":"#0043bf"},{"x":15,"y":43,"fill":"#0043bf"},{"x":15,"y":42,"fill":"#0043bf"},{"x":15,"y":41,"fill":"#0043bf"},{"x":15,"y":40,"fill":"#0043bf"},{"x":15,"y":39,"fill":"#0043bf"},{"x":15,"y":38,"fill":"#0043bf"},{"x":15,"y":37,"fill":"#0043bf"},{"x":15,"y":36,"fill":"#0043bf"},{"x":15,"y":35,"fill":"#0043bf"},{"x":15,"y":34,"fill":"#0043bf"},{"x":15,"y":33,"fill":"#0043bf"},{"x":15,"y":32,"fill":"#0043bf"},{"x":15,"y":31,"fill":"#0043bf"},{"x":15,"y":30,"fill":"#0043bf"},{"x":15,"y":29,"fill":"#0043bf"},{"x":15,"y":28,"fill":"#0043bf"},{"x":15,"y":27,"fill":"#0043bf"},{"x":15,"y":26,"fill":"#0043bf"},{"x":15,"y":25,"fill":"#0043bf"},{"x":15,"y":24,"fill":"#0043bf"},{"x":15,"y":23,"fill":"#0043bf"},{"x":15,"y":22,"fill":"#0043bf"},{"x":15,"y":21,"fill":"#0043bf"},{"x":15,"y":20,"fill":"#0043bf"},{"x":15,"y":19,"fill":"#0043bf"},{"x":15,"y":18,"fill":"#b3c4d3"},{"x":15,"y":17,"fill":"#b3c4d3"},{"x":15,"y":16,"fill":"#000000"},{"x":15,"y":15,"fill":"#000000"},{"x":15,"y":14,"fill":"#000000"},{"x":15,"y":13,"fill":"#000000"},{"x":15,"y":12,"fill":"#e0e1d4"},{"x":15,"y":11,"fill":"#e0e1d4"},{"x":15,"y":10,"fill":"#e0e1d4"},{"x":15,"y":9,"fill":"#e0e1d4"},{"x":15,"y":8,"fill":"#e0e1d4"},{"x":15,"y":7,"fill":"#e0e1d4"},{"x":15,"y":6,"fill":"#e0e1d4"},{"x":15,"y":5,"fill":"#e0e1d4"},{"x":15,"y":4,"fill":"#e0e1d4"},{"x":15,"y":3,"fill":"#e0e1d4"},{"x":15,"y":2,"fill":"#e0e1d4"},{"x":15,"y":1,"fill":"#e0e1d4"},{"x":16,"y":64,"fill":"#0043bf"},{"x":16,"y":63,"fill":"#0043bf"},{"x":16,"y":62,"fill":"#0043bf"},{"x":16,"y":61,"fill":"#0043bf"},{"x":16,"y":60,"fill":"#0043bf"},{"x":16,"y":59,"fill":"#0043bf"},{"x":16,"y":58,"fill":"#0043bf"},{"x":16,"y":57,"fill":"#0043bf"},{"x":16,"y":56,"fill":"#0043bf"},{"x":16,"y":55,"fill":"#0043bf"},{"x":16,"y":54,"fill":"#0043bf"},{"x":16,"y":53,"fill":"#0043bf"},{"x":16,"y":52,"fill":"#0043bf"},{"x":16,"y":51,"fill":"#0043bf"},{"x":16,"y":50,"fill":"#0043bf"},{"x":16,"y":49,"fill":"#0043bf"},{"x":16,"y":48,"fill":"#0043bf"},{"x":16,"y":47,"fill":"#0043bf"},{"x":16,"y":46,"fill":"#0043bf"},{"x":16,"y":45,"fill":"#0043bf"},{"x":16,"y":44,"fill":"#0043bf"},{"x":16,"y":43,"fill":"#0043bf"},{"x":16,"y":42,"fill":"#000000"},{"x":16,"y":41,"fill":"#000000"},{"x":16,"y":40,"fill":"#000000"},{"x":16,"y":39,"fill":"#000000"},{"x":16,"y":38,"fill":"#0043bf"},{"x":16,"y":37,"fill":"#0043bf"},{"x":16,"y":36,"fill":"#0043bf"},{"x":16,"y":35,"fill":"#0043bf"},{"x":16,"y":34,"fill":"#0043bf"},{"x":16,"y":33,"fill":"#0043bf"},{"x":16,"y":32,"fill":"#000000"},{"x":16,"y":31,"fill":"#000000"},{"x":16,"y":30,"fill":"#000000"},{"x":16,"y":29,"fill":"#000000"},{"x":16,"y":28,"fill":"#0043bf"},{"x":16,"y":27,"fill":"#0043bf"},{"x":16,"y":26,"fill":"#0043bf"},{"x":16,"y":25,"fill":"#0043bf"},{"x":16,"y":24,"fill":"#0043bf"},{"x":16,"y":23,"fill":"#0043bf"},{"x":16,"y":22,"fill":"#0043bf"},{"x":16,"y":21,"fill":"#0043bf"},{"x":16,"y":20,"fill":"#000000"},{"x":16,"y":19,"fill":"#000000"},{"x":16,"y":18,"fill":"#000000"},{"x":16,"y":17,"fill":"#000000"},{"x":16,"y":16,"fill":"#dadada"},{"x":16,"y":15,"fill":"#dadada"},{"x":16,"y":14,"fill":"#dadada"},{"x":16,"y":13,"fill":"#dadada"},{"x":16,"y":12,"fill":"#000000"},{"x":16,"y":11,"fill":"#000000"},{"x":16,"y":10,"fill":"#e0e1d4"},{"x":16,"y":9,"fill":"#e0e1d4"},{"x":16,"y":8,"fill":"#e0e1d4"},{"x":16,"y":7,"fill":"#e0e1d4"},{"x":16,"y":6,"fill":"#e0e1d4"},{"x":16,"y":5,"fill":"#e0e1d4"},{"x":16,"y":4,"fill":"#e0e1d4"},{"x":16,"y":3,"fill":"#e0e1d4"},{"x":16,"y":2,"fill":"#e0e1d4"},{"x":16,"y":1,"fill":"#e0e1d4"},{"x":17,"y":64,"fill":"#0043bf"},{"x":17,"y":63,"fill":"#0043bf"},{"x":17,"y":62,"fill":"#0043bf"},{"x":17,"y":61,"fill":"#0043bf"},{"x":17,"y":60,"fill":"#0043bf"},{"x":17,"y":59,"fill":"#0043bf"},{"x":17,"y":58,"fill":"#0043bf"},{"x":17,"y":57,"fill":"#0043bf"},{"x":17,"y":56,"fill":"#0043bf"},{"x":17,"y":55,"fill":"#0043bf"},{"x":17,"y":54,"fill":"#0043bf"},{"x":17,"y":53,"fill":"#0043bf"},{"x":17,"y":52,"fill":"#0043bf"},{"x":17,"y":51,"fill":"#0043bf"},{"x":17,"y":50,"fill":"#0043bf"},{"x":17,"y":49,"fill":"#0043bf"},{"x":17,"y":48,"fill":"#0043bf"},{"x":17,"y":47,"fill":"#0043bf"},{"x":17,"y":46,"fill":"#0043bf"},{"x":17,"y":45,"fill":"#0043bf"},{"x":17,"y":44,"fill":"#0043bf"},{"x":17,"y":43,"fill":"#0043bf"},{"x":17,"y":42,"fill":"#000000"},{"x":17,"y":41,"fill":"#000000"},{"x":17,"y":40,"fill":"#000000"},{"x":17,"y":39,"fill":"#000000"},{"x":17,"y":38,"fill":"#0043bf"},{"x":17,"y":37,"fill":"#0043bf"},{"x":17,"y":36,"fill":"#0043bf"},{"x":17,"y":35,"fill":"#0043bf"},{"x":17,"y":34,"fill":"#0043bf"},{"x":17,"y":33,"fill":"#0043bf"},{"x":17,"y":32,"fill":"#000000"},{"x":17,"y":31,"fill":"#000000"},{"x":17,"y":30,"fill":"#000000"},{"x":17,"y":29,"fill":"#000000"},{"x":17,"y":28,"fill":"#0043bf"},{"x":17,"y":27,"fill":"#0043bf"},{"x":17,"y":26,"fill":"#0043bf"},{"x":17,"y":25,"fill":"#0043bf"},{"x":17,"y":24,"fill":"#0043bf"},{"x":17,"y":23,"fill":"#0043bf"},{"x":17,"y":22,"fill":"#0043bf"},{"x":17,"y":21,"fill":"#0043bf"},{"x":17,"y":20,"fill":"#000000"},{"x":17,"y":19,"fill":"#000000"},{"x":17,"y":18,"fill":"#000000"},{"x":17,"y":17,"fill":"#000000"},{"x":17,"y":16,"fill":"#dadada"},{"x":17,"y":15,"fill":"#dadada"},{"x":17,"y":14,"fill":"#dadada"},{"x":17,"y":13,"fill":"#dadada"},{"x":17,"y":12,"fill":"#000000"},{"x":17,"y":11,"fill":"#000000"},{"x":17,"y":10,"fill":"#e0e1d4"},{"x":17,"y":9,"fill":"#e0e1d4"},{"x":17,"y":8,"fill":"#e0e1d4"},{"x":17,"y":7,"fill":"#e0e1d4"},{"x":17,"y":6,"fill":"#e0e1d4"},{"x":17,"y":5,"fill":"#e0e1d4"},{"x":17,"y":4,"fill":"#e0e1d4"},{"x":17,"y":3,"fill":"#e0e1d4"},{"x":17,"y":2,"fill":"#e0e1d4"},{"x":17,"y":1,"fill":"#e0e1d4"},{"x":18,"y":64,"fill":"#ffffff"},{"x":18,"y":63,"fill":"#ffffff"},{"x":18,"y":62,"fill":"#0043bf"},{"x":18,"y":61,"fill":"#0043bf"},{"x":18,"y":60,"fill":"#0043bf"},{"x":18,"y":59,"fill":"#0043bf"},{"x":18,"y":58,"fill":"#0043bf"},{"x":18,"y":57,"fill":"#0043bf"},{"x":18,"y":56,"fill":"#0043bf"},{"x":18,"y":55,"fill":"#0043bf"},{"x":18,"y":54,"fill":"#0043bf"},{"x":18,"y":53,"fill":"#0043bf"},{"x":18,"y":52,"fill":"#0043bf"},{"x":18,"y":51,"fill":"#0043bf"},{"x":18,"y":50,"fill":"#0043bf"},{"x":18,"y":49,"fill":"#0043bf"},{"x":18,"y":48,"fill":"#0043bf"},{"x":18,"y":47,"fill":"#0043bf"},{"x":18,"y":46,"fill":"#0043bf"},{"x":18,"y":45,"fill":"#0043bf"},{"x":18,"y":44,"fill":"#000000"},{"x":18,"y":43,"fill":"#000000"},{"x":18,"y":42,"fill":"#dcde99"},{"x":18,"y":41,"fill":"#dcde99"},{"x":18,"y":40,"fill":"#dcde99"},{"x":18,"y":39,"fill":"#dcde99"},{"x":18,"y":38,"fill":"#000000"},{"x":18,"y":37,"fill":"#000000"},{"x":18,"y":36,"fill":"#000000"},{"x":18,"y":35,"fill":"#000000"},{"x":18,"y":34,"fill":"#000000"},{"x":18,"y":33,"fill":"#000000"},{"x":18,"y":32,"fill":"#dcde99"},{"x":18,"y":31,"fill":"#dcde99"},{"x":18,"y":30,"fill":"#dcde99"},{"x":18,"y":29,"fill":"#dcde99"},{"x":18,"y":28,"fill":"#000000"},{"x":18,"y":27,"fill":"#000000"},{"x":18,"y":26,"fill":"#000000"},{"x":18,"y":25,"fill":"#000000"},{"x":18,"y":24,"fill":"#ffffff"},{"x":18,"y":23,"fill":"#ffffff"},{"x":18,"y":22,"fill":"#000000"},{"x":18,"y":21,"fill":"#000000"},{"x":18,"y":20,"fill":"#ff0000"},{"x":18,"y":19,"fill":"#ff0000"},{"x":18,"y":18,"fill":"#ff0000"},{"x":18,"y":17,"fill":"#ff0000"},{"x":18,"y":16,"fill":"#ffffff"},{"x":18,"y":15,"fill":"#ffffff"},{"x":18,"y":14,"fill":"#dadada"},{"x":18,"y":13,"fill":"#dadada"},{"x":18,"y":12,"fill":"#000000"},{"x":18,"y":11,"fill":"#000000"},{"x":18,"y":10,"fill":"#e0e1d4"},{"x":18,"y":9,"fill":"#e0e1d4"},{"x":18,"y":8,"fill":"#e0e1d4"},{"x":18,"y":7,"fill":"#e0e1d4"},{"x":18,"y":6,"fill":"#e0e1d4"},{"x":18,"y":5,"fill":"#e0e1d4"},{"x":18,"y":4,"fill":"#e0e1d4"},{"x":18,"y":3,"fill":"#e0e1d4"},{"x":18,"y":2,"fill":"#e0e1d4"},{"x":18,"y":1,"fill":"#e0e1d4"},{"x":19,"y":64,"fill":"#ffffff"},{"x":19,"y":63,"fill":"#ffffff"},{"x":19,"y":62,"fill":"#0043bf"},{"x":19,"y":61,"fill":"#0043bf"},{"x":19,"y":60,"fill":"#0043bf"},{"x":19,"y":59,"fill":"#0043bf"},{"x":19,"y":58,"fill":"#0043bf"},{"x":19,"y":57,"fill":"#0043bf"},{"x":19,"y":56,"fill":"#0043bf"},{"x":19,"y":55,"fill":"#0043bf"},{"x":19,"y":54,"fill":"#0043bf"},{"x":19,"y":53,"fill":"#0043bf"},{"x":19,"y":52,"fill":"#0043bf"},{"x":19,"y":51,"fill":"#0043bf"},{"x":19,"y":50,"fill":"#0043bf"},{"x":19,"y":49,"fill":"#0043bf"},{"x":19,"y":48,"fill":"#0043bf"},{"x":19,"y":47,"fill":"#0043bf"},{"x":19,"y":46,"fill":"#0043bf"},{"x":19,"y":45,"fill":"#0043bf"},{"x":19,"y":44,"fill":"#000000"},{"x":19,"y":43,"fill":"#000000"},{"x":19,"y":42,"fill":"#dcde99"},{"x":19,"y":41,"fill":"#dcde99"},{"x":19,"y":40,"fill":"#dcde99"},{"x":19,"y":39,"fill":"#dcde99"},{"x":19,"y":38,"fill":"#000000"},{"x":19,"y":37,"fill":"#000000"},{"x":19,"y":36,"fill":"#000000"},{"x":19,"y":35,"fill":"#000000"},{"x":19,"y":34,"fill":"#000000"},{"x":19,"y":33,"fill":"#000000"},{"x":19,"y":32,"fill":"#dcde99"},{"x":19,"y":31,"fill":"#dcde99"},{"x":19,"y":30,"fill":"#dcde99"},{"x":19,"y":29,"fill":"#dcde99"},{"x":19,"y":28,"fill":"#000000"},{"x":19,"y":27,"fill":"#000000"},{"x":19,"y":26,"fill":"#000000"},{"x":19,"y":25,"fill":"#000000"},{"x":19,"y":24,"fill":"#ffffff"},{"x":19,"y":23,"fill":"#ffffff"},{"x":19,"y":22,"fill":"#000000"},{"x":19,"y":21,"fill":"#000000"},{"x":19,"y":20,"fill":"#ff0000"},{"x":19,"y":19,"fill":"#ff0000"},{"x":19,"y":18,"fill":"#ff0000"},{"x":19,"y":17,"fill":"#ff0000"},{"x":19,"y":16,"fill":"#ffffff"},{"x":19,"y":15,"fill":"#ffffff"},{"x":19,"y":14,"fill":"#dadada"},{"x":19,"y":13,"fill":"#dadada"},{"x":19,"y":12,"fill":"#000000"},{"x":19,"y":11,"fill":"#000000"},{"x":19,"y":10,"fill":"#e0e1d4"},{"x":19,"y":9,"fill":"#e0e1d4"},{"x":19,"y":8,"fill":"#e0e1d4"},{"x":19,"y":7,"fill":"#e0e1d4"},{"x":19,"y":6,"fill":"#e0e1d4"},{"x":19,"y":5,"fill":"#e0e1d4"},{"x":19,"y":4,"fill":"#e0e1d4"},{"x":19,"y":3,"fill":"#e0e1d4"},{"x":19,"y":2,"fill":"#e0e1d4"},{"x":19,"y":1,"fill":"#e0e1d4"},{"x":20,"y":64,"fill":"#0043bf"},{"x":20,"y":63,"fill":"#0043bf"},{"x":20,"y":62,"fill":"#0043bf"},{"x":20,"y":61,"fill":"#0043bf"},{"x":20,"y":60,"fill":"#0043bf"},{"x":20,"y":59,"fill":"#0043bf"},{"x":20,"y":58,"fill":"#0043bf"},{"x":20,"y":57,"fill":"#0043bf"},{"x":20,"y":56,"fill":"#0043bf"},{"x":20,"y":55,"fill":"#0043bf"},{"x":20,"y":54,"fill":"#0043bf"},{"x":20,"y":53,"fill":"#0043bf"},{"x":20,"y":52,"fill":"#0043bf"},{"x":20,"y":51,"fill":"#0043bf"},{"x":20,"y":50,"fill":"#0043bf"},{"x":20,"y":49,"fill":"#0043bf"},{"x":20,"y":48,"fill":"#000000"},{"x":20,"y":47,"fill":"#000000"},{"x":20,"y":46,"fill":"#000000"},{"x":20,"y":45,"fill":"#000000"},{"x":20,"y":44,"fill":"#000000"},{"x":20,"y":43,"fill":"#000000"},{"x":20,"y":42,"fill":"#dcde99"},{"x":20,"y":41,"fill":"#dcde99"},{"x":20,"y":40,"fill":"#dcde99"},{"x":20,"y":39,"fill":"#dcde99"},{"x":20,"y":38,"fill":"#000000"},{"x":20,"y":37,"fill":"#000000"},{"x":20,"y":36,"fill":"#dcde99"},{"x":20,"y":35,"fill":"#dcde99"},{"x":20,"y":34,"fill":"#dcde99"},{"x":20,"y":33,"fill":"#dcde99"},{"x":20,"y":32,"fill":"#dcde99"},{"x":20,"y":31,"fill":"#dcde99"},{"x":20,"y":30,"fill":"#dcde99"},{"x":20,"y":29,"fill":"#dcde99"},{"x":20,"y":28,"fill":"#dcde99"},{"x":20,"y":27,"fill":"#dcde99"},{"x":20,"y":26,"fill":"#dcde99"},{"x":20,"y":25,"fill":"#dcde99"},{"x":20,"y":24,"fill":"#000000"},{"x":20,"y":23,"fill":"#000000"},{"x":20,"y":22,"fill":"#000000"},{"x":20,"y":21,"fill":"#000000"},{"x":20,"y":20,"fill":"#000000"},{"x":20,"y":19,"fill":"#000000"},{"x":20,"y":18,"fill":"#000000"},{"x":20,"y":17,"fill":"#000000"},{"x":20,"y":16,"fill":"#000000"},{"x":20,"y":15,"fill":"#000000"},{"x":20,"y":14,"fill":"#000000"},{"x":20,"y":13,"fill":"#000000"},{"x":20,"y":12,"fill":"#000000"},{"x":20,"y":11,"fill":"#000000"},{"x":20,"y":10,"fill":"#000000"},{"x":20,"y":9,"fill":"#000000"},{"x":20,"y":8,"fill":"#e0e1d4"},{"x":20,"y":7,"fill":"#e0e1d4"},{"x":20,"y":6,"fill":"#e0e1d4"},{"x":20,"y":5,"fill":"#e0e1d4"},{"x":20,"y":4,"fill":"#e0e1d4"},{"x":20,"y":3,"fill":"#e0e1d4"},{"x":20,"y":2,"fill":"#e0e1d4"},{"x":20,"y":1,"fill":"#e0e1d4"},{"x":21,"y":64,"fill":"#0043bf"},{"x":21,"y":63,"fill":"#0043bf"},{"x":21,"y":62,"fill":"#0043bf"},{"x":21,"y":61,"fill":"#0043bf"},{"x":21,"y":60,"fill":"#0043bf"},{"x":21,"y":59,"fill":"#0043bf"},{"x":21,"y":58,"fill":"#0043bf"},{"x":21,"y":57,"fill":"#0043bf"},{"x":21,"y":56,"fill":"#0043bf"},{"x":21,"y":55,"fill":"#0043bf"},{"x":21,"y":54,"fill":"#0043bf"},{"x":21,"y":53,"fill":"#0043bf"},{"x":21,"y":52,"fill":"#0043bf"},{"x":21,"y":51,"fill":"#0043bf"},{"x":21,"y":50,"fill":"#0043bf"},{"x":21,"y":49,"fill":"#0043bf"},{"x":21,"y":48,"fill":"#000000"},{"x":21,"y":47,"fill":"#000000"},{"x":21,"y":46,"fill":"#000000"},{"x":21,"y":45,"fill":"#000000"},{"x":21,"y":44,"fill":"#000000"},{"x":21,"y":43,"fill":"#000000"},{"x":21,"y":42,"fill":"#dcde99"},{"x":21,"y":41,"fill":"#dcde99"},{"x":21,"y":40,"fill":"#dcde99"},{"x":21,"y":39,"fill":"#dcde99"},{"x":21,"y":38,"fill":"#000000"},{"x":21,"y":37,"fill":"#000000"},{"x":21,"y":36,"fill":"#dcde99"},{"x":21,"y":35,"fill":"#dcde99"},{"x":21,"y":34,"fill":"#dcde99"},{"x":21,"y":33,"fill":"#dcde99"},{"x":21,"y":32,"fill":"#dcde99"},{"x":21,"y":31,"fill":"#dcde99"},{"x":21,"y":30,"fill":"#dcde99"},{"x":21,"y":29,"fill":"#dcde99"},{"x":21,"y":28,"fill":"#dcde99"},{"x":21,"y":27,"fill":"#dcde99"},{"x":21,"y":26,"fill":"#dcde99"},{"x":21,"y":25,"fill":"#dcde99"},{"x":21,"y":24,"fill":"#000000"},{"x":21,"y":23,"fill":"#000000"},{"x":21,"y":22,"fill":"#000000"},{"x":21,"y":21,"fill":"#000000"},{"x":21,"y":20,"fill":"#000000"},{"x":21,"y":19,"fill":"#000000"},{"x":21,"y":18,"fill":"#000000"},{"x":21,"y":17,"fill":"#000000"},{"x":21,"y":16,"fill":"#000000"},{"x":21,"y":15,"fill":"#000000"},{"x":21,"y":14,"fill":"#000000"},{"x":21,"y":13,"fill":"#000000"},{"x":21,"y":12,"fill":"#000000"},{"x":21,"y":11,"fill":"#000000"},{"x":21,"y":10,"fill":"#000000"},{"x":21,"y":9,"fill":"#000000"},{"x":21,"y":8,"fill":"#e0e1d4"},{"x":21,"y":7,"fill":"#e0e1d4"},{"x":21,"y":6,"fill":"#e0e1d4"},{"x":21,"y":5,"fill":"#e0e1d4"},{"x":21,"y":4,"fill":"#e0e1d4"},{"x":21,"y":3,"fill":"#e0e1d4"},{"x":21,"y":2,"fill":"#e0e1d4"},{"x":21,"y":1,"fill":"#e0e1d4"},{"x":22,"y":64,"fill":"#0043bf"},{"x":22,"y":63,"fill":"#0043bf"},{"x":22,"y":62,"fill":"#0043bf"},{"x":22,"y":61,"fill":"#0043bf"},{"x":22,"y":60,"fill":"#0043bf"},{"x":22,"y":59,"fill":"#0043bf"},{"x":22,"y":58,"fill":"#0043bf"},{"x":22,"y":57,"fill":"#0043bf"},{"x":22,"y":56,"fill":"#0043bf"},{"x":22,"y":55,"fill":"#0043bf"},{"x":22,"y":54,"fill":"#0043bf"},{"x":22,"y":53,"fill":"#0043bf"},{"x":22,"y":52,"fill":"#0043bf"},{"x":22,"y":51,"fill":"#0043bf"},{"x":22,"y":50,"fill":"#000000"},{"x":22,"y":49,"fill":"#000000"},{"x":22,"y":48,"fill":"#b33636"},{"x":22,"y":47,"fill":"#b33636"},{"x":22,"y":46,"fill":"#b33636"},{"x":22,"y":45,"fill":"#b33636"},{"x":22,"y":44,"fill":"#000000"},{"x":22,"y":43,"fill":"#000000"},{"x":22,"y":42,"fill":"#fcf9dc"},{"x":22,"y":41,"fill":"#fcf9dc"},{"x":22,"y":40,"fill":"#dcde99"},{"x":22,"y":39,"fill":"#dcde99"},{"x":22,"y":38,"fill":"#000000"},{"x":22,"y":37,"fill":"#000000"},{"x":22,"y":36,"fill":"#fedcb8"},{"x":22,"y":35,"fill":"#fedcb8"},{"x":22,"y":34,"fill":"#fedcb8"},{"x":22,"y":33,"fill":"#fedcb8"},{"x":22,"y":32,"fill":"#f8b976"},{"x":22,"y":31,"fill":"#f8b976"},{"x":22,"y":30,"fill":"#fedcb8"},{"x":22,"y":29,"fill":"#fedcb8"},{"x":22,"y":28,"fill":"#fcf9dc"},{"x":22,"y":27,"fill":"#fcf9dc"},{"x":22,"y":26,"fill":"#dcde99"},{"x":22,"y":25,"fill":"#dcde99"},{"x":22,"y":24,"fill":"#dcde99"},{"x":22,"y":23,"fill":"#dcde99"},{"x":22,"y":22,"fill":"#000000"},{"x":22,"y":21,"fill":"#000000"},{"x":22,"y":20,"fill":"#ff0000"},{"x":22,"y":19,"fill":"#ff0000"},{"x":22,"y":18,"fill":"#ff0000"},{"x":22,"y":17,"fill":"#ff0000"},{"x":22,"y":16,"fill":"#925c0e"},{"x":22,"y":15,"fill":"#925c0e"},{"x":22,"y":14,"fill":"#925c0e"},{"x":22,"y":13,"fill":"#925c0e"},{"x":22,"y":12,"fill":"#d8d5b3"},{"x":22,"y":11,"fill":"#d8d5b3"},{"x":22,"y":10,"fill":"#000000"},{"x":22,"y":9,"fill":"#000000"},{"x":22,"y":8,"fill":"#000000"},{"x":22,"y":7,"fill":"#000000"},{"x":22,"y":6,"fill":"#000000"},{"x":22,"y":5,"fill":"#000000"},{"x":22,"y":4,"fill":"#e0e1d4"},{"x":22,"y":3,"fill":"#e0e1d4"},{"x":22,"y":2,"fill":"#e0e1d4"},{"x":22,"y":1,"fill":"#e0e1d4"},{"x":23,"y":64,"fill":"#0043bf"},{"x":23,"y":63,"fill":"#0043bf"},{"x":23,"y":62,"fill":"#0043bf"},{"x":23,"y":61,"fill":"#0043bf"},{"x":23,"y":60,"fill":"#0043bf"},{"x":23,"y":59,"fill":"#0043bf"},{"x":23,"y":58,"fill":"#0043bf"},{"x":23,"y":57,"fill":"#0043bf"},{"x":23,"y":56,"fill":"#0043bf"},{"x":23,"y":55,"fill":"#0043bf"},{"x":23,"y":54,"fill":"#0043bf"},{"x":23,"y":53,"fill":"#0043bf"},{"x":23,"y":52,"fill":"#0043bf"},{"x":23,"y":51,"fill":"#0043bf"},{"x":23,"y":50,"fill":"#000000"},{"x":23,"y":49,"fill":"#000000"},{"x":23,"y":48,"fill":"#b33636"},{"x":23,"y":47,"fill":"#b33636"},{"x":23,"y":46,"fill":"#b33636"},{"x":23,"y":45,"fill":"#b33636"},{"x":23,"y":44,"fill":"#000000"},{"x":23,"y":43,"fill":"#000000"},{"x":23,"y":42,"fill":"#fcf9dc"},{"x":23,"y":41,"fill":"#fcf9dc"},{"x":23,"y":40,"fill":"#dcde99"},{"x":23,"y":39,"fill":"#dcde99"},{"x":23,"y":38,"fill":"#000000"},{"x":23,"y":37,"fill":"#000000"},{"x":23,"y":36,"fill":"#fedcb8"},{"x":23,"y":35,"fill":"#fedcb8"},{"x":23,"y":34,"fill":"#fedcb8"},{"x":23,"y":33,"fill":"#fedcb8"},{"x":23,"y":32,"fill":"#f8b976"},{"x":23,"y":31,"fill":"#f8b976"},{"x":23,"y":30,"fill":"#fedcb8"},{"x":23,"y":29,"fill":"#fedcb8"},{"x":23,"y":28,"fill":"#fcf9dc"},{"x":23,"y":27,"fill":"#fcf9dc"},{"x":23,"y":26,"fill":"#dcde99"},{"x":23,"y":25,"fill":"#dcde99"},{"x":23,"y":24,"fill":"#dcde99"},{"x":23,"y":23,"fill":"#dcde99"},{"x":23,"y":22,"fill":"#000000"},{"x":23,"y":21,"fill":"#000000"},{"x":23,"y":20,"fill":"#ff0000"},{"x":23,"y":19,"fill":"#ff0000"},{"x":23,"y":18,"fill":"#ff0000"},{"x":23,"y":17,"fill":"#ff0000"},{"x":23,"y":16,"fill":"#925c0e"},{"x":23,"y":15,"fill":"#925c0e"},{"x":23,"y":14,"fill":"#925c0e"},{"x":23,"y":13,"fill":"#925c0e"},{"x":23,"y":12,"fill":"#d8d5b3"},{"x":23,"y":11,"fill":"#d8d5b3"},{"x":23,"y":10,"fill":"#000000"},{"x":23,"y":9,"fill":"#000000"},{"x":23,"y":8,"fill":"#000000"},{"x":23,"y":7,"fill":"#000000"},{"x":23,"y":6,"fill":"#000000"},{"x":23,"y":5,"fill":"#000000"},{"x":23,"y":4,"fill":"#e0e1d4"},{"x":23,"y":3,"fill":"#e0e1d4"},{"x":23,"y":2,"fill":"#e0e1d4"},{"x":23,"y":1,"fill":"#e0e1d4"},{"x":24,"y":64,"fill":"#0043bf"},{"x":24,"y":63,"fill":"#0043bf"},{"x":24,"y":62,"fill":"#0043bf"},{"x":24,"y":61,"fill":"#0043bf"},{"x":24,"y":60,"fill":"#0043bf"},{"x":24,"y":59,"fill":"#0043bf"},{"x":24,"y":58,"fill":"#0043bf"},{"x":24,"y":57,"fill":"#0043bf"},{"x":24,"y":56,"fill":"#0043bf"},{"x":24,"y":55,"fill":"#0043bf"},{"x":24,"y":54,"fill":"#0043bf"},{"x":24,"y":53,"fill":"#0043bf"},{"x":24,"y":52,"fill":"#000000"},{"x":24,"y":51,"fill":"#000000"},{"x":24,"y":50,"fill":"#b33636"},{"x":24,"y":49,"fill":"#b33636"},{"x":24,"y":48,"fill":"#e17070"},{"x":24,"y":47,"fill":"#e17070"},{"x":24,"y":46,"fill":"#b33636"},{"x":24,"y":45,"fill":"#b33636"},{"x":24,"y":44,"fill":"#000000"},{"x":24,"y":43,"fill":"#000000"},{"x":24,"y":42,"fill":"#fcf9dc"},{"x":24,"y":41,"fill":"#fcf9dc"},{"x":24,"y":40,"fill":"#dcde99"},{"x":24,"y":39,"fill":"#dcde99"},{"x":24,"y":38,"fill":"#000000"},{"x":24,"y":37,"fill":"#000000"},{"x":24,"y":36,"fill":"#fedcb8"},{"x":24,"y":35,"fill":"#fedcb8"},{"x":24,"y":34,"fill":"#000000"},{"x":24,"y":33,"fill":"#000000"},{"x":24,"y":32,"fill":"#fedcb8"},{"x":24,"y":31,"fill":"#fedcb8"},{"x":24,"y":30,"fill":"#fcf9dc"},{"x":24,"y":29,"fill":"#fcf9dc"},{"x":24,"y":28,"fill":"#fcf9dc"},{"x":24,"y":27,"fill":"#fcf9dc"},{"x":24,"y":26,"fill":"#fcf9dc"},{"x":24,"y":25,"fill":"#fcf9dc"},{"x":24,"y":24,"fill":"#dcde99"},{"x":24,"y":23,"fill":"#dcde99"},{"x":24,"y":22,"fill":"#dcde99"},{"x":24,"y":21,"fill":"#dcde99"},{"x":24,"y":20,"fill":"#000000"},{"x":24,"y":19,"fill":"#000000"},{"x":24,"y":18,"fill":"#ff0000"},{"x":24,"y":17,"fill":"#ff0000"},{"x":24,"y":16,"fill":"#925c0e"},{"x":24,"y":15,"fill":"#925c0e"},{"x":24,"y":14,"fill":"#925c0e"},{"x":24,"y":13,"fill":"#925c0e"},{"x":24,"y":12,"fill":"#d8d5b3"},{"x":24,"y":11,"fill":"#d8d5b3"},{"x":24,"y":10,"fill":"#ff0000"},{"x":24,"y":9,"fill":"#ff0000"},{"x":24,"y":8,"fill":"#925c0e"},{"x":24,"y":7,"fill":"#925c0e"},{"x":24,"y":6,"fill":"#000000"},{"x":24,"y":5,"fill":"#000000"},{"x":24,"y":4,"fill":"#e0e1d4"},{"x":24,"y":3,"fill":"#e0e1d4"},{"x":24,"y":2,"fill":"#e0e1d4"},{"x":24,"y":1,"fill":"#e0e1d4"},{"x":25,"y":64,"fill":"#0043bf"},{"x":25,"y":63,"fill":"#0043bf"},{"x":25,"y":62,"fill":"#0043bf"},{"x":25,"y":61,"fill":"#0043bf"},{"x":25,"y":60,"fill":"#0043bf"},{"x":25,"y":59,"fill":"#0043bf"},{"x":25,"y":58,"fill":"#0043bf"},{"x":25,"y":57,"fill":"#0043bf"},{"x":25,"y":56,"fill":"#0043bf"},{"x":25,"y":55,"fill":"#0043bf"},{"x":25,"y":54,"fill":"#0043bf"},{"x":25,"y":53,"fill":"#0043bf"},{"x":25,"y":52,"fill":"#000000"},{"x":25,"y":51,"fill":"#000000"},{"x":25,"y":50,"fill":"#b33636"},{"x":25,"y":49,"fill":"#b33636"},{"x":25,"y":48,"fill":"#e17070"},{"x":25,"y":47,"fill":"#e17070"},{"x":25,"y":46,"fill":"#b33636"},{"x":25,"y":45,"fill":"#b33636"},{"x":25,"y":44,"fill":"#000000"},{"x":25,"y":43,"fill":"#000000"},{"x":25,"y":42,"fill":"#fcf9dc"},{"x":25,"y":41,"fill":"#fcf9dc"},{"x":25,"y":40,"fill":"#dcde99"},{"x":25,"y":39,"fill":"#dcde99"},{"x":25,"y":38,"fill":"#000000"},{"x":25,"y":37,"fill":"#000000"},{"x":25,"y":36,"fill":"#fedcb8"},{"x":25,"y":35,"fill":"#fedcb8"},{"x":25,"y":34,"fill":"#000000"},{"x":25,"y":33,"fill":"#000000"},{"x":25,"y":32,"fill":"#fedcb8"},{"x":25,"y":31,"fill":"#fedcb8"},{"x":25,"y":30,"fill":"#fcf9dc"},{"x":25,"y":29,"fill":"#fcf9dc"},{"x":25,"y":28,"fill":"#fcf9dc"},{"x":25,"y":27,"fill":"#fcf9dc"},{"x":25,"y":26,"fill":"#fcf9dc"},{"x":25,"y":25,"fill":"#fcf9dc"},{"x":25,"y":24,"fill":"#dcde99"},{"x":25,"y":23,"fill":"#dcde99"},{"x":25,"y":22,"fill":"#dcde99"},{"x":25,"y":21,"fill":"#dcde99"},{"x":25,"y":20,"fill":"#000000"},{"x":25,"y":19,"fill":"#000000"},{"x":25,"y":18,"fill":"#ff0000"},{"x":25,"y":17,"fill":"#ff0000"},{"x":25,"y":16,"fill":"#925c0e"},{"x":25,"y":15,"fill":"#925c0e"},{"x":25,"y":14,"fill":"#925c0e"},{"x":25,"y":13,"fill":"#925c0e"},{"x":25,"y":12,"fill":"#d8d5b3"},{"x":25,"y":11,"fill":"#d8d5b3"},{"x":25,"y":10,"fill":"#ff0000"},{"x":25,"y":9,"fill":"#ff0000"},{"x":25,"y":8,"fill":"#925c0e"},{"x":25,"y":7,"fill":"#925c0e"},{"x":25,"y":6,"fill":"#000000"},{"x":25,"y":5,"fill":"#000000"},{"x":25,"y":4,"fill":"#e0e1d4"},{"x":25,"y":3,"fill":"#e0e1d4"},{"x":25,"y":2,"fill":"#e0e1d4"},{"x":25,"y":1,"fill":"#e0e1d4"},{"x":26,"y":64,"fill":"#0043bf"},{"x":26,"y":63,"fill":"#0043bf"},{"x":26,"y":62,"fill":"#0043bf"},{"x":26,"y":61,"fill":"#0043bf"},{"x":26,"y":60,"fill":"#0043bf"},{"x":26,"y":59,"fill":"#0043bf"},{"x":26,"y":58,"fill":"#0043bf"},{"x":26,"y":57,"fill":"#0043bf"},{"x":26,"y":56,"fill":"#0043bf"},{"x":26,"y":55,"fill":"#0043bf"},{"x":26,"y":54,"fill":"#000000"},{"x":26,"y":53,"fill":"#000000"},{"x":26,"y":52,"fill":"#b33636"},{"x":26,"y":51,"fill":"#b33636"},{"x":26,"y":50,"fill":"#e17070"},{"x":26,"y":49,"fill":"#e17070"},{"x":26,"y":48,"fill":"#e17070"},{"x":26,"y":47,"fill":"#e17070"},{"x":26,"y":46,"fill":"#b33636"},{"x":26,"y":45,"fill":"#b33636"},{"x":26,"y":44,"fill":"#000000"},{"x":26,"y":43,"fill":"#000000"},{"x":26,"y":42,"fill":"#fcf9dc"},{"x":26,"y":41,"fill":"#fcf9dc"},{"x":26,"y":40,"fill":"#dcde99"},{"x":26,"y":39,"fill":"#dcde99"},{"x":26,"y":38,"fill":"#000000"},{"x":26,"y":37,"fill":"#000000"},{"x":26,"y":36,"fill":"#fedcb8"},{"x":26,"y":35,"fill":"#fedcb8"},{"x":26,"y":34,"fill":"#fedcb8"},{"x":26,"y":33,"fill":"#fedcb8"},{"x":26,"y":32,"fill":"#fcf9dc"},{"x":26,"y":31,"fill":"#fcf9dc"},{"x":26,"y":30,"fill":"#000000"},{"x":26,"y":29,"fill":"#000000"},{"x":26,"y":28,"fill":"#fedcb8"},{"x":26,"y":27,"fill":"#fedcb8"},{"x":26,"y":26,"fill":"#fcf9dc"},{"x":26,"y":25,"fill":"#fcf9dc"},{"x":26,"y":24,"fill":"#fcf9dc"},{"x":26,"y":23,"fill":"#fcf9dc"},{"x":26,"y":22,"fill":"#dcde99"},{"x":26,"y":21,"fill":"#dcde99"},{"x":26,"y":20,"fill":"#000000"},{"x":26,"y":19,"fill":"#000000"},{"x":26,"y":18,"fill":"#ff0000"},{"x":26,"y":17,"fill":"#ff0000"},{"x":26,"y":16,"fill":"#f5e975"},{"x":26,"y":15,"fill":"#f5e975"},{"x":26,"y":14,"fill":"#f5e975"},{"x":26,"y":13,"fill":"#f5e975"},{"x":26,"y":12,"fill":"#fcf9dc"},{"x":26,"y":11,"fill":"#fcf9dc"},{"x":26,"y":10,"fill":"#ff0000"},{"x":26,"y":9,"fill":"#ff0000"},{"x":26,"y":8,"fill":"#000000"},{"x":26,"y":7,"fill":"#000000"},{"x":26,"y":6,"fill":"#000000"},{"x":26,"y":5,"fill":"#000000"},{"x":26,"y":4,"fill":"#e0e1d4"},{"x":26,"y":3,"fill":"#e0e1d4"},{"x":26,"y":2,"fill":"#e0e1d4"},{"x":26,"y":1,"fill":"#e0e1d4"},{"x":27,"y":64,"fill":"#0043bf"},{"x":27,"y":63,"fill":"#0043bf"},{"x":27,"y":62,"fill":"#0043bf"},{"x":27,"y":61,"fill":"#0043bf"},{"x":27,"y":60,"fill":"#0043bf"},{"x":27,"y":59,"fill":"#0043bf"},{"x":27,"y":58,"fill":"#0043bf"},{"x":27,"y":57,"fill":"#0043bf"},{"x":27,"y":56,"fill":"#0043bf"},{"x":27,"y":55,"fill":"#0043bf"},{"x":27,"y":54,"fill":"#000000"},{"x":27,"y":53,"fill":"#000000"},{"x":27,"y":52,"fill":"#b33636"},{"x":27,"y":51,"fill":"#b33636"},{"x":27,"y":50,"fill":"#e17070"},{"x":27,"y":49,"fill":"#e17070"},{"x":27,"y":48,"fill":"#e17070"},{"x":27,"y":47,"fill":"#e17070"},{"x":27,"y":46,"fill":"#b33636"},{"x":27,"y":45,"fill":"#b33636"},{"x":27,"y":44,"fill":"#000000"},{"x":27,"y":43,"fill":"#000000"},{"x":27,"y":42,"fill":"#fcf9dc"},{"x":27,"y":41,"fill":"#fcf9dc"},{"x":27,"y":40,"fill":"#dcde99"},{"x":27,"y":39,"fill":"#dcde99"},{"x":27,"y":38,"fill":"#000000"},{"x":27,"y":37,"fill":"#000000"},{"x":27,"y":36,"fill":"#fedcb8"},{"x":27,"y":35,"fill":"#fedcb8"},{"x":27,"y":34,"fill":"#fedcb8"},{"x":27,"y":33,"fill":"#fedcb8"},{"x":27,"y":32,"fill":"#fcf9dc"},{"x":27,"y":31,"fill":"#fcf9dc"},{"x":27,"y":30,"fill":"#000000"},{"x":27,"y":29,"fill":"#000000"},{"x":27,"y":28,"fill":"#fedcb8"},{"x":27,"y":27,"fill":"#fedcb8"},{"x":27,"y":26,"fill":"#fcf9dc"},{"x":27,"y":25,"fill":"#fcf9dc"},{"x":27,"y":24,"fill":"#fcf9dc"},{"x":27,"y":23,"fill":"#fcf9dc"},{"x":27,"y":22,"fill":"#dcde99"},{"x":27,"y":21,"fill":"#dcde99"},{"x":27,"y":20,"fill":"#000000"},{"x":27,"y":19,"fill":"#000000"},{"x":27,"y":18,"fill":"#ff0000"},{"x":27,"y":17,"fill":"#ff0000"},{"x":27,"y":16,"fill":"#f5e975"},{"x":27,"y":15,"fill":"#f5e975"},{"x":27,"y":14,"fill":"#f5e975"},{"x":27,"y":13,"fill":"#f5e975"},{"x":27,"y":12,"fill":"#fcf9dc"},{"x":27,"y":11,"fill":"#fcf9dc"},{"x":27,"y":10,"fill":"#ff0000"},{"x":27,"y":9,"fill":"#ff0000"},{"x":27,"y":8,"fill":"#000000"},{"x":27,"y":7,"fill":"#000000"},{"x":27,"y":6,"fill":"#000000"},{"x":27,"y":5,"fill":"#000000"},{"x":27,"y":4,"fill":"#e0e1d4"},{"x":27,"y":3,"fill":"#e0e1d4"},{"x":27,"y":2,"fill":"#e0e1d4"},{"x":27,"y":1,"fill":"#e0e1d4"},{"x":28,"y":64,"fill":"#0043bf"},{"x":28,"y":63,"fill":"#0043bf"},{"x":28,"y":62,"fill":"#0043bf"},{"x":28,"y":61,"fill":"#0043bf"},{"x":28,"y":60,"fill":"#0043bf"},{"x":28,"y":59,"fill":"#0043bf"},{"x":28,"y":58,"fill":"#0043bf"},{"x":28,"y":57,"fill":"#0043bf"},{"x":28,"y":56,"fill":"#000000"},{"x":28,"y":55,"fill":"#000000"},{"x":28,"y":54,"fill":"#b33636"},{"x":28,"y":53,"fill":"#b33636"},{"x":28,"y":52,"fill":"#e17070"},{"x":28,"y":51,"fill":"#e17070"},{"x":28,"y":50,"fill":"#ff0000"},{"x":28,"y":49,"fill":"#ff0000"},{"x":28,"y":48,"fill":"#e17070"},{"x":28,"y":47,"fill":"#e17070"},{"x":28,"y":46,"fill":"#e17070"},{"x":28,"y":45,"fill":"#e17070"},{"x":28,"y":44,"fill":"#000000"},{"x":28,"y":43,"fill":"#000000"},{"x":28,"y":42,"fill":"#fcf9dc"},{"x":28,"y":41,"fill":"#fcf9dc"},{"x":28,"y":40,"fill":"#dcde99"},{"x":28,"y":39,"fill":"#dcde99"},{"x":28,"y":38,"fill":"#000000"},{"x":28,"y":37,"fill":"#000000"},{"x":28,"y":36,"fill":"#fedcb8"},{"x":28,"y":35,"fill":"#fedcb8"},{"x":28,"y":34,"fill":"#fedcb8"},{"x":28,"y":33,"fill":"#fedcb8"},{"x":28,"y":32,"fill":"#fcf9dc"},{"x":28,"y":31,"fill":"#fcf9dc"},{"x":28,"y":30,"fill":"#000000"},{"x":28,"y":29,"fill":"#000000"},{"x":28,"y":28,"fill":"#fedcb8"},{"x":28,"y":27,"fill":"#fedcb8"},{"x":28,"y":26,"fill":"#fcf9dc"},{"x":28,"y":25,"fill":"#fcf9dc"},{"x":28,"y":24,"fill":"#fcf9dc"},{"x":28,"y":23,"fill":"#fcf9dc"},{"x":28,"y":22,"fill":"#dcde99"},{"x":28,"y":21,"fill":"#dcde99"},{"x":28,"y":20,"fill":"#dcde99"},{"x":28,"y":19,"fill":"#dcde99"},{"x":28,"y":18,"fill":"#000000"},{"x":28,"y":17,"fill":"#000000"},{"x":28,"y":16,"fill":"#f5e975"},{"x":28,"y":15,"fill":"#f5e975"},{"x":28,"y":14,"fill":"#835108"},{"x":28,"y":13,"fill":"#835108"},{"x":28,"y":12,"fill":"#ff0000"},{"x":28,"y":11,"fill":"#ff0000"},{"x":28,"y":10,"fill":"#000000"},{"x":28,"y":9,"fill":"#000000"},{"x":28,"y":8,"fill":"#e0e1d4"},{"x":28,"y":7,"fill":"#e0e1d4"},{"x":28,"y":6,"fill":"#e0e1d4"},{"x":28,"y":5,"fill":"#e0e1d4"},{"x":28,"y":4,"fill":"#e0e1d4"},{"x":28,"y":3,"fill":"#e0e1d4"},{"x":28,"y":2,"fill":"#e0e1d4"},{"x":28,"y":1,"fill":"#e0e1d4"},{"x":29,"y":64,"fill":"#0043bf"},{"x":29,"y":63,"fill":"#0043bf"},{"x":29,"y":62,"fill":"#0043bf"},{"x":29,"y":61,"fill":"#0043bf"},{"x":29,"y":60,"fill":"#0043bf"},{"x":29,"y":59,"fill":"#0043bf"},{"x":29,"y":58,"fill":"#0043bf"},{"x":29,"y":57,"fill":"#0043bf"},{"x":29,"y":56,"fill":"#000000"},{"x":29,"y":55,"fill":"#000000"},{"x":29,"y":54,"fill":"#b33636"},{"x":29,"y":53,"fill":"#b33636"},{"x":29,"y":52,"fill":"#e17070"},{"x":29,"y":51,"fill":"#e17070"},{"x":29,"y":50,"fill":"#ff0000"},{"x":29,"y":49,"fill":"#ff0000"},{"x":29,"y":48,"fill":"#e17070"},{"x":29,"y":47,"fill":"#e17070"},{"x":29,"y":46,"fill":"#e17070"},{"x":29,"y":45,"fill":"#e17070"},{"x":29,"y":44,"fill":"#000000"},{"x":29,"y":43,"fill":"#000000"},{"x":29,"y":42,"fill":"#fcf9dc"},{"x":29,"y":41,"fill":"#fcf9dc"},{"x":29,"y":40,"fill":"#dcde99"},{"x":29,"y":39,"fill":"#dcde99"},{"x":29,"y":38,"fill":"#000000"},{"x":29,"y":37,"fill":"#000000"},{"x":29,"y":36,"fill":"#fedcb8"},{"x":29,"y":35,"fill":"#fedcb8"},{"x":29,"y":34,"fill":"#fedcb8"},{"x":29,"y":33,"fill":"#fedcb8"},{"x":29,"y":32,"fill":"#fcf9dc"},{"x":29,"y":31,"fill":"#fcf9dc"},{"x":29,"y":30,"fill":"#000000"},{"x":29,"y":29,"fill":"#000000"},{"x":29,"y":28,"fill":"#fedcb8"},{"x":29,"y":27,"fill":"#fedcb8"},{"x":29,"y":26,"fill":"#fcf9dc"},{"x":29,"y":25,"fill":"#fcf9dc"},{"x":29,"y":24,"fill":"#fcf9dc"},{"x":29,"y":23,"fill":"#fcf9dc"},{"x":29,"y":22,"fill":"#dcde99"},{"x":29,"y":21,"fill":"#dcde99"},{"x":29,"y":20,"fill":"#dcde99"},{"x":29,"y":19,"fill":"#dcde99"},{"x":29,"y":18,"fill":"#000000"},{"x":29,"y":17,"fill":"#000000"},{"x":29,"y":16,"fill":"#f5e975"},{"x":29,"y":15,"fill":"#f5e975"},{"x":29,"y":14,"fill":"#835108"},{"x":29,"y":13,"fill":"#835108"},{"x":29,"y":12,"fill":"#ff0000"},{"x":29,"y":11,"fill":"#ff0000"},{"x":29,"y":10,"fill":"#000000"},{"x":29,"y":9,"fill":"#000000"},{"x":29,"y":8,"fill":"#e0e1d4"},{"x":29,"y":7,"fill":"#e0e1d4"},{"x":29,"y":6,"fill":"#e0e1d4"},{"x":29,"y":5,"fill":"#e0e1d4"},{"x":29,"y":4,"fill":"#e0e1d4"},{"x":29,"y":3,"fill":"#e0e1d4"},{"x":29,"y":2,"fill":"#e0e1d4"},{"x":29,"y":1,"fill":"#e0e1d4"},{"x":30,"y":64,"fill":"#0043bf"},{"x":30,"y":63,"fill":"#0043bf"},{"x":30,"y":62,"fill":"#0043bf"},{"x":30,"y":61,"fill":"#0043bf"},{"x":30,"y":60,"fill":"#ffffff"},{"x":30,"y":59,"fill":"#ffffff"},{"x":30,"y":58,"fill":"#0043bf"},{"x":30,"y":57,"fill":"#0043bf"},{"x":30,"y":56,"fill":"#000000"},{"x":30,"y":55,"fill":"#000000"},{"x":30,"y":54,"fill":"#b33636"},{"x":30,"y":53,"fill":"#b33636"},{"x":30,"y":52,"fill":"#e17070"},{"x":30,"y":51,"fill":"#e17070"},{"x":30,"y":50,"fill":"#ff0000"},{"x":30,"y":49,"fill":"#ff0000"},{"x":30,"y":48,"fill":"#ff0000"},{"x":30,"y":47,"fill":"#ff0000"},{"x":30,"y":46,"fill":"#e17070"},{"x":30,"y":45,"fill":"#e17070"},{"x":30,"y":44,"fill":"#000000"},{"x":30,"y":43,"fill":"#000000"},{"x":30,"y":42,"fill":"#fcf9dc"},{"x":30,"y":41,"fill":"#fcf9dc"},{"x":30,"y":40,"fill":"#dcde99"},{"x":30,"y":39,"fill":"#dcde99"},{"x":30,"y":38,"fill":"#000000"},{"x":30,"y":37,"fill":"#000000"},{"x":30,"y":36,"fill":"#fedcb8"},{"x":30,"y":35,"fill":"#fedcb8"},{"x":30,"y":34,"fill":"#fedcb8"},{"x":30,"y":33,"fill":"#fedcb8"},{"x":30,"y":32,"fill":"#fcf9dc"},{"x":30,"y":31,"fill":"#fcf9dc"},{"x":30,"y":30,"fill":"#000000"},{"x":30,"y":29,"fill":"#000000"},{"x":30,"y":28,"fill":"#fedcb8"},{"x":30,"y":27,"fill":"#fedcb8"},{"x":30,"y":26,"fill":"#fcf9dc"},{"x":30,"y":25,"fill":"#fcf9dc"},{"x":30,"y":24,"fill":"#fcf9dc"},{"x":30,"y":23,"fill":"#fcf9dc"},{"x":30,"y":22,"fill":"#fcf9dc"},{"x":30,"y":21,"fill":"#fcf9dc"},{"x":30,"y":20,"fill":"#dcde99"},{"x":30,"y":19,"fill":"#dcde99"},{"x":30,"y":18,"fill":"#000000"},{"x":30,"y":17,"fill":"#000000"},{"x":30,"y":16,"fill":"#f5e975"},{"x":30,"y":15,"fill":"#f5e975"},{"x":30,"y":14,"fill":"#f5e975"},{"x":30,"y":13,"fill":"#f5e975"},{"x":30,"y":12,"fill":"#fcf9dc"},{"x":30,"y":11,"fill":"#fcf9dc"},{"x":30,"y":10,"fill":"#000000"},{"x":30,"y":9,"fill":"#000000"},{"x":30,"y":8,"fill":"#e0e1d4"},{"x":30,"y":7,"fill":"#e0e1d4"},{"x":30,"y":6,"fill":"#e0e1d4"},{"x":30,"y":5,"fill":"#e0e1d4"},{"x":30,"y":4,"fill":"#e0e1d4"},{"x":30,"y":3,"fill":"#e0e1d4"},{"x":30,"y":2,"fill":"#e0e1d4"},{"x":30,"y":1,"fill":"#e0e1d4"},{"x":31,"y":64,"fill":"#0043bf"},{"x":31,"y":63,"fill":"#0043bf"},{"x":31,"y":62,"fill":"#0043bf"},{"x":31,"y":61,"fill":"#0043bf"},{"x":31,"y":60,"fill":"#ffffff"},{"x":31,"y":59,"fill":"#ffffff"},{"x":31,"y":58,"fill":"#0043bf"},{"x":31,"y":57,"fill":"#0043bf"},{"x":31,"y":56,"fill":"#000000"},{"x":31,"y":55,"fill":"#000000"},{"x":31,"y":54,"fill":"#b33636"},{"x":31,"y":53,"fill":"#b33636"},{"x":31,"y":52,"fill":"#e17070"},{"x":31,"y":51,"fill":"#e17070"},{"x":31,"y":50,"fill":"#ff0000"},{"x":31,"y":49,"fill":"#ff0000"},{"x":31,"y":48,"fill":"#ff0000"},{"x":31,"y":47,"fill":"#ff0000"},{"x":31,"y":46,"fill":"#e17070"},{"x":31,"y":45,"fill":"#e17070"},{"x":31,"y":44,"fill":"#000000"},{"x":31,"y":43,"fill":"#000000"},{"x":31,"y":42,"fill":"#fcf9dc"},{"x":31,"y":41,"fill":"#fcf9dc"},{"x":31,"y":40,"fill":"#dcde99"},{"x":31,"y":39,"fill":"#dcde99"},{"x":31,"y":38,"fill":"#000000"},{"x":31,"y":37,"fill":"#000000"},{"x":31,"y":36,"fill":"#fedcb8"},{"x":31,"y":35,"fill":"#fedcb8"},{"x":31,"y":34,"fill":"#fedcb8"},{"x":31,"y":33,"fill":"#fedcb8"},{"x":31,"y":32,"fill":"#fcf9dc"},{"x":31,"y":31,"fill":"#fcf9dc"},{"x":31,"y":30,"fill":"#000000"},{"x":31,"y":29,"fill":"#000000"},{"x":31,"y":28,"fill":"#fedcb8"},{"x":31,"y":27,"fill":"#fedcb8"},{"x":31,"y":26,"fill":"#fcf9dc"},{"x":31,"y":25,"fill":"#fcf9dc"},{"x":31,"y":24,"fill":"#fcf9dc"},{"x":31,"y":23,"fill":"#fcf9dc"},{"x":31,"y":22,"fill":"#fcf9dc"},{"x":31,"y":21,"fill":"#fcf9dc"},{"x":31,"y":20,"fill":"#dcde99"},{"x":31,"y":19,"fill":"#dcde99"},{"x":31,"y":18,"fill":"#000000"},{"x":31,"y":17,"fill":"#000000"},{"x":31,"y":16,"fill":"#f5e975"},{"x":31,"y":15,"fill":"#f5e975"},{"x":31,"y":14,"fill":"#f5e975"},{"x":31,"y":13,"fill":"#f5e975"},{"x":31,"y":12,"fill":"#fcf9dc"},{"x":31,"y":11,"fill":"#fcf9dc"},{"x":31,"y":10,"fill":"#000000"},{"x":31,"y":9,"fill":"#000000"},{"x":31,"y":8,"fill":"#e0e1d4"},{"x":31,"y":7,"fill":"#e0e1d4"},{"x":31,"y":6,"fill":"#e0e1d4"},{"x":31,"y":5,"fill":"#e0e1d4"},{"x":31,"y":4,"fill":"#e0e1d4"},{"x":31,"y":3,"fill":"#e0e1d4"},{"x":31,"y":2,"fill":"#e0e1d4"},{"x":31,"y":1,"fill":"#e0e1d4"},{"x":32,"y":64,"fill":"#0043bf"},{"x":32,"y":63,"fill":"#0043bf"},{"x":32,"y":62,"fill":"#0043bf"},{"x":32,"y":61,"fill":"#0043bf"},{"x":32,"y":60,"fill":"#0043bf"},{"x":32,"y":59,"fill":"#0043bf"},{"x":32,"y":58,"fill":"#0043bf"},{"x":32,"y":57,"fill":"#0043bf"},{"x":32,"y":56,"fill":"#000000"},{"x":32,"y":55,"fill":"#000000"},{"x":32,"y":54,"fill":"#b33636"},{"x":32,"y":53,"fill":"#b33636"},{"x":32,"y":52,"fill":"#e17070"},{"x":32,"y":51,"fill":"#e17070"},{"x":32,"y":50,"fill":"#ff0000"},{"x":32,"y":49,"fill":"#ff0000"},{"x":32,"y":48,"fill":"#ff0000"},{"x":32,"y":47,"fill":"#ff0000"},{"x":32,"y":46,"fill":"#e17070"},{"x":32,"y":45,"fill":"#e17070"},{"x":32,"y":44,"fill":"#000000"},{"x":32,"y":43,"fill":"#000000"},{"x":32,"y":42,"fill":"#fcf9dc"},{"x":32,"y":41,"fill":"#fcf9dc"},{"x":32,"y":40,"fill":"#dcde99"},{"x":32,"y":39,"fill":"#dcde99"},{"x":32,"y":38,"fill":"#000000"},{"x":32,"y":37,"fill":"#000000"},{"x":32,"y":36,"fill":"#fedcb8"},{"x":32,"y":35,"fill":"#fedcb8"},{"x":32,"y":34,"fill":"#000000"},{"x":32,"y":33,"fill":"#000000"},{"x":32,"y":32,"fill":"#fedcb8"},{"x":32,"y":31,"fill":"#fedcb8"},{"x":32,"y":30,"fill":"#fcf9dc"},{"x":32,"y":29,"fill":"#fcf9dc"},{"x":32,"y":28,"fill":"#fcf9dc"},{"x":32,"y":27,"fill":"#fcf9dc"},{"x":32,"y":26,"fill":"#fcf9dc"},{"x":32,"y":25,"fill":"#fcf9dc"},{"x":32,"y":24,"fill":"#fcf9dc"},{"x":32,"y":23,"fill":"#fcf9dc"},{"x":32,"y":22,"fill":"#fcf9dc"},{"x":32,"y":21,"fill":"#fcf9dc"},{"x":32,"y":20,"fill":"#000000"},{"x":32,"y":19,"fill":"#000000"},{"x":32,"y":18,"fill":"#000000"},{"x":32,"y":17,"fill":"#000000"},{"x":32,"y":16,"fill":"#925c0e"},{"x":32,"y":15,"fill":"#925c0e"},{"x":32,"y":14,"fill":"#925c0e"},{"x":32,"y":13,"fill":"#925c0e"},{"x":32,"y":12,"fill":"#fcf9dc"},{"x":32,"y":11,"fill":"#fcf9dc"},{"x":32,"y":10,"fill":"#ff0000"},{"x":32,"y":9,"fill":"#ff0000"},{"x":32,"y":8,"fill":"#000000"},{"x":32,"y":7,"fill":"#000000"},{"x":32,"y":6,"fill":"#000000"},{"x":32,"y":5,"fill":"#000000"},{"x":32,"y":4,"fill":"#e0e1d4"},{"x":32,"y":3,"fill":"#e0e1d4"},{"x":32,"y":2,"fill":"#e0e1d4"},{"x":32,"y":1,"fill":"#e0e1d4"},{"x":33,"y":64,"fill":"#0043bf"},{"x":33,"y":63,"fill":"#0043bf"},{"x":33,"y":62,"fill":"#0043bf"},{"x":33,"y":61,"fill":"#0043bf"},{"x":33,"y":60,"fill":"#0043bf"},{"x":33,"y":59,"fill":"#0043bf"},{"x":33,"y":58,"fill":"#0043bf"},{"x":33,"y":57,"fill":"#0043bf"},{"x":33,"y":56,"fill":"#000000"},{"x":33,"y":55,"fill":"#000000"},{"x":33,"y":54,"fill":"#b33636"},{"x":33,"y":53,"fill":"#b33636"},{"x":33,"y":52,"fill":"#e17070"},{"x":33,"y":51,"fill":"#e17070"},{"x":33,"y":50,"fill":"#ff0000"},{"x":33,"y":49,"fill":"#ff0000"},{"x":33,"y":48,"fill":"#ff0000"},{"x":33,"y":47,"fill":"#ff0000"},{"x":33,"y":46,"fill":"#e17070"},{"x":33,"y":45,"fill":"#e17070"},{"x":33,"y":44,"fill":"#000000"},{"x":33,"y":43,"fill":"#000000"},{"x":33,"y":42,"fill":"#fcf9dc"},{"x":33,"y":41,"fill":"#fcf9dc"},{"x":33,"y":40,"fill":"#dcde99"},{"x":33,"y":39,"fill":"#dcde99"},{"x":33,"y":38,"fill":"#000000"},{"x":33,"y":37,"fill":"#000000"},{"x":33,"y":36,"fill":"#fedcb8"},{"x":33,"y":35,"fill":"#fedcb8"},{"x":33,"y":34,"fill":"#000000"},{"x":33,"y":33,"fill":"#000000"},{"x":33,"y":32,"fill":"#fedcb8"},{"x":33,"y":31,"fill":"#fedcb8"},{"x":33,"y":30,"fill":"#fcf9dc"},{"x":33,"y":29,"fill":"#fcf9dc"},{"x":33,"y":28,"fill":"#fcf9dc"},{"x":33,"y":27,"fill":"#fcf9dc"},{"x":33,"y":26,"fill":"#fcf9dc"},{"x":33,"y":25,"fill":"#fcf9dc"},{"x":33,"y":24,"fill":"#fcf9dc"},{"x":33,"y":23,"fill":"#fcf9dc"},{"x":33,"y":22,"fill":"#fcf9dc"},{"x":33,"y":21,"fill":"#fcf9dc"},{"x":33,"y":20,"fill":"#000000"},{"x":33,"y":19,"fill":"#000000"},{"x":33,"y":18,"fill":"#000000"},{"x":33,"y":17,"fill":"#000000"},{"x":33,"y":16,"fill":"#925c0e"},{"x":33,"y":15,"fill":"#925c0e"},{"x":33,"y":14,"fill":"#925c0e"},{"x":33,"y":13,"fill":"#925c0e"},{"x":33,"y":12,"fill":"#fcf9dc"},{"x":33,"y":11,"fill":"#fcf9dc"},{"x":33,"y":10,"fill":"#ff0000"},{"x":33,"y":9,"fill":"#ff0000"},{"x":33,"y":8,"fill":"#000000"},{"x":33,"y":7,"fill":"#000000"},{"x":33,"y":6,"fill":"#000000"},{"x":33,"y":5,"fill":"#000000"},{"x":33,"y":4,"fill":"#e0e1d4"},{"x":33,"y":3,"fill":"#e0e1d4"},{"x":33,"y":2,"fill":"#e0e1d4"},{"x":33,"y":1,"fill":"#e0e1d4"},{"x":34,"y":64,"fill":"#0043bf"},{"x":34,"y":63,"fill":"#0043bf"},{"x":34,"y":62,"fill":"#0043bf"},{"x":34,"y":61,"fill":"#0043bf"},{"x":34,"y":60,"fill":"#0043bf"},{"x":34,"y":59,"fill":"#0043bf"},{"x":34,"y":58,"fill":"#0043bf"},{"x":34,"y":57,"fill":"#0043bf"},{"x":34,"y":56,"fill":"#000000"},{"x":34,"y":55,"fill":"#000000"},{"x":34,"y":54,"fill":"#b33636"},{"x":34,"y":53,"fill":"#b33636"},{"x":34,"y":52,"fill":"#000000"},{"x":34,"y":51,"fill":"#000000"},{"x":34,"y":50,"fill":"#ff0000"},{"x":34,"y":49,"fill":"#ff0000"},{"x":34,"y":48,"fill":"#ff0000"},{"x":34,"y":47,"fill":"#ff0000"},{"x":34,"y":46,"fill":"#ff0000"},{"x":34,"y":45,"fill":"#ff0000"},{"x":34,"y":44,"fill":"#000000"},{"x":34,"y":43,"fill":"#000000"},{"x":34,"y":42,"fill":"#fcf9dc"},{"x":34,"y":41,"fill":"#fcf9dc"},{"x":34,"y":40,"fill":"#dcde99"},{"x":34,"y":39,"fill":"#dcde99"},{"x":34,"y":38,"fill":"#000000"},{"x":34,"y":37,"fill":"#000000"},{"x":34,"y":36,"fill":"#fedcb8"},{"x":34,"y":35,"fill":"#fedcb8"},{"x":34,"y":34,"fill":"#fedcb8"},{"x":34,"y":33,"fill":"#fedcb8"},{"x":34,"y":32,"fill":"#f8b976"},{"x":34,"y":31,"fill":"#f8b976"},{"x":34,"y":30,"fill":"#fedcb8"},{"x":34,"y":29,"fill":"#fedcb8"},{"x":34,"y":28,"fill":"#fcf9dc"},{"x":34,"y":27,"fill":"#fcf9dc"},{"x":34,"y":26,"fill":"#fcf9dc"},{"x":34,"y":25,"fill":"#fcf9dc"},{"x":34,"y":24,"fill":"#fcf9dc"},{"x":34,"y":23,"fill":"#fcf9dc"},{"x":34,"y":22,"fill":"#fcf9dc"},{"x":34,"y":21,"fill":"#fcf9dc"},{"x":34,"y":20,"fill":"#000000"},{"x":34,"y":19,"fill":"#000000"},{"x":34,"y":18,"fill":"#ff0000"},{"x":34,"y":17,"fill":"#ff0000"},{"x":34,"y":16,"fill":"#ae6f14"},{"x":34,"y":15,"fill":"#ae6f14"},{"x":34,"y":14,"fill":"#925c0e"},{"x":34,"y":13,"fill":"#925c0e"},{"x":34,"y":12,"fill":"#fcf9dc"},{"x":34,"y":11,"fill":"#fcf9dc"},{"x":34,"y":10,"fill":"#ff0000"},{"x":34,"y":9,"fill":"#ff0000"},{"x":34,"y":8,"fill":"#925c0e"},{"x":34,"y":7,"fill":"#925c0e"},{"x":34,"y":6,"fill":"#000000"},{"x":34,"y":5,"fill":"#000000"},{"x":34,"y":4,"fill":"#e0e1d4"},{"x":34,"y":3,"fill":"#e0e1d4"},{"x":34,"y":2,"fill":"#e0e1d4"},{"x":34,"y":1,"fill":"#e0e1d4"},{"x":35,"y":64,"fill":"#0043bf"},{"x":35,"y":63,"fill":"#0043bf"},{"x":35,"y":62,"fill":"#0043bf"},{"x":35,"y":61,"fill":"#0043bf"},{"x":35,"y":60,"fill":"#0043bf"},{"x":35,"y":59,"fill":"#0043bf"},{"x":35,"y":58,"fill":"#0043bf"},{"x":35,"y":57,"fill":"#0043bf"},{"x":35,"y":56,"fill":"#000000"},{"x":35,"y":55,"fill":"#000000"},{"x":35,"y":54,"fill":"#b33636"},{"x":35,"y":53,"fill":"#b33636"},{"x":35,"y":52,"fill":"#000000"},{"x":35,"y":51,"fill":"#000000"},{"x":35,"y":50,"fill":"#ff0000"},{"x":35,"y":49,"fill":"#ff0000"},{"x":35,"y":48,"fill":"#ff0000"},{"x":35,"y":47,"fill":"#ff0000"},{"x":35,"y":46,"fill":"#ff0000"},{"x":35,"y":45,"fill":"#ff0000"},{"x":35,"y":44,"fill":"#000000"},{"x":35,"y":43,"fill":"#000000"},{"x":35,"y":42,"fill":"#fcf9dc"},{"x":35,"y":41,"fill":"#fcf9dc"},{"x":35,"y":40,"fill":"#dcde99"},{"x":35,"y":39,"fill":"#dcde99"},{"x":35,"y":38,"fill":"#000000"},{"x":35,"y":37,"fill":"#000000"},{"x":35,"y":36,"fill":"#fedcb8"},{"x":35,"y":35,"fill":"#fedcb8"},{"x":35,"y":34,"fill":"#fedcb8"},{"x":35,"y":33,"fill":"#fedcb8"},{"x":35,"y":32,"fill":"#f8b976"},{"x":35,"y":31,"fill":"#f8b976"},{"x":35,"y":30,"fill":"#fedcb8"},{"x":35,"y":29,"fill":"#fedcb8"},{"x":35,"y":28,"fill":"#fcf9dc"},{"x":35,"y":27,"fill":"#fcf9dc"},{"x":35,"y":26,"fill":"#fcf9dc"},{"x":35,"y":25,"fill":"#fcf9dc"},{"x":35,"y":24,"fill":"#fcf9dc"},{"x":35,"y":23,"fill":"#fcf9dc"},{"x":35,"y":22,"fill":"#fcf9dc"},{"x":35,"y":21,"fill":"#fcf9dc"},{"x":35,"y":20,"fill":"#000000"},{"x":35,"y":19,"fill":"#000000"},{"x":35,"y":18,"fill":"#ff0000"},{"x":35,"y":17,"fill":"#ff0000"},{"x":35,"y":16,"fill":"#ae6f14"},{"x":35,"y":15,"fill":"#ae6f14"},{"x":35,"y":14,"fill":"#925c0e"},{"x":35,"y":13,"fill":"#925c0e"},{"x":35,"y":12,"fill":"#fcf9dc"},{"x":35,"y":11,"fill":"#fcf9dc"},{"x":35,"y":10,"fill":"#ff0000"},{"x":35,"y":9,"fill":"#ff0000"},{"x":35,"y":8,"fill":"#925c0e"},{"x":35,"y":7,"fill":"#925c0e"},{"x":35,"y":6,"fill":"#000000"},{"x":35,"y":5,"fill":"#000000"},{"x":35,"y":4,"fill":"#e0e1d4"},{"x":35,"y":3,"fill":"#e0e1d4"},{"x":35,"y":2,"fill":"#e0e1d4"},{"x":35,"y":1,"fill":"#e0e1d4"},{"x":36,"y":64,"fill":"#0043bf"},{"x":36,"y":63,"fill":"#0043bf"},{"x":36,"y":62,"fill":"#0043bf"},{"x":36,"y":61,"fill":"#0043bf"},{"x":36,"y":60,"fill":"#0043bf"},{"x":36,"y":59,"fill":"#0043bf"},{"x":36,"y":58,"fill":"#0043bf"},{"x":36,"y":57,"fill":"#0043bf"},{"x":36,"y":56,"fill":"#000000"},{"x":36,"y":55,"fill":"#000000"},{"x":36,"y":54,"fill":"#b33636"},{"x":36,"y":53,"fill":"#b33636"},{"x":36,"y":52,"fill":"#e17070"},{"x":36,"y":51,"fill":"#e17070"},{"x":36,"y":50,"fill":"#000000"},{"x":36,"y":49,"fill":"#000000"},{"x":36,"y":48,"fill":"#ff0000"},{"x":36,"y":47,"fill":"#ff0000"},{"x":36,"y":46,"fill":"#ff0000"},{"x":36,"y":45,"fill":"#ff0000"},{"x":36,"y":44,"fill":"#000000"},{"x":36,"y":43,"fill":"#000000"},{"x":36,"y":42,"fill":"#fcf9dc"},{"x":36,"y":41,"fill":"#fcf9dc"},{"x":36,"y":40,"fill":"#dcde99"},{"x":36,"y":39,"fill":"#dcde99"},{"x":36,"y":38,"fill":"#000000"},{"x":36,"y":37,"fill":"#000000"},{"x":36,"y":36,"fill":"#fcf9dc"},{"x":36,"y":35,"fill":"#fcf9dc"},{"x":36,"y":34,"fill":"#fcf9dc"},{"x":36,"y":33,"fill":"#fcf9dc"},{"x":36,"y":32,"fill":"#fcf9dc"},{"x":36,"y":31,"fill":"#fcf9dc"},{"x":36,"y":30,"fill":"#fcf9dc"},{"x":36,"y":29,"fill":"#fcf9dc"},{"x":36,"y":28,"fill":"#fcf9dc"},{"x":36,"y":27,"fill":"#fcf9dc"},{"x":36,"y":26,"fill":"#fcf9dc"},{"x":36,"y":25,"fill":"#fcf9dc"},{"x":36,"y":24,"fill":"#fcf9dc"},{"x":36,"y":23,"fill":"#fcf9dc"},{"x":36,"y":22,"fill":"#000000"},{"x":36,"y":21,"fill":"#000000"},{"x":36,"y":20,"fill":"#ff7f7f"},{"x":36,"y":19,"fill":"#ff7f7f"},{"x":36,"y":18,"fill":"#ff0000"},{"x":36,"y":17,"fill":"#ff0000"},{"x":36,"y":16,"fill":"#ae6f14"},{"x":36,"y":15,"fill":"#ae6f14"},{"x":36,"y":14,"fill":"#ae6f14"},{"x":36,"y":13,"fill":"#ae6f14"},{"x":36,"y":12,"fill":"#fcf9dc"},{"x":36,"y":11,"fill":"#fcf9dc"},{"x":36,"y":10,"fill":"#000000"},{"x":36,"y":9,"fill":"#000000"},{"x":36,"y":8,"fill":"#000000"},{"x":36,"y":7,"fill":"#000000"},{"x":36,"y":6,"fill":"#000000"},{"x":36,"y":5,"fill":"#000000"},{"x":36,"y":4,"fill":"#e0e1d4"},{"x":36,"y":3,"fill":"#e0e1d4"},{"x":36,"y":2,"fill":"#e0e1d4"},{"x":36,"y":1,"fill":"#e0e1d4"},{"x":37,"y":64,"fill":"#0043bf"},{"x":37,"y":63,"fill":"#0043bf"},{"x":37,"y":62,"fill":"#0043bf"},{"x":37,"y":61,"fill":"#0043bf"},{"x":37,"y":60,"fill":"#0043bf"},{"x":37,"y":59,"fill":"#0043bf"},{"x":37,"y":58,"fill":"#0043bf"},{"x":37,"y":57,"fill":"#0043bf"},{"x":37,"y":56,"fill":"#000000"},{"x":37,"y":55,"fill":"#000000"},{"x":37,"y":54,"fill":"#b33636"},{"x":37,"y":53,"fill":"#b33636"},{"x":37,"y":52,"fill":"#e17070"},{"x":37,"y":51,"fill":"#e17070"},{"x":37,"y":50,"fill":"#000000"},{"x":37,"y":49,"fill":"#000000"},{"x":37,"y":48,"fill":"#ff0000"},{"x":37,"y":47,"fill":"#ff0000"},{"x":37,"y":46,"fill":"#ff0000"},{"x":37,"y":45,"fill":"#ff0000"},{"x":37,"y":44,"fill":"#000000"},{"x":37,"y":43,"fill":"#000000"},{"x":37,"y":42,"fill":"#fcf9dc"},{"x":37,"y":41,"fill":"#fcf9dc"},{"x":37,"y":40,"fill":"#dcde99"},{"x":37,"y":39,"fill":"#dcde99"},{"x":37,"y":38,"fill":"#000000"},{"x":37,"y":37,"fill":"#000000"},{"x":37,"y":36,"fill":"#fcf9dc"},{"x":37,"y":35,"fill":"#fcf9dc"},{"x":37,"y":34,"fill":"#fcf9dc"},{"x":37,"y":33,"fill":"#fcf9dc"},{"x":37,"y":32,"fill":"#fcf9dc"},{"x":37,"y":31,"fill":"#fcf9dc"},{"x":37,"y":30,"fill":"#fcf9dc"},{"x":37,"y":29,"fill":"#fcf9dc"},{"x":37,"y":28,"fill":"#fcf9dc"},{"x":37,"y":27,"fill":"#fcf9dc"},{"x":37,"y":26,"fill":"#fcf9dc"},{"x":37,"y":25,"fill":"#fcf9dc"},{"x":37,"y":24,"fill":"#fcf9dc"},{"x":37,"y":23,"fill":"#fcf9dc"},{"x":37,"y":22,"fill":"#000000"},{"x":37,"y":21,"fill":"#000000"},{"x":37,"y":20,"fill":"#ff7f7f"},{"x":37,"y":19,"fill":"#ff7f7f"},{"x":37,"y":18,"fill":"#ff0000"},{"x":37,"y":17,"fill":"#ff0000"},{"x":37,"y":16,"fill":"#ae6f14"},{"x":37,"y":15,"fill":"#ae6f14"},{"x":37,"y":14,"fill":"#ae6f14"},{"x":37,"y":13,"fill":"#ae6f14"},{"x":37,"y":12,"fill":"#fcf9dc"},{"x":37,"y":11,"fill":"#fcf9dc"},{"x":37,"y":10,"fill":"#000000"},{"x":37,"y":9,"fill":"#000000"},{"x":37,"y":8,"fill":"#000000"},{"x":37,"y":7,"fill":"#000000"},{"x":37,"y":6,"fill":"#000000"},{"x":37,"y":5,"fill":"#000000"},{"x":37,"y":4,"fill":"#e0e1d4"},{"x":37,"y":3,"fill":"#e0e1d4"},{"x":37,"y":2,"fill":"#e0e1d4"},{"x":37,"y":1,"fill":"#e0e1d4"},{"x":38,"y":64,"fill":"#0043bf"},{"x":38,"y":63,"fill":"#0043bf"},{"x":38,"y":62,"fill":"#0043bf"},{"x":38,"y":61,"fill":"#0043bf"},{"x":38,"y":60,"fill":"#0043bf"},{"x":38,"y":59,"fill":"#0043bf"},{"x":38,"y":58,"fill":"#0043bf"},{"x":38,"y":57,"fill":"#0043bf"},{"x":38,"y":56,"fill":"#0043bf"},{"x":38,"y":55,"fill":"#0043bf"},{"x":38,"y":54,"fill":"#000000"},{"x":38,"y":53,"fill":"#000000"},{"x":38,"y":52,"fill":"#ff0000"},{"x":38,"y":51,"fill":"#ff0000"},{"x":38,"y":50,"fill":"#ff0000"},{"x":38,"y":49,"fill":"#ff0000"},{"x":38,"y":48,"fill":"#000000"},{"x":38,"y":47,"fill":"#000000"},{"x":38,"y":46,"fill":"#000000"},{"x":38,"y":45,"fill":"#000000"},{"x":38,"y":44,"fill":"#000000"},{"x":38,"y":43,"fill":"#000000"},{"x":38,"y":42,"fill":"#fcf9dc"},{"x":38,"y":41,"fill":"#fcf9dc"},{"x":38,"y":40,"fill":"#dcde99"},{"x":38,"y":39,"fill":"#dcde99"},{"x":38,"y":38,"fill":"#000000"},{"x":38,"y":37,"fill":"#000000"},{"x":38,"y":36,"fill":"#fcf9dc"},{"x":38,"y":35,"fill":"#fcf9dc"},{"x":38,"y":34,"fill":"#fcf9dc"},{"x":38,"y":33,"fill":"#fcf9dc"},{"x":38,"y":32,"fill":"#fcf9dc"},{"x":38,"y":31,"fill":"#fcf9dc"},{"x":38,"y":30,"fill":"#fcf9dc"},{"x":38,"y":29,"fill":"#fcf9dc"},{"x":38,"y":28,"fill":"#fcf9dc"},{"x":38,"y":27,"fill":"#fcf9dc"},{"x":38,"y":26,"fill":"#fcf9dc"},{"x":38,"y":25,"fill":"#fcf9dc"},{"x":38,"y":24,"fill":"#000000"},{"x":38,"y":23,"fill":"#000000"},{"x":38,"y":22,"fill":"#000000"},{"x":38,"y":21,"fill":"#000000"},{"x":38,"y":20,"fill":"#000000"},{"x":38,"y":19,"fill":"#000000"},{"x":38,"y":18,"fill":"#000000"},{"x":38,"y":17,"fill":"#000000"},{"x":38,"y":16,"fill":"#000000"},{"x":38,"y":15,"fill":"#000000"},{"x":38,"y":14,"fill":"#000000"},{"x":38,"y":13,"fill":"#000000"},{"x":38,"y":12,"fill":"#000000"},{"x":38,"y":11,"fill":"#000000"},{"x":38,"y":10,"fill":"#000000"},{"x":38,"y":9,"fill":"#000000"},{"x":38,"y":8,"fill":"#e0e1d4"},{"x":38,"y":7,"fill":"#e0e1d4"},{"x":38,"y":6,"fill":"#e0e1d4"},{"x":38,"y":5,"fill":"#e0e1d4"},{"x":38,"y":4,"fill":"#e0e1d4"},{"x":38,"y":3,"fill":"#e0e1d4"},{"x":38,"y":2,"fill":"#e0e1d4"},{"x":38,"y":1,"fill":"#e0e1d4"},{"x":39,"y":64,"fill":"#0043bf"},{"x":39,"y":63,"fill":"#0043bf"},{"x":39,"y":62,"fill":"#0043bf"},{"x":39,"y":61,"fill":"#0043bf"},{"x":39,"y":60,"fill":"#0043bf"},{"x":39,"y":59,"fill":"#0043bf"},{"x":39,"y":58,"fill":"#0043bf"},{"x":39,"y":57,"fill":"#0043bf"},{"x":39,"y":56,"fill":"#0043bf"},{"x":39,"y":55,"fill":"#0043bf"},{"x":39,"y":54,"fill":"#000000"},{"x":39,"y":53,"fill":"#000000"},{"x":39,"y":52,"fill":"#ff0000"},{"x":39,"y":51,"fill":"#ff0000"},{"x":39,"y":50,"fill":"#ff0000"},{"x":39,"y":49,"fill":"#ff0000"},{"x":39,"y":48,"fill":"#000000"},{"x":39,"y":47,"fill":"#000000"},{"x":39,"y":46,"fill":"#000000"},{"x":39,"y":45,"fill":"#000000"},{"x":39,"y":44,"fill":"#000000"},{"x":39,"y":43,"fill":"#000000"},{"x":39,"y":42,"fill":"#fcf9dc"},{"x":39,"y":41,"fill":"#fcf9dc"},{"x":39,"y":40,"fill":"#dcde99"},{"x":39,"y":39,"fill":"#dcde99"},{"x":39,"y":38,"fill":"#000000"},{"x":39,"y":37,"fill":"#000000"},{"x":39,"y":36,"fill":"#fcf9dc"},{"x":39,"y":35,"fill":"#fcf9dc"},{"x":39,"y":34,"fill":"#fcf9dc"},{"x":39,"y":33,"fill":"#fcf9dc"},{"x":39,"y":32,"fill":"#fcf9dc"},{"x":39,"y":31,"fill":"#fcf9dc"},{"x":39,"y":30,"fill":"#fcf9dc"},{"x":39,"y":29,"fill":"#fcf9dc"},{"x":39,"y":28,"fill":"#fcf9dc"},{"x":39,"y":27,"fill":"#fcf9dc"},{"x":39,"y":26,"fill":"#fcf9dc"},{"x":39,"y":25,"fill":"#fcf9dc"},{"x":39,"y":24,"fill":"#000000"},{"x":39,"y":23,"fill":"#000000"},{"x":39,"y":22,"fill":"#000000"},{"x":39,"y":21,"fill":"#000000"},{"x":39,"y":20,"fill":"#000000"},{"x":39,"y":19,"fill":"#000000"},{"x":39,"y":18,"fill":"#000000"},{"x":39,"y":17,"fill":"#000000"},{"x":39,"y":16,"fill":"#000000"},{"x":39,"y":15,"fill":"#000000"},{"x":39,"y":14,"fill":"#000000"},{"x":39,"y":13,"fill":"#000000"},{"x":39,"y":12,"fill":"#000000"},{"x":39,"y":11,"fill":"#000000"},{"x":39,"y":10,"fill":"#000000"},{"x":39,"y":9,"fill":"#000000"},{"x":39,"y":8,"fill":"#e0e1d4"},{"x":39,"y":7,"fill":"#e0e1d4"},{"x":39,"y":6,"fill":"#e0e1d4"},{"x":39,"y":5,"fill":"#e0e1d4"},{"x":39,"y":4,"fill":"#e0e1d4"},{"x":39,"y":3,"fill":"#e0e1d4"},{"x":39,"y":2,"fill":"#e0e1d4"},{"x":39,"y":1,"fill":"#e0e1d4"},{"x":40,"y":64,"fill":"#0043bf"},{"x":40,"y":63,"fill":"#0043bf"},{"x":40,"y":62,"fill":"#0043bf"},{"x":40,"y":61,"fill":"#0043bf"},{"x":40,"y":60,"fill":"#0043bf"},{"x":40,"y":59,"fill":"#0043bf"},{"x":40,"y":58,"fill":"#0043bf"},{"x":40,"y":57,"fill":"#0043bf"},{"x":40,"y":56,"fill":"#0043bf"},{"x":40,"y":55,"fill":"#0043bf"},{"x":40,"y":54,"fill":"#0043bf"},{"x":40,"y":53,"fill":"#0043bf"},{"x":40,"y":52,"fill":"#000000"},{"x":40,"y":51,"fill":"#000000"},{"x":40,"y":50,"fill":"#ff0000"},{"x":40,"y":49,"fill":"#ff0000"},{"x":40,"y":48,"fill":"#ff0000"},{"x":40,"y":47,"fill":"#ff0000"},{"x":40,"y":46,"fill":"#dcde99"},{"x":40,"y":45,"fill":"#dcde99"},{"x":40,"y":44,"fill":"#000000"},{"x":40,"y":43,"fill":"#000000"},{"x":40,"y":42,"fill":"#fcf9dc"},{"x":40,"y":41,"fill":"#fcf9dc"},{"x":40,"y":40,"fill":"#dcde99"},{"x":40,"y":39,"fill":"#dcde99"},{"x":40,"y":38,"fill":"#000000"},{"x":40,"y":37,"fill":"#000000"},{"x":40,"y":36,"fill":"#000000"},{"x":40,"y":35,"fill":"#000000"},{"x":40,"y":34,"fill":"#000000"},{"x":40,"y":33,"fill":"#000000"},{"x":40,"y":32,"fill":"#fcf9dc"},{"x":40,"y":31,"fill":"#fcf9dc"},{"x":40,"y":30,"fill":"#fcf9dc"},{"x":40,"y":29,"fill":"#fcf9dc"},{"x":40,"y":28,"fill":"#000000"},{"x":40,"y":27,"fill":"#000000"},{"x":40,"y":26,"fill":"#000000"},{"x":40,"y":25,"fill":"#000000"},{"x":40,"y":24,"fill":"#ae6a09"},{"x":40,"y":23,"fill":"#ae6a09"},{"x":40,"y":22,"fill":"#000000"},{"x":40,"y":21,"fill":"#000000"},{"x":40,"y":20,"fill":"#ff7f7f"},{"x":40,"y":19,"fill":"#ff7f7f"},{"x":40,"y":18,"fill":"#ff0000"},{"x":40,"y":17,"fill":"#ff0000"},{"x":40,"y":16,"fill":"#dadada"},{"x":40,"y":15,"fill":"#dadada"},{"x":40,"y":14,"fill":"#dadada"},{"x":40,"y":13,"fill":"#dadada"},{"x":40,"y":12,"fill":"#000000"},{"x":40,"y":11,"fill":"#000000"},{"x":40,"y":10,"fill":"#e0e1d4"},{"x":40,"y":9,"fill":"#e0e1d4"},{"x":40,"y":8,"fill":"#e0e1d4"},{"x":40,"y":7,"fill":"#e0e1d4"},{"x":40,"y":6,"fill":"#e0e1d4"},{"x":40,"y":5,"fill":"#e0e1d4"},{"x":40,"y":4,"fill":"#e0e1d4"},{"x":40,"y":3,"fill":"#e0e1d4"},{"x":40,"y":2,"fill":"#e0e1d4"},{"x":40,"y":1,"fill":"#e0e1d4"},{"x":41,"y":64,"fill":"#0043bf"},{"x":41,"y":63,"fill":"#0043bf"},{"x":41,"y":62,"fill":"#0043bf"},{"x":41,"y":61,"fill":"#0043bf"},{"x":41,"y":60,"fill":"#0043bf"},{"x":41,"y":59,"fill":"#0043bf"},{"x":41,"y":58,"fill":"#0043bf"},{"x":41,"y":57,"fill":"#0043bf"},{"x":41,"y":56,"fill":"#0043bf"},{"x":41,"y":55,"fill":"#0043bf"},{"x":41,"y":54,"fill":"#0043bf"},{"x":41,"y":53,"fill":"#0043bf"},{"x":41,"y":52,"fill":"#000000"},{"x":41,"y":51,"fill":"#000000"},{"x":41,"y":50,"fill":"#ff0000"},{"x":41,"y":49,"fill":"#ff0000"},{"x":41,"y":48,"fill":"#ff0000"},{"x":41,"y":47,"fill":"#ff0000"},{"x":41,"y":46,"fill":"#dcde99"},{"x":41,"y":45,"fill":"#dcde99"},{"x":41,"y":44,"fill":"#000000"},{"x":41,"y":43,"fill":"#000000"},{"x":41,"y":42,"fill":"#fcf9dc"},{"x":41,"y":41,"fill":"#fcf9dc"},{"x":41,"y":40,"fill":"#dcde99"},{"x":41,"y":39,"fill":"#dcde99"},{"x":41,"y":38,"fill":"#000000"},{"x":41,"y":37,"fill":"#000000"},{"x":41,"y":36,"fill":"#000000"},{"x":41,"y":35,"fill":"#000000"},{"x":41,"y":34,"fill":"#000000"},{"x":41,"y":33,"fill":"#000000"},{"x":41,"y":32,"fill":"#fcf9dc"},{"x":41,"y":31,"fill":"#fcf9dc"},{"x":41,"y":30,"fill":"#fcf9dc"},{"x":41,"y":29,"fill":"#fcf9dc"},{"x":41,"y":28,"fill":"#000000"},{"x":41,"y":27,"fill":"#000000"},{"x":41,"y":26,"fill":"#000000"},{"x":41,"y":25,"fill":"#000000"},{"x":41,"y":24,"fill":"#ae6a09"},{"x":41,"y":23,"fill":"#ae6a09"},{"x":41,"y":22,"fill":"#000000"},{"x":41,"y":21,"fill":"#000000"},{"x":41,"y":20,"fill":"#ff7f7f"},{"x":41,"y":19,"fill":"#ff7f7f"},{"x":41,"y":18,"fill":"#ff0000"},{"x":41,"y":17,"fill":"#ff0000"},{"x":41,"y":16,"fill":"#dadada"},{"x":41,"y":15,"fill":"#dadada"},{"x":41,"y":14,"fill":"#dadada"},{"x":41,"y":13,"fill":"#dadada"},{"x":41,"y":12,"fill":"#000000"},{"x":41,"y":11,"fill":"#000000"},{"x":41,"y":10,"fill":"#e0e1d4"},{"x":41,"y":9,"fill":"#e0e1d4"},{"x":41,"y":8,"fill":"#e0e1d4"},{"x":41,"y":7,"fill":"#e0e1d4"},{"x":41,"y":6,"fill":"#e0e1d4"},{"x":41,"y":5,"fill":"#e0e1d4"},{"x":41,"y":4,"fill":"#e0e1d4"},{"x":41,"y":3,"fill":"#e0e1d4"},{"x":41,"y":2,"fill":"#e0e1d4"},{"x":41,"y":1,"fill":"#e0e1d4"},{"x":42,"y":64,"fill":"#0043bf"},{"x":42,"y":63,"fill":"#0043bf"},{"x":42,"y":62,"fill":"#0043bf"},{"x":42,"y":61,"fill":"#0043bf"},{"x":42,"y":60,"fill":"#0043bf"},{"x":42,"y":59,"fill":"#0043bf"},{"x":42,"y":58,"fill":"#0043bf"},{"x":42,"y":57,"fill":"#0043bf"},{"x":42,"y":56,"fill":"#0043bf"},{"x":42,"y":55,"fill":"#0043bf"},{"x":42,"y":54,"fill":"#0043bf"},{"x":42,"y":53,"fill":"#0043bf"},{"x":42,"y":52,"fill":"#0043bf"},{"x":42,"y":51,"fill":"#0043bf"},{"x":42,"y":50,"fill":"#000000"},{"x":42,"y":49,"fill":"#000000"},{"x":42,"y":48,"fill":"#dcde99"},{"x":42,"y":47,"fill":"#dcde99"},{"x":42,"y":46,"fill":"#dcde99"},{"x":42,"y":45,"fill":"#dcde99"},{"x":42,"y":44,"fill":"#dcde99"},{"x":42,"y":43,"fill":"#dcde99"},{"x":42,"y":42,"fill":"#000000"},{"x":42,"y":41,"fill":"#000000"},{"x":42,"y":40,"fill":"#000000"},{"x":42,"y":39,"fill":"#000000"},{"x":42,"y":38,"fill":"#0043bf"},{"x":42,"y":37,"fill":"#0043bf"},{"x":42,"y":36,"fill":"#ffffff"},{"x":42,"y":35,"fill":"#ffffff"},{"x":42,"y":34,"fill":"#0043bf"},{"x":42,"y":33,"fill":"#0043bf"},{"x":42,"y":32,"fill":"#000000"},{"x":42,"y":31,"fill":"#000000"},{"x":42,"y":30,"fill":"#000000"},{"x":42,"y":29,"fill":"#000000"},{"x":42,"y":28,"fill":"#000000"},{"x":42,"y":27,"fill":"#000000"},{"x":42,"y":26,"fill":"#ae6a09"},{"x":42,"y":25,"fill":"#ae6a09"},{"x":42,"y":24,"fill":"#ae6a09"},{"x":42,"y":23,"fill":"#ae6a09"},{"x":42,"y":22,"fill":"#ae6a09"},{"x":42,"y":21,"fill":"#ae6a09"},{"x":42,"y":20,"fill":"#000000"},{"x":42,"y":19,"fill":"#000000"},{"x":42,"y":18,"fill":"#000000"},{"x":42,"y":17,"fill":"#000000"},{"x":42,"y":16,"fill":"#ffffff"},{"x":42,"y":15,"fill":"#ffffff"},{"x":42,"y":14,"fill":"#dadada"},{"x":42,"y":13,"fill":"#dadada"},{"x":42,"y":12,"fill":"#000000"},{"x":42,"y":11,"fill":"#000000"},{"x":42,"y":10,"fill":"#e0e1d4"},{"x":42,"y":9,"fill":"#e0e1d4"},{"x":42,"y":8,"fill":"#e0e1d4"},{"x":42,"y":7,"fill":"#e0e1d4"},{"x":42,"y":6,"fill":"#e0e1d4"},{"x":42,"y":5,"fill":"#e0e1d4"},{"x":42,"y":4,"fill":"#e0e1d4"},{"x":42,"y":3,"fill":"#e0e1d4"},{"x":42,"y":2,"fill":"#e0e1d4"},{"x":42,"y":1,"fill":"#e0e1d4"},{"x":43,"y":64,"fill":"#0043bf"},{"x":43,"y":63,"fill":"#0043bf"},{"x":43,"y":62,"fill":"#0043bf"},{"x":43,"y":61,"fill":"#0043bf"},{"x":43,"y":60,"fill":"#0043bf"},{"x":43,"y":59,"fill":"#0043bf"},{"x":43,"y":58,"fill":"#0043bf"},{"x":43,"y":57,"fill":"#0043bf"},{"x":43,"y":56,"fill":"#0043bf"},{"x":43,"y":55,"fill":"#0043bf"},{"x":43,"y":54,"fill":"#0043bf"},{"x":43,"y":53,"fill":"#0043bf"},{"x":43,"y":52,"fill":"#0043bf"},{"x":43,"y":51,"fill":"#0043bf"},{"x":43,"y":50,"fill":"#000000"},{"x":43,"y":49,"fill":"#000000"},{"x":43,"y":48,"fill":"#dcde99"},{"x":43,"y":47,"fill":"#dcde99"},{"x":43,"y":46,"fill":"#dcde99"},{"x":43,"y":45,"fill":"#dcde99"},{"x":43,"y":44,"fill":"#dcde99"},{"x":43,"y":43,"fill":"#dcde99"},{"x":43,"y":42,"fill":"#000000"},{"x":43,"y":41,"fill":"#000000"},{"x":43,"y":40,"fill":"#000000"},{"x":43,"y":39,"fill":"#000000"},{"x":43,"y":38,"fill":"#0043bf"},{"x":43,"y":37,"fill":"#0043bf"},{"x":43,"y":36,"fill":"#ffffff"},{"x":43,"y":35,"fill":"#ffffff"},{"x":43,"y":34,"fill":"#0043bf"},{"x":43,"y":33,"fill":"#0043bf"},{"x":43,"y":32,"fill":"#000000"},{"x":43,"y":31,"fill":"#000000"},{"x":43,"y":30,"fill":"#000000"},{"x":43,"y":29,"fill":"#000000"},{"x":43,"y":28,"fill":"#000000"},{"x":43,"y":27,"fill":"#000000"},{"x":43,"y":26,"fill":"#ae6a09"},{"x":43,"y":25,"fill":"#ae6a09"},{"x":43,"y":24,"fill":"#ae6a09"},{"x":43,"y":23,"fill":"#ae6a09"},{"x":43,"y":22,"fill":"#ae6a09"},{"x":43,"y":21,"fill":"#ae6a09"},{"x":43,"y":20,"fill":"#000000"},{"x":43,"y":19,"fill":"#000000"},{"x":43,"y":18,"fill":"#000000"},{"x":43,"y":17,"fill":"#000000"},{"x":43,"y":16,"fill":"#ffffff"},{"x":43,"y":15,"fill":"#ffffff"},{"x":43,"y":14,"fill":"#dadada"},{"x":43,"y":13,"fill":"#dadada"},{"x":43,"y":12,"fill":"#000000"},{"x":43,"y":11,"fill":"#000000"},{"x":43,"y":10,"fill":"#e0e1d4"},{"x":43,"y":9,"fill":"#e0e1d4"},{"x":43,"y":8,"fill":"#e0e1d4"},{"x":43,"y":7,"fill":"#e0e1d4"},{"x":43,"y":6,"fill":"#e0e1d4"},{"x":43,"y":5,"fill":"#e0e1d4"},{"x":43,"y":4,"fill":"#e0e1d4"},{"x":43,"y":3,"fill":"#e0e1d4"},{"x":43,"y":2,"fill":"#e0e1d4"},{"x":43,"y":1,"fill":"#e0e1d4"},{"x":44,"y":64,"fill":"#0043bf"},{"x":44,"y":63,"fill":"#0043bf"},{"x":44,"y":62,"fill":"#0043bf"},{"x":44,"y":61,"fill":"#0043bf"},{"x":44,"y":60,"fill":"#0043bf"},{"x":44,"y":59,"fill":"#0043bf"},{"x":44,"y":58,"fill":"#0043bf"},{"x":44,"y":57,"fill":"#0043bf"},{"x":44,"y":56,"fill":"#0043bf"},{"x":44,"y":55,"fill":"#0043bf"},{"x":44,"y":54,"fill":"#0043bf"},{"x":44,"y":53,"fill":"#0043bf"},{"x":44,"y":52,"fill":"#0043bf"},{"x":44,"y":51,"fill":"#0043bf"},{"x":44,"y":50,"fill":"#000000"},{"x":44,"y":49,"fill":"#000000"},{"x":44,"y":48,"fill":"#fcf9dc"},{"x":44,"y":47,"fill":"#fcf9dc"},{"x":44,"y":46,"fill":"#fcf9dc"},{"x":44,"y":45,"fill":"#fcf9dc"},{"x":44,"y":44,"fill":"#dcde99"},{"x":44,"y":43,"fill":"#dcde99"},{"x":44,"y":42,"fill":"#000000"},{"x":44,"y":41,"fill":"#000000"},{"x":44,"y":40,"fill":"#0043bf"},{"x":44,"y":39,"fill":"#0043bf"},{"x":44,"y":38,"fill":"#0043bf"},{"x":44,"y":37,"fill":"#0043bf"},{"x":44,"y":36,"fill":"#0043bf"},{"x":44,"y":35,"fill":"#0043bf"},{"x":44,"y":34,"fill":"#0043bf"},{"x":44,"y":33,"fill":"#0043bf"},{"x":44,"y":32,"fill":"#0043bf"},{"x":44,"y":31,"fill":"#0043bf"},{"x":44,"y":30,"fill":"#0043bf"},{"x":44,"y":29,"fill":"#0043bf"},{"x":44,"y":28,"fill":"#000000"},{"x":44,"y":27,"fill":"#000000"},{"x":44,"y":26,"fill":"#ae6a09"},{"x":44,"y":25,"fill":"#ae6a09"},{"x":44,"y":24,"fill":"#ae6a09"},{"x":44,"y":23,"fill":"#ae6a09"},{"x":44,"y":22,"fill":"#ae6a09"},{"x":44,"y":21,"fill":"#ae6a09"},{"x":44,"y":20,"fill":"#ae6a09"},{"x":44,"y":19,"fill":"#ae6a09"},{"x":44,"y":18,"fill":"#ae6a09"},{"x":44,"y":17,"fill":"#ae6a09"},{"x":44,"y":16,"fill":"#000000"},{"x":44,"y":15,"fill":"#000000"},{"x":44,"y":14,"fill":"#000000"},{"x":44,"y":13,"fill":"#000000"},{"x":44,"y":12,"fill":"#000000"},{"x":44,"y":11,"fill":"#000000"},{"x":44,"y":10,"fill":"#e0e1d4"},{"x":44,"y":9,"fill":"#e0e1d4"},{"x":44,"y":8,"fill":"#e0e1d4"},{"x":44,"y":7,"fill":"#e0e1d4"},{"x":44,"y":6,"fill":"#e0e1d4"},{"x":44,"y":5,"fill":"#e0e1d4"},{"x":44,"y":4,"fill":"#e0e1d4"},{"x":44,"y":3,"fill":"#e0e1d4"},{"x":44,"y":2,"fill":"#e0e1d4"},{"x":44,"y":1,"fill":"#e0e1d4"},{"x":45,"y":64,"fill":"#0043bf"},{"x":45,"y":63,"fill":"#0043bf"},{"x":45,"y":62,"fill":"#0043bf"},{"x":45,"y":61,"fill":"#0043bf"},{"x":45,"y":60,"fill":"#0043bf"},{"x":45,"y":59,"fill":"#0043bf"},{"x":45,"y":58,"fill":"#0043bf"},{"x":45,"y":57,"fill":"#0043bf"},{"x":45,"y":56,"fill":"#0043bf"},{"x":45,"y":55,"fill":"#0043bf"},{"x":45,"y":54,"fill":"#0043bf"},{"x":45,"y":53,"fill":"#0043bf"},{"x":45,"y":52,"fill":"#0043bf"},{"x":45,"y":51,"fill":"#0043bf"},{"x":45,"y":50,"fill":"#000000"},{"x":45,"y":49,"fill":"#000000"},{"x":45,"y":48,"fill":"#fcf9dc"},{"x":45,"y":47,"fill":"#fcf9dc"},{"x":45,"y":46,"fill":"#fcf9dc"},{"x":45,"y":45,"fill":"#fcf9dc"},{"x":45,"y":44,"fill":"#dcde99"},{"x":45,"y":43,"fill":"#dcde99"},{"x":45,"y":42,"fill":"#000000"},{"x":45,"y":41,"fill":"#000000"},{"x":45,"y":40,"fill":"#0043bf"},{"x":45,"y":39,"fill":"#0043bf"},{"x":45,"y":38,"fill":"#0043bf"},{"x":45,"y":37,"fill":"#0043bf"},{"x":45,"y":36,"fill":"#0043bf"},{"x":45,"y":35,"fill":"#0043bf"},{"x":45,"y":34,"fill":"#0043bf"},{"x":45,"y":33,"fill":"#0043bf"},{"x":45,"y":32,"fill":"#0043bf"},{"x":45,"y":31,"fill":"#0043bf"},{"x":45,"y":30,"fill":"#0043bf"},{"x":45,"y":29,"fill":"#0043bf"},{"x":45,"y":28,"fill":"#000000"},{"x":45,"y":27,"fill":"#000000"},{"x":45,"y":26,"fill":"#ae6a09"},{"x":45,"y":25,"fill":"#ae6a09"},{"x":45,"y":24,"fill":"#ae6a09"},{"x":45,"y":23,"fill":"#ae6a09"},{"x":45,"y":22,"fill":"#ae6a09"},{"x":45,"y":21,"fill":"#ae6a09"},{"x":45,"y":20,"fill":"#ae6a09"},{"x":45,"y":19,"fill":"#ae6a09"},{"x":45,"y":18,"fill":"#ae6a09"},{"x":45,"y":17,"fill":"#ae6a09"},{"x":45,"y":16,"fill":"#000000"},{"x":45,"y":15,"fill":"#000000"},{"x":45,"y":14,"fill":"#000000"},{"x":45,"y":13,"fill":"#000000"},{"x":45,"y":12,"fill":"#000000"},{"x":45,"y":11,"fill":"#000000"},{"x":45,"y":10,"fill":"#e0e1d4"},{"x":45,"y":9,"fill":"#e0e1d4"},{"x":45,"y":8,"fill":"#e0e1d4"},{"x":45,"y":7,"fill":"#e0e1d4"},{"x":45,"y":6,"fill":"#e0e1d4"},{"x":45,"y":5,"fill":"#e0e1d4"},{"x":45,"y":4,"fill":"#e0e1d4"},{"x":45,"y":3,"fill":"#e0e1d4"},{"x":45,"y":2,"fill":"#e0e1d4"},{"x":45,"y":1,"fill":"#e0e1d4"},{"x":46,"y":64,"fill":"#0043bf"},{"x":46,"y":63,"fill":"#0043bf"},{"x":46,"y":62,"fill":"#0043bf"},{"x":46,"y":61,"fill":"#0043bf"},{"x":46,"y":60,"fill":"#0043bf"},{"x":46,"y":59,"fill":"#0043bf"},{"x":46,"y":58,"fill":"#0043bf"},{"x":46,"y":57,"fill":"#0043bf"},{"x":46,"y":56,"fill":"#0043bf"},{"x":46,"y":55,"fill":"#0043bf"},{"x":46,"y":54,"fill":"#0043bf"},{"x":46,"y":53,"fill":"#0043bf"},{"x":46,"y":52,"fill":"#0043bf"},{"x":46,"y":51,"fill":"#0043bf"},{"x":46,"y":50,"fill":"#0043bf"},{"x":46,"y":49,"fill":"#0043bf"},{"x":46,"y":48,"fill":"#000000"},{"x":46,"y":47,"fill":"#000000"},{"x":46,"y":46,"fill":"#000000"},{"x":46,"y":45,"fill":"#000000"},{"x":46,"y":44,"fill":"#000000"},{"x":46,"y":43,"fill":"#000000"},{"x":46,"y":42,"fill":"#0043bf"},{"x":46,"y":41,"fill":"#0043bf"},{"x":46,"y":40,"fill":"#0043bf"},{"x":46,"y":39,"fill":"#0043bf"},{"x":46,"y":38,"fill":"#0043bf"},{"x":46,"y":37,"fill":"#0043bf"},{"x":46,"y":36,"fill":"#0043bf"},{"x":46,"y":35,"fill":"#0043bf"},{"x":46,"y":34,"fill":"#0043bf"},{"x":46,"y":33,"fill":"#0043bf"},{"x":46,"y":32,"fill":"#0043bf"},{"x":46,"y":31,"fill":"#0043bf"},{"x":46,"y":30,"fill":"#0043bf"},{"x":46,"y":29,"fill":"#0043bf"},{"x":46,"y":28,"fill":"#000000"},{"x":46,"y":27,"fill":"#000000"},{"x":46,"y":26,"fill":"#cb7b09"},{"x":46,"y":25,"fill":"#cb7b09"},{"x":46,"y":24,"fill":"#ae6a09"},{"x":46,"y":23,"fill":"#ae6a09"},{"x":46,"y":22,"fill":"#ae6a09"},{"x":46,"y":21,"fill":"#ae6a09"},{"x":46,"y":20,"fill":"#ae6a09"},{"x":46,"y":19,"fill":"#ae6a09"},{"x":46,"y":18,"fill":"#ae6a09"},{"x":46,"y":17,"fill":"#ae6a09"},{"x":46,"y":16,"fill":"#ae6a09"},{"x":46,"y":15,"fill":"#ae6a09"},{"x":46,"y":14,"fill":"#ae6a09"},{"x":46,"y":13,"fill":"#ae6a09"},{"x":46,"y":12,"fill":"#000000"},{"x":46,"y":11,"fill":"#000000"},{"x":46,"y":10,"fill":"#e0e1d4"},{"x":46,"y":9,"fill":"#e0e1d4"},{"x":46,"y":8,"fill":"#e0e1d4"},{"x":46,"y":7,"fill":"#e0e1d4"},{"x":46,"y":6,"fill":"#e0e1d4"},{"x":46,"y":5,"fill":"#e0e1d4"},{"x":46,"y":4,"fill":"#e0e1d4"},{"x":46,"y":3,"fill":"#e0e1d4"},{"x":46,"y":2,"fill":"#e0e1d4"},{"x":46,"y":1,"fill":"#e0e1d4"},{"x":47,"y":64,"fill":"#0043bf"},{"x":47,"y":63,"fill":"#0043bf"},{"x":47,"y":62,"fill":"#0043bf"},{"x":47,"y":61,"fill":"#0043bf"},{"x":47,"y":60,"fill":"#0043bf"},{"x":47,"y":59,"fill":"#0043bf"},{"x":47,"y":58,"fill":"#0043bf"},{"x":47,"y":57,"fill":"#0043bf"},{"x":47,"y":56,"fill":"#0043bf"},{"x":47,"y":55,"fill":"#0043bf"},{"x":47,"y":54,"fill":"#0043bf"},{"x":47,"y":53,"fill":"#0043bf"},{"x":47,"y":52,"fill":"#0043bf"},{"x":47,"y":51,"fill":"#0043bf"},{"x":47,"y":50,"fill":"#0043bf"},{"x":47,"y":49,"fill":"#0043bf"},{"x":47,"y":48,"fill":"#000000"},{"x":47,"y":47,"fill":"#000000"},{"x":47,"y":46,"fill":"#000000"},{"x":47,"y":45,"fill":"#000000"},{"x":47,"y":44,"fill":"#000000"},{"x":47,"y":43,"fill":"#000000"},{"x":47,"y":42,"fill":"#0043bf"},{"x":47,"y":41,"fill":"#0043bf"},{"x":47,"y":40,"fill":"#0043bf"},{"x":47,"y":39,"fill":"#0043bf"},{"x":47,"y":38,"fill":"#0043bf"},{"x":47,"y":37,"fill":"#0043bf"},{"x":47,"y":36,"fill":"#0043bf"},{"x":47,"y":35,"fill":"#0043bf"},{"x":47,"y":34,"fill":"#0043bf"},{"x":47,"y":33,"fill":"#0043bf"},{"x":47,"y":32,"fill":"#0043bf"},{"x":47,"y":31,"fill":"#0043bf"},{"x":47,"y":30,"fill":"#0043bf"},{"x":47,"y":29,"fill":"#0043bf"},{"x":47,"y":28,"fill":"#000000"},{"x":47,"y":27,"fill":"#000000"},{"x":47,"y":26,"fill":"#cb7b09"},{"x":47,"y":25,"fill":"#cb7b09"},{"x":47,"y":24,"fill":"#ae6a09"},{"x":47,"y":23,"fill":"#ae6a09"},{"x":47,"y":22,"fill":"#ae6a09"},{"x":47,"y":21,"fill":"#ae6a09"},{"x":47,"y":20,"fill":"#ae6a09"},{"x":47,"y":19,"fill":"#ae6a09"},{"x":47,"y":18,"fill":"#ae6a09"},{"x":47,"y":17,"fill":"#ae6a09"},{"x":47,"y":16,"fill":"#ae6a09"},{"x":47,"y":15,"fill":"#ae6a09"},{"x":47,"y":14,"fill":"#ae6a09"},{"x":47,"y":13,"fill":"#ae6a09"},{"x":47,"y":12,"fill":"#000000"},{"x":47,"y":11,"fill":"#000000"},{"x":47,"y":10,"fill":"#e0e1d4"},{"x":47,"y":9,"fill":"#e0e1d4"},{"x":47,"y":8,"fill":"#e0e1d4"},{"x":47,"y":7,"fill":"#e0e1d4"},{"x":47,"y":6,"fill":"#e0e1d4"},{"x":47,"y":5,"fill":"#e0e1d4"},{"x":47,"y":4,"fill":"#e0e1d4"},{"x":47,"y":3,"fill":"#e0e1d4"},{"x":47,"y":2,"fill":"#e0e1d4"},{"x":47,"y":1,"fill":"#e0e1d4"},{"x":48,"y":64,"fill":"#0043bf"},{"x":48,"y":63,"fill":"#0043bf"},{"x":48,"y":62,"fill":"#ffffff"},{"x":48,"y":61,"fill":"#ffffff"},{"x":48,"y":60,"fill":"#0043bf"},{"x":48,"y":59,"fill":"#0043bf"},{"x":48,"y":58,"fill":"#0043bf"},{"x":48,"y":57,"fill":"#0043bf"},{"x":48,"y":56,"fill":"#0043bf"},{"x":48,"y":55,"fill":"#0043bf"},{"x":48,"y":54,"fill":"#0043bf"},{"x":48,"y":53,"fill":"#0043bf"},{"x":48,"y":52,"fill":"#0043bf"},{"x":48,"y":51,"fill":"#0043bf"},{"x":48,"y":50,"fill":"#0043bf"},{"x":48,"y":49,"fill":"#0043bf"},{"x":48,"y":48,"fill":"#0043bf"},{"x":48,"y":47,"fill":"#0043bf"},{"x":48,"y":46,"fill":"#0043bf"},{"x":48,"y":45,"fill":"#0043bf"},{"x":48,"y":44,"fill":"#0043bf"},{"x":48,"y":43,"fill":"#0043bf"},{"x":48,"y":42,"fill":"#0043bf"},{"x":48,"y":41,"fill":"#0043bf"},{"x":48,"y":40,"fill":"#0043bf"},{"x":48,"y":39,"fill":"#0043bf"},{"x":48,"y":38,"fill":"#0043bf"},{"x":48,"y":37,"fill":"#0043bf"},{"x":48,"y":36,"fill":"#0043bf"},{"x":48,"y":35,"fill":"#0043bf"},{"x":48,"y":34,"fill":"#ffffff"},{"x":48,"y":33,"fill":"#ffffff"},{"x":48,"y":32,"fill":"#0043bf"},{"x":48,"y":31,"fill":"#0043bf"},{"x":48,"y":30,"fill":"#0043bf"},{"x":48,"y":29,"fill":"#0043bf"},{"x":48,"y":28,"fill":"#000000"},{"x":48,"y":27,"fill":"#000000"},{"x":48,"y":26,"fill":"#cb7b09"},{"x":48,"y":25,"fill":"#cb7b09"},{"x":48,"y":24,"fill":"#cb7b09"},{"x":48,"y":23,"fill":"#cb7b09"},{"x":48,"y":22,"fill":"#ae6a09"},{"x":48,"y":21,"fill":"#ae6a09"},{"x":48,"y":20,"fill":"#ae6a09"},{"x":48,"y":19,"fill":"#ae6a09"},{"x":48,"y":18,"fill":"#ae6a09"},{"x":48,"y":17,"fill":"#ae6a09"},{"x":48,"y":16,"fill":"#ae6a09"},{"x":48,"y":15,"fill":"#ae6a09"},{"x":48,"y":14,"fill":"#000000"},{"x":48,"y":13,"fill":"#000000"},{"x":48,"y":12,"fill":"#e0e1d4"},{"x":48,"y":11,"fill":"#e0e1d4"},{"x":48,"y":10,"fill":"#e0e1d4"},{"x":48,"y":9,"fill":"#e0e1d4"},{"x":48,"y":8,"fill":"#e0e1d4"},{"x":48,"y":7,"fill":"#e0e1d4"},{"x":48,"y":6,"fill":"#e0e1d4"},{"x":48,"y":5,"fill":"#e0e1d4"},{"x":48,"y":4,"fill":"#e0e1d4"},{"x":48,"y":3,"fill":"#e0e1d4"},{"x":48,"y":2,"fill":"#e0e1d4"},{"x":48,"y":1,"fill":"#e0e1d4"},{"x":49,"y":64,"fill":"#0043bf"},{"x":49,"y":63,"fill":"#0043bf"},{"x":49,"y":62,"fill":"#ffffff"},{"x":49,"y":61,"fill":"#ffffff"},{"x":49,"y":60,"fill":"#0043bf"},{"x":49,"y":59,"fill":"#0043bf"},{"x":49,"y":58,"fill":"#0043bf"},{"x":49,"y":57,"fill":"#0043bf"},{"x":49,"y":56,"fill":"#0043bf"},{"x":49,"y":55,"fill":"#0043bf"},{"x":49,"y":54,"fill":"#0043bf"},{"x":49,"y":53,"fill":"#0043bf"},{"x":49,"y":52,"fill":"#0043bf"},{"x":49,"y":51,"fill":"#0043bf"},{"x":49,"y":50,"fill":"#0043bf"},{"x":49,"y":49,"fill":"#0043bf"},{"x":49,"y":48,"fill":"#0043bf"},{"x":49,"y":47,"fill":"#0043bf"},{"x":49,"y":46,"fill":"#0043bf"},{"x":49,"y":45,"fill":"#0043bf"},{"x":49,"y":44,"fill":"#0043bf"},{"x":49,"y":43,"fill":"#0043bf"},{"x":49,"y":42,"fill":"#0043bf"},{"x":49,"y":41,"fill":"#0043bf"},{"x":49,"y":40,"fill":"#0043bf"},{"x":49,"y":39,"fill":"#0043bf"},{"x":49,"y":38,"fill":"#0043bf"},{"x":49,"y":37,"fill":"#0043bf"},{"x":49,"y":36,"fill":"#0043bf"},{"x":49,"y":35,"fill":"#0043bf"},{"x":49,"y":34,"fill":"#ffffff"},{"x":49,"y":33,"fill":"#ffffff"},{"x":49,"y":32,"fill":"#0043bf"},{"x":49,"y":31,"fill":"#0043bf"},{"x":49,"y":30,"fill":"#0043bf"},{"x":49,"y":29,"fill":"#0043bf"},{"x":49,"y":28,"fill":"#000000"},{"x":49,"y":27,"fill":"#000000"},{"x":49,"y":26,"fill":"#cb7b09"},{"x":49,"y":25,"fill":"#cb7b09"},{"x":49,"y":24,"fill":"#cb7b09"},{"x":49,"y":23,"fill":"#cb7b09"},{"x":49,"y":22,"fill":"#ae6a09"},{"x":49,"y":21,"fill":"#ae6a09"},{"x":49,"y":20,"fill":"#ae6a09"},{"x":49,"y":19,"fill":"#ae6a09"},{"x":49,"y":18,"fill":"#ae6a09"},{"x":49,"y":17,"fill":"#ae6a09"},{"x":49,"y":16,"fill":"#ae6a09"},{"x":49,"y":15,"fill":"#ae6a09"},{"x":49,"y":14,"fill":"#000000"},{"x":49,"y":13,"fill":"#000000"},{"x":49,"y":12,"fill":"#e0e1d4"},{"x":49,"y":11,"fill":"#e0e1d4"},{"x":49,"y":10,"fill":"#e0e1d4"},{"x":49,"y":9,"fill":"#e0e1d4"},{"x":49,"y":8,"fill":"#e0e1d4"},{"x":49,"y":7,"fill":"#e0e1d4"},{"x":49,"y":6,"fill":"#e0e1d4"},{"x":49,"y":5,"fill":"#e0e1d4"},{"x":49,"y":4,"fill":"#e0e1d4"},{"x":49,"y":3,"fill":"#e0e1d4"},{"x":49,"y":2,"fill":"#e0e1d4"},{"x":49,"y":1,"fill":"#e0e1d4"},{"x":50,"y":64,"fill":"#0043bf"},{"x":50,"y":63,"fill":"#0043bf"},{"x":50,"y":62,"fill":"#0043bf"},{"x":50,"y":61,"fill":"#0043bf"},{"x":50,"y":60,"fill":"#0043bf"},{"x":50,"y":59,"fill":"#0043bf"},{"x":50,"y":58,"fill":"#0043bf"},{"x":50,"y":57,"fill":"#0043bf"},{"x":50,"y":56,"fill":"#0043bf"},{"x":50,"y":55,"fill":"#0043bf"},{"x":50,"y":54,"fill":"#0043bf"},{"x":50,"y":53,"fill":"#0043bf"},{"x":50,"y":52,"fill":"#0043bf"},{"x":50,"y":51,"fill":"#0043bf"},{"x":50,"y":50,"fill":"#0043bf"},{"x":50,"y":49,"fill":"#0043bf"},{"x":50,"y":48,"fill":"#0043bf"},{"x":50,"y":47,"fill":"#0043bf"},{"x":50,"y":46,"fill":"#0043bf"},{"x":50,"y":45,"fill":"#0043bf"},{"x":50,"y":44,"fill":"#0043bf"},{"x":50,"y":43,"fill":"#0043bf"},{"x":50,"y":42,"fill":"#0043bf"},{"x":50,"y":41,"fill":"#0043bf"},{"x":50,"y":40,"fill":"#0043bf"},{"x":50,"y":39,"fill":"#0043bf"},{"x":50,"y":38,"fill":"#0043bf"},{"x":50,"y":37,"fill":"#0043bf"},{"x":50,"y":36,"fill":"#0043bf"},{"x":50,"y":35,"fill":"#0043bf"},{"x":50,"y":34,"fill":"#0043bf"},{"x":50,"y":33,"fill":"#0043bf"},{"x":50,"y":32,"fill":"#0043bf"},{"x":50,"y":31,"fill":"#0043bf"},{"x":50,"y":30,"fill":"#0043bf"},{"x":50,"y":29,"fill":"#0043bf"},{"x":50,"y":28,"fill":"#0043bf"},{"x":50,"y":27,"fill":"#0043bf"},{"x":50,"y":26,"fill":"#000000"},{"x":50,"y":25,"fill":"#000000"},{"x":50,"y":24,"fill":"#cb7b09"},{"x":50,"y":23,"fill":"#cb7b09"},{"x":50,"y":22,"fill":"#cb7b09"},{"x":50,"y":21,"fill":"#cb7b09"},{"x":50,"y":20,"fill":"#cb7b09"},{"x":50,"y":19,"fill":"#cb7b09"},{"x":50,"y":18,"fill":"#ae6a09"},{"x":50,"y":17,"fill":"#ae6a09"},{"x":50,"y":16,"fill":"#ae6a09"},{"x":50,"y":15,"fill":"#ae6a09"},{"x":50,"y":14,"fill":"#000000"},{"x":50,"y":13,"fill":"#000000"},{"x":50,"y":12,"fill":"#e0e1d4"},{"x":50,"y":11,"fill":"#e0e1d4"},{"x":50,"y":10,"fill":"#e0e1d4"},{"x":50,"y":9,"fill":"#e0e1d4"},{"x":50,"y":8,"fill":"#e0e1d4"},{"x":50,"y":7,"fill":"#e0e1d4"},{"x":50,"y":6,"fill":"#e0e1d4"},{"x":50,"y":5,"fill":"#e0e1d4"},{"x":50,"y":4,"fill":"#e0e1d4"},{"x":50,"y":3,"fill":"#e0e1d4"},{"x":50,"y":2,"fill":"#e0e1d4"},{"x":50,"y":1,"fill":"#e0e1d4"},{"x":51,"y":64,"fill":"#0043bf"},{"x":51,"y":63,"fill":"#0043bf"},{"x":51,"y":62,"fill":"#0043bf"},{"x":51,"y":61,"fill":"#0043bf"},{"x":51,"y":60,"fill":"#0043bf"},{"x":51,"y":59,"fill":"#0043bf"},{"x":51,"y":58,"fill":"#0043bf"},{"x":51,"y":57,"fill":"#0043bf"},{"x":51,"y":56,"fill":"#0043bf"},{"x":51,"y":55,"fill":"#0043bf"},{"x":51,"y":54,"fill":"#0043bf"},{"x":51,"y":53,"fill":"#0043bf"},{"x":51,"y":52,"fill":"#0043bf"},{"x":51,"y":51,"fill":"#0043bf"},{"x":51,"y":50,"fill":"#0043bf"},{"x":51,"y":49,"fill":"#0043bf"},{"x":51,"y":48,"fill":"#0043bf"},{"x":51,"y":47,"fill":"#0043bf"},{"x":51,"y":46,"fill":"#0043bf"},{"x":51,"y":45,"fill":"#0043bf"},{"x":51,"y":44,"fill":"#0043bf"},{"x":51,"y":43,"fill":"#0043bf"},{"x":51,"y":42,"fill":"#0043bf"},{"x":51,"y":41,"fill":"#0043bf"},{"x":51,"y":40,"fill":"#0043bf"},{"x":51,"y":39,"fill":"#0043bf"},{"x":51,"y":38,"fill":"#0043bf"},{"x":51,"y":37,"fill":"#0043bf"},{"x":51,"y":36,"fill":"#0043bf"},{"x":51,"y":35,"fill":"#0043bf"},{"x":51,"y":34,"fill":"#0043bf"},{"x":51,"y":33,"fill":"#0043bf"},{"x":51,"y":32,"fill":"#0043bf"},{"x":51,"y":31,"fill":"#0043bf"},{"x":51,"y":30,"fill":"#0043bf"},{"x":51,"y":29,"fill":"#0043bf"},{"x":51,"y":28,"fill":"#0043bf"},{"x":51,"y":27,"fill":"#0043bf"},{"x":51,"y":26,"fill":"#000000"},{"x":51,"y":25,"fill":"#000000"},{"x":51,"y":24,"fill":"#cb7b09"},{"x":51,"y":23,"fill":"#cb7b09"},{"x":51,"y":22,"fill":"#cb7b09"},{"x":51,"y":21,"fill":"#cb7b09"},{"x":51,"y":20,"fill":"#cb7b09"},{"x":51,"y":19,"fill":"#cb7b09"},{"x":51,"y":18,"fill":"#ae6a09"},{"x":51,"y":17,"fill":"#ae6a09"},{"x":51,"y":16,"fill":"#ae6a09"},{"x":51,"y":15,"fill":"#ae6a09"},{"x":51,"y":14,"fill":"#000000"},{"x":51,"y":13,"fill":"#000000"},{"x":51,"y":12,"fill":"#e0e1d4"},{"x":51,"y":11,"fill":"#e0e1d4"},{"x":51,"y":10,"fill":"#e0e1d4"},{"x":51,"y":9,"fill":"#e0e1d4"},{"x":51,"y":8,"fill":"#e0e1d4"},{"x":51,"y":7,"fill":"#e0e1d4"},{"x":51,"y":6,"fill":"#e0e1d4"},{"x":51,"y":5,"fill":"#e0e1d4"},{"x":51,"y":4,"fill":"#e0e1d4"},{"x":51,"y":3,"fill":"#e0e1d4"},{"x":51,"y":2,"fill":"#e0e1d4"},{"x":51,"y":1,"fill":"#e0e1d4"},{"x":52,"y":64,"fill":"#0043bf"},{"x":52,"y":63,"fill":"#0043bf"},{"x":52,"y":62,"fill":"#0043bf"},{"x":52,"y":61,"fill":"#0043bf"},{"x":52,"y":60,"fill":"#0043bf"},{"x":52,"y":59,"fill":"#0043bf"},{"x":52,"y":58,"fill":"#0043bf"},{"x":52,"y":57,"fill":"#0043bf"},{"x":52,"y":56,"fill":"#0043bf"},{"x":52,"y":55,"fill":"#0043bf"},{"x":52,"y":54,"fill":"#0043bf"},{"x":52,"y":53,"fill":"#0043bf"},{"x":52,"y":52,"fill":"#0043bf"},{"x":52,"y":51,"fill":"#0043bf"},{"x":52,"y":50,"fill":"#0043bf"},{"x":52,"y":49,"fill":"#0043bf"},{"x":52,"y":48,"fill":"#0043bf"},{"x":52,"y":47,"fill":"#0043bf"},{"x":52,"y":46,"fill":"#0043bf"},{"x":52,"y":45,"fill":"#0043bf"},{"x":52,"y":44,"fill":"#0043bf"},{"x":52,"y":43,"fill":"#0043bf"},{"x":52,"y":42,"fill":"#ffffff"},{"x":52,"y":41,"fill":"#ffffff"},{"x":52,"y":40,"fill":"#0043bf"},{"x":52,"y":39,"fill":"#0043bf"},{"x":52,"y":38,"fill":"#0043bf"},{"x":52,"y":37,"fill":"#0043bf"},{"x":52,"y":36,"fill":"#0043bf"},{"x":52,"y":35,"fill":"#0043bf"},{"x":52,"y":34,"fill":"#0043bf"},{"x":52,"y":33,"fill":"#0043bf"},{"x":52,"y":32,"fill":"#0043bf"},{"x":52,"y":31,"fill":"#0043bf"},{"x":52,"y":30,"fill":"#0043bf"},{"x":52,"y":29,"fill":"#0043bf"},{"x":52,"y":28,"fill":"#0043bf"},{"x":52,"y":27,"fill":"#0043bf"},{"x":52,"y":26,"fill":"#0043bf"},{"x":52,"y":25,"fill":"#0043bf"},{"x":52,"y":24,"fill":"#000000"},{"x":52,"y":23,"fill":"#000000"},{"x":52,"y":22,"fill":"#000000"},{"x":52,"y":21,"fill":"#000000"},{"x":52,"y":20,"fill":"#000000"},{"x":52,"y":19,"fill":"#000000"},{"x":52,"y":18,"fill":"#000000"},{"x":52,"y":17,"fill":"#000000"},{"x":52,"y":16,"fill":"#000000"},{"x":52,"y":15,"fill":"#000000"},{"x":52,"y":14,"fill":"#e0e1d4"},{"x":52,"y":13,"fill":"#e0e1d4"},{"x":52,"y":12,"fill":"#e0e1d4"},{"x":52,"y":11,"fill":"#e0e1d4"},{"x":52,"y":10,"fill":"#e0e1d4"},{"x":52,"y":9,"fill":"#e0e1d4"},{"x":52,"y":8,"fill":"#e0e1d4"},{"x":52,"y":7,"fill":"#e0e1d4"},{"x":52,"y":6,"fill":"#e0e1d4"},{"x":52,"y":5,"fill":"#e0e1d4"},{"x":52,"y":4,"fill":"#e0e1d4"},{"x":52,"y":3,"fill":"#e0e1d4"},{"x":52,"y":2,"fill":"#e0e1d4"},{"x":52,"y":1,"fill":"#e0e1d4"},{"x":53,"y":64,"fill":"#0043bf"},{"x":53,"y":63,"fill":"#0043bf"},{"x":53,"y":62,"fill":"#0043bf"},{"x":53,"y":61,"fill":"#0043bf"},{"x":53,"y":60,"fill":"#0043bf"},{"x":53,"y":59,"fill":"#0043bf"},{"x":53,"y":58,"fill":"#0043bf"},{"x":53,"y":57,"fill":"#0043bf"},{"x":53,"y":56,"fill":"#0043bf"},{"x":53,"y":55,"fill":"#0043bf"},{"x":53,"y":54,"fill":"#0043bf"},{"x":53,"y":53,"fill":"#0043bf"},{"x":53,"y":52,"fill":"#0043bf"},{"x":53,"y":51,"fill":"#0043bf"},{"x":53,"y":50,"fill":"#0043bf"},{"x":53,"y":49,"fill":"#0043bf"},{"x":53,"y":48,"fill":"#0043bf"},{"x":53,"y":47,"fill":"#0043bf"},{"x":53,"y":46,"fill":"#0043bf"},{"x":53,"y":45,"fill":"#0043bf"},{"x":53,"y":44,"fill":"#0043bf"},{"x":53,"y":43,"fill":"#0043bf"},{"x":53,"y":42,"fill":"#ffffff"},{"x":53,"y":41,"fill":"#ffffff"},{"x":53,"y":40,"fill":"#0043bf"},{"x":53,"y":39,"fill":"#0043bf"},{"x":53,"y":38,"fill":"#0043bf"},{"x":53,"y":37,"fill":"#0043bf"},{"x":53,"y":36,"fill":"#0043bf"},{"x":53,"y":35,"fill":"#0043bf"},{"x":53,"y":34,"fill":"#0043bf"},{"x":53,"y":33,"fill":"#0043bf"},{"x":53,"y":32,"fill":"#0043bf"},{"x":53,"y":31,"fill":"#0043bf"},{"x":53,"y":30,"fill":"#0043bf"},{"x":53,"y":29,"fill":"#0043bf"},{"x":53,"y":28,"fill":"#0043bf"},{"x":53,"y":27,"fill":"#0043bf"},{"x":53,"y":26,"fill":"#0043bf"},{"x":53,"y":25,"fill":"#0043bf"},{"x":53,"y":24,"fill":"#000000"},{"x":53,"y":23,"fill":"#000000"},{"x":53,"y":22,"fill":"#000000"},{"x":53,"y":21,"fill":"#000000"},{"x":53,"y":20,"fill":"#000000"},{"x":53,"y":19,"fill":"#000000"},{"x":53,"y":18,"fill":"#000000"},{"x":53,"y":17,"fill":"#000000"},{"x":53,"y":16,"fill":"#000000"},{"x":53,"y":15,"fill":"#000000"},{"x":53,"y":14,"fill":"#e0e1d4"},{"x":53,"y":13,"fill":"#e0e1d4"},{"x":53,"y":12,"fill":"#e0e1d4"},{"x":53,"y":11,"fill":"#e0e1d4"},{"x":53,"y":10,"fill":"#e0e1d4"},{"x":53,"y":9,"fill":"#e0e1d4"},{"x":53,"y":8,"fill":"#e0e1d4"},{"x":53,"y":7,"fill":"#e0e1d4"},{"x":53,"y":6,"fill":"#e0e1d4"},{"x":53,"y":5,"fill":"#e0e1d4"},{"x":53,"y":4,"fill":"#e0e1d4"},{"x":53,"y":3,"fill":"#e0e1d4"},{"x":53,"y":2,"fill":"#e0e1d4"},{"x":53,"y":1,"fill":"#e0e1d4"},{"x":54,"y":64,"fill":"#0043bf"},{"x":54,"y":63,"fill":"#0043bf"},{"x":54,"y":62,"fill":"#0043bf"},{"x":54,"y":61,"fill":"#0043bf"},{"x":54,"y":60,"fill":"#0043bf"},{"x":54,"y":59,"fill":"#0043bf"},{"x":54,"y":58,"fill":"#0043bf"},{"x":54,"y":57,"fill":"#0043bf"},{"x":54,"y":56,"fill":"#0043bf"},{"x":54,"y":55,"fill":"#0043bf"},{"x":54,"y":54,"fill":"#0043bf"},{"x":54,"y":53,"fill":"#0043bf"},{"x":54,"y":52,"fill":"#0043bf"},{"x":54,"y":51,"fill":"#0043bf"},{"x":54,"y":50,"fill":"#0043bf"},{"x":54,"y":49,"fill":"#0043bf"},{"x":54,"y":48,"fill":"#0043bf"},{"x":54,"y":47,"fill":"#0043bf"},{"x":54,"y":46,"fill":"#0043bf"},{"x":54,"y":45,"fill":"#0043bf"},{"x":54,"y":44,"fill":"#0043bf"},{"x":54,"y":43,"fill":"#0043bf"},{"x":54,"y":42,"fill":"#0043bf"},{"x":54,"y":41,"fill":"#0043bf"},{"x":54,"y":40,"fill":"#0043bf"},{"x":54,"y":39,"fill":"#0043bf"},{"x":54,"y":38,"fill":"#0043bf"},{"x":54,"y":37,"fill":"#0043bf"},{"x":54,"y":36,"fill":"#0043bf"},{"x":54,"y":35,"fill":"#0043bf"},{"x":54,"y":34,"fill":"#0043bf"},{"x":54,"y":33,"fill":"#0043bf"},{"x":54,"y":32,"fill":"#0043bf"},{"x":54,"y":31,"fill":"#0043bf"},{"x":54,"y":30,"fill":"#0043bf"},{"x":54,"y":29,"fill":"#0043bf"},{"x":54,"y":28,"fill":"#0043bf"},{"x":54,"y":27,"fill":"#0043bf"},{"x":54,"y":26,"fill":"#0043bf"},{"x":54,"y":25,"fill":"#0043bf"},{"x":54,"y":24,"fill":"#0043bf"},{"x":54,"y":23,"fill":"#0043bf"},{"x":54,"y":22,"fill":"#0043bf"},{"x":54,"y":21,"fill":"#0043bf"},{"x":54,"y":20,"fill":"#97b0d1"},{"x":54,"y":19,"fill":"#97b0d1"},{"x":54,"y":18,"fill":"#ced6d5"},{"x":54,"y":17,"fill":"#ced6d5"},{"x":54,"y":16,"fill":"#e0e1d4"},{"x":54,"y":15,"fill":"#e0e1d4"},{"x":54,"y":14,"fill":"#e0e1d4"},{"x":54,"y":13,"fill":"#e0e1d4"},{"x":54,"y":12,"fill":"#e0e1d4"},{"x":54,"y":11,"fill":"#e0e1d4"},{"x":54,"y":10,"fill":"#e0e1d4"},{"x":54,"y":9,"fill":"#e0e1d4"},{"x":54,"y":8,"fill":"#e0e1d4"},{"x":54,"y":7,"fill":"#e0e1d4"},{"x":54,"y":6,"fill":"#e0e1d4"},{"x":54,"y":5,"fill":"#e0e1d4"},{"x":54,"y":4,"fill":"#e0e1d4"},{"x":54,"y":3,"fill":"#e0e1d4"},{"x":54,"y":2,"fill":"#e0e1d4"},{"x":54,"y":1,"fill":"#e0e1d4"},{"x":55,"y":64,"fill":"#0043bf"},{"x":55,"y":63,"fill":"#0043bf"},{"x":55,"y":62,"fill":"#0043bf"},{"x":55,"y":61,"fill":"#0043bf"},{"x":55,"y":60,"fill":"#0043bf"},{"x":55,"y":59,"fill":"#0043bf"},{"x":55,"y":58,"fill":"#0043bf"},{"x":55,"y":57,"fill":"#0043bf"},{"x":55,"y":56,"fill":"#0043bf"},{"x":55,"y":55,"fill":"#0043bf"},{"x":55,"y":54,"fill":"#0043bf"},{"x":55,"y":53,"fill":"#0043bf"},{"x":55,"y":52,"fill":"#0043bf"},{"x":55,"y":51,"fill":"#0043bf"},{"x":55,"y":50,"fill":"#0043bf"},{"x":55,"y":49,"fill":"#0043bf"},{"x":55,"y":48,"fill":"#0043bf"},{"x":55,"y":47,"fill":"#0043bf"},{"x":55,"y":46,"fill":"#0043bf"},{"x":55,"y":45,"fill":"#0043bf"},{"x":55,"y":44,"fill":"#0043bf"},{"x":55,"y":43,"fill":"#0043bf"},{"x":55,"y":42,"fill":"#0043bf"},{"x":55,"y":41,"fill":"#0043bf"},{"x":55,"y":40,"fill":"#0043bf"},{"x":55,"y":39,"fill":"#0043bf"},{"x":55,"y":38,"fill":"#0043bf"},{"x":55,"y":37,"fill":"#0043bf"},{"x":55,"y":36,"fill":"#0043bf"},{"x":55,"y":35,"fill":"#0043bf"},{"x":55,"y":34,"fill":"#0043bf"},{"x":55,"y":33,"fill":"#0043bf"},{"x":55,"y":32,"fill":"#0043bf"},{"x":55,"y":31,"fill":"#0043bf"},{"x":55,"y":30,"fill":"#0043bf"},{"x":55,"y":29,"fill":"#0043bf"},{"x":55,"y":28,"fill":"#0043bf"},{"x":55,"y":27,"fill":"#0043bf"},{"x":55,"y":26,"fill":"#0043bf"},{"x":55,"y":25,"fill":"#0043bf"},{"x":55,"y":24,"fill":"#0043bf"},{"x":55,"y":23,"fill":"#0043bf"},{"x":55,"y":22,"fill":"#0043bf"},{"x":55,"y":21,"fill":"#0043bf"},{"x":55,"y":20,"fill":"#97b0d1"},{"x":55,"y":19,"fill":"#97b0d1"},{"x":55,"y":18,"fill":"#ced6d5"},{"x":55,"y":17,"fill":"#ced6d5"},{"x":55,"y":16,"fill":"#e0e1d4"},{"x":55,"y":15,"fill":"#e0e1d4"},{"x":55,"y":14,"fill":"#e0e1d4"},{"x":55,"y":13,"fill":"#e0e1d4"},{"x":55,"y":12,"fill":"#e0e1d4"},{"x":55,"y":11,"fill":"#e0e1d4"},{"x":55,"y":10,"fill":"#e0e1d4"},{"x":55,"y":9,"fill":"#e0e1d4"},{"x":55,"y":8,"fill":"#e0e1d4"},{"x":55,"y":7,"fill":"#e0e1d4"},{"x":55,"y":6,"fill":"#e0e1d4"},{"x":55,"y":5,"fill":"#e0e1d4"},{"x":55,"y":4,"fill":"#e0e1d4"},{"x":55,"y":3,"fill":"#e0e1d4"},{"x":55,"y":2,"fill":"#e0e1d4"},{"x":55,"y":1,"fill":"#e0e1d4"},{"x":56,"y":64,"fill":"#0043bf"},{"x":56,"y":63,"fill":"#0043bf"},{"x":56,"y":62,"fill":"#0043bf"},{"x":56,"y":61,"fill":"#0043bf"},{"x":56,"y":60,"fill":"#ffffff"},{"x":56,"y":59,"fill":"#ffffff"},{"x":56,"y":58,"fill":"#0043bf"},{"x":56,"y":57,"fill":"#0043bf"},{"x":56,"y":56,"fill":"#0043bf"},{"x":56,"y":55,"fill":"#0043bf"},{"x":56,"y":54,"fill":"#0043bf"},{"x":56,"y":53,"fill":"#0043bf"},{"x":56,"y":52,"fill":"#0043bf"},{"x":56,"y":51,"fill":"#0043bf"},{"x":56,"y":50,"fill":"#0043bf"},{"x":56,"y":49,"fill":"#0043bf"},{"x":56,"y":48,"fill":"#0043bf"},{"x":56,"y":47,"fill":"#0043bf"},{"x":56,"y":46,"fill":"#0043bf"},{"x":56,"y":45,"fill":"#0043bf"},{"x":56,"y":44,"fill":"#0043bf"},{"x":56,"y":43,"fill":"#0043bf"},{"x":56,"y":42,"fill":"#0043bf"},{"x":56,"y":41,"fill":"#0043bf"},{"x":56,"y":40,"fill":"#0043bf"},{"x":56,"y":39,"fill":"#0043bf"},{"x":56,"y":38,"fill":"#0043bf"},{"x":56,"y":37,"fill":"#0043bf"},{"x":56,"y":36,"fill":"#0043bf"},{"x":56,"y":35,"fill":"#0043bf"},{"x":56,"y":34,"fill":"#0043bf"},{"x":56,"y":33,"fill":"#0043bf"},{"x":56,"y":32,"fill":"#0043bf"},{"x":56,"y":31,"fill":"#0043bf"},{"x":56,"y":30,"fill":"#0043bf"},{"x":56,"y":29,"fill":"#0043bf"},{"x":56,"y":28,"fill":"#0043bf"},{"x":56,"y":27,"fill":"#0043bf"},{"x":56,"y":26,"fill":"#0043bf"},{"x":56,"y":25,"fill":"#0043bf"},{"x":56,"y":24,"fill":"#0043bf"},{"x":56,"y":23,"fill":"#0043bf"},{"x":56,"y":22,"fill":"#0043bf"},{"x":56,"y":21,"fill":"#0043bf"},{"x":56,"y":20,"fill":"#b7c7d4"},{"x":56,"y":19,"fill":"#b7c7d4"},{"x":56,"y":18,"fill":"#e0e1d4"},{"x":56,"y":17,"fill":"#e0e1d4"},{"x":56,"y":16,"fill":"#e0e1d4"},{"x":56,"y":15,"fill":"#e0e1d4"},{"x":56,"y":14,"fill":"#e0e1d4"},{"x":56,"y":13,"fill":"#e0e1d4"},{"x":56,"y":12,"fill":"#e0e1d4"},{"x":56,"y":11,"fill":"#e0e1d4"},{"x":56,"y":10,"fill":"#e0e1d4"},{"x":56,"y":9,"fill":"#e0e1d4"},{"x":56,"y":8,"fill":"#e0e1d4"},{"x":56,"y":7,"fill":"#e0e1d4"},{"x":56,"y":6,"fill":"#e0e1d4"},{"x":56,"y":5,"fill":"#e0e1d4"},{"x":56,"y":4,"fill":"#e0e1d4"},{"x":56,"y":3,"fill":"#e0e1d4"},{"x":56,"y":2,"fill":"#e0e1d4"},{"x":56,"y":1,"fill":"#e0e1d4"},{"x":57,"y":64,"fill":"#0043bf"},{"x":57,"y":63,"fill":"#0043bf"},{"x":57,"y":62,"fill":"#0043bf"},{"x":57,"y":61,"fill":"#0043bf"},{"x":57,"y":60,"fill":"#ffffff"},{"x":57,"y":59,"fill":"#ffffff"},{"x":57,"y":58,"fill":"#0043bf"},{"x":57,"y":57,"fill":"#0043bf"},{"x":57,"y":56,"fill":"#0043bf"},{"x":57,"y":55,"fill":"#0043bf"},{"x":57,"y":54,"fill":"#0043bf"},{"x":57,"y":53,"fill":"#0043bf"},{"x":57,"y":52,"fill":"#0043bf"},{"x":57,"y":51,"fill":"#0043bf"},{"x":57,"y":50,"fill":"#0043bf"},{"x":57,"y":49,"fill":"#0043bf"},{"x":57,"y":48,"fill":"#0043bf"},{"x":57,"y":47,"fill":"#0043bf"},{"x":57,"y":46,"fill":"#0043bf"},{"x":57,"y":45,"fill":"#0043bf"},{"x":57,"y":44,"fill":"#0043bf"},{"x":57,"y":43,"fill":"#0043bf"},{"x":57,"y":42,"fill":"#0043bf"},{"x":57,"y":41,"fill":"#0043bf"},{"x":57,"y":40,"fill":"#0043bf"},{"x":57,"y":39,"fill":"#0043bf"},{"x":57,"y":38,"fill":"#0043bf"},{"x":57,"y":37,"fill":"#0043bf"},{"x":57,"y":36,"fill":"#0043bf"},{"x":57,"y":35,"fill":"#0043bf"},{"x":57,"y":34,"fill":"#0043bf"},{"x":57,"y":33,"fill":"#0043bf"},{"x":57,"y":32,"fill":"#0043bf"},{"x":57,"y":31,"fill":"#0043bf"},{"x":57,"y":30,"fill":"#0043bf"},{"x":57,"y":29,"fill":"#0043bf"},{"x":57,"y":28,"fill":"#0043bf"},{"x":57,"y":27,"fill":"#0043bf"},{"x":57,"y":26,"fill":"#0043bf"},{"x":57,"y":25,"fill":"#0043bf"},{"x":57,"y":24,"fill":"#0043bf"},{"x":57,"y":23,"fill":"#0043bf"},{"x":57,"y":22,"fill":"#0043bf"},{"x":57,"y":21,"fill":"#0043bf"},{"x":57,"y":20,"fill":"#b7c7d4"},{"x":57,"y":19,"fill":"#b7c7d4"},{"x":57,"y":18,"fill":"#e0e1d4"},{"x":57,"y":17,"fill":"#e0e1d4"},{"x":57,"y":16,"fill":"#e0e1d4"},{"x":57,"y":15,"fill":"#e0e1d4"},{"x":57,"y":14,"fill":"#e0e1d4"},{"x":57,"y":13,"fill":"#e0e1d4"},{"x":57,"y":12,"fill":"#e0e1d4"},{"x":57,"y":11,"fill":"#e0e1d4"},{"x":57,"y":10,"fill":"#e0e1d4"},{"x":57,"y":9,"fill":"#e0e1d4"},{"x":57,"y":8,"fill":"#e0e1d4"},{"x":57,"y":7,"fill":"#e0e1d4"},{"x":57,"y":6,"fill":"#e0e1d4"},{"x":57,"y":5,"fill":"#e0e1d4"},{"x":57,"y":4,"fill":"#e0e1d4"},{"x":57,"y":3,"fill":"#e0e1d4"},{"x":57,"y":2,"fill":"#e0e1d4"},{"x":57,"y":1,"fill":"#e0e1d4"},{"x":58,"y":64,"fill":"#0043bf"},{"x":58,"y":63,"fill":"#0043bf"},{"x":58,"y":62,"fill":"#0043bf"},{"x":58,"y":61,"fill":"#0043bf"},{"x":58,"y":60,"fill":"#0043bf"},{"x":58,"y":59,"fill":"#0043bf"},{"x":58,"y":58,"fill":"#0043bf"},{"x":58,"y":57,"fill":"#0043bf"},{"x":58,"y":56,"fill":"#0043bf"},{"x":58,"y":55,"fill":"#0043bf"},{"x":58,"y":54,"fill":"#0043bf"},{"x":58,"y":53,"fill":"#0043bf"},{"x":58,"y":52,"fill":"#0043bf"},{"x":58,"y":51,"fill":"#0043bf"},{"x":58,"y":50,"fill":"#0043bf"},{"x":58,"y":49,"fill":"#0043bf"},{"x":58,"y":48,"fill":"#0043bf"},{"x":58,"y":47,"fill":"#0043bf"},{"x":58,"y":46,"fill":"#0043bf"},{"x":58,"y":45,"fill":"#0043bf"},{"x":58,"y":44,"fill":"#0043bf"},{"x":58,"y":43,"fill":"#0043bf"},{"x":58,"y":42,"fill":"#0043bf"},{"x":58,"y":41,"fill":"#0043bf"},{"x":58,"y":40,"fill":"#0043bf"},{"x":58,"y":39,"fill":"#0043bf"},{"x":58,"y":38,"fill":"#0043bf"},{"x":58,"y":37,"fill":"#0043bf"},{"x":58,"y":36,"fill":"#0043bf"},{"x":58,"y":35,"fill":"#0043bf"},{"x":58,"y":34,"fill":"#0043bf"},{"x":58,"y":33,"fill":"#0043bf"},{"x":58,"y":32,"fill":"#0043bf"},{"x":58,"y":31,"fill":"#0043bf"},{"x":58,"y":30,"fill":"#0043bf"},{"x":58,"y":29,"fill":"#0043bf"},{"x":58,"y":28,"fill":"#0043bf"},{"x":58,"y":27,"fill":"#0043bf"},{"x":58,"y":26,"fill":"#0043bf"},{"x":58,"y":25,"fill":"#0043bf"},{"x":58,"y":24,"fill":"#0043bf"},{"x":58,"y":23,"fill":"#0043bf"},{"x":58,"y":22,"fill":"#0043bf"},{"x":58,"y":21,"fill":"#0043bf"},{"x":58,"y":20,"fill":"#b3c4d3"},{"x":58,"y":19,"fill":"#b3c4d3"},{"x":58,"y":18,"fill":"#e0e1d4"},{"x":58,"y":17,"fill":"#e0e1d4"},{"x":58,"y":16,"fill":"#e0e1d4"},{"x":58,"y":15,"fill":"#e0e1d4"},{"x":58,"y":14,"fill":"#e0e1d4"},{"x":58,"y":13,"fill":"#e0e1d4"},{"x":58,"y":12,"fill":"#e0e1d4"},{"x":58,"y":11,"fill":"#e0e1d4"},{"x":58,"y":10,"fill":"#e0e1d4"},{"x":58,"y":9,"fill":"#e0e1d4"},{"x":58,"y":8,"fill":"#e0e1d4"},{"x":58,"y":7,"fill":"#e0e1d4"},{"x":58,"y":6,"fill":"#e0e1d4"},{"x":58,"y":5,"fill":"#e0e1d4"},{"x":58,"y":4,"fill":"#e0e1d4"},{"x":58,"y":3,"fill":"#e0e1d4"},{"x":58,"y":2,"fill":"#e0e1d4"},{"x":58,"y":1,"fill":"#e0e1d4"},{"x":59,"y":64,"fill":"#0043bf"},{"x":59,"y":63,"fill":"#0043bf"},{"x":59,"y":62,"fill":"#0043bf"},{"x":59,"y":61,"fill":"#0043bf"},{"x":59,"y":60,"fill":"#0043bf"},{"x":59,"y":59,"fill":"#0043bf"},{"x":59,"y":58,"fill":"#0043bf"},{"x":59,"y":57,"fill":"#0043bf"},{"x":59,"y":56,"fill":"#0043bf"},{"x":59,"y":55,"fill":"#0043bf"},{"x":59,"y":54,"fill":"#0043bf"},{"x":59,"y":53,"fill":"#0043bf"},{"x":59,"y":52,"fill":"#0043bf"},{"x":59,"y":51,"fill":"#0043bf"},{"x":59,"y":50,"fill":"#0043bf"},{"x":59,"y":49,"fill":"#0043bf"},{"x":59,"y":48,"fill":"#0043bf"},{"x":59,"y":47,"fill":"#0043bf"},{"x":59,"y":46,"fill":"#0043bf"},{"x":59,"y":45,"fill":"#0043bf"},{"x":59,"y":44,"fill":"#0043bf"},{"x":59,"y":43,"fill":"#0043bf"},{"x":59,"y":42,"fill":"#0043bf"},{"x":59,"y":41,"fill":"#0043bf"},{"x":59,"y":40,"fill":"#0043bf"},{"x":59,"y":39,"fill":"#0043bf"},{"x":59,"y":38,"fill":"#0043bf"},{"x":59,"y":37,"fill":"#0043bf"},{"x":59,"y":36,"fill":"#0043bf"},{"x":59,"y":35,"fill":"#0043bf"},{"x":59,"y":34,"fill":"#0043bf"},{"x":59,"y":33,"fill":"#0043bf"},{"x":59,"y":32,"fill":"#0043bf"},{"x":59,"y":31,"fill":"#0043bf"},{"x":59,"y":30,"fill":"#0043bf"},{"x":59,"y":29,"fill":"#0043bf"},{"x":59,"y":28,"fill":"#0043bf"},{"x":59,"y":27,"fill":"#0043bf"},{"x":59,"y":26,"fill":"#0043bf"},{"x":59,"y":25,"fill":"#0043bf"},{"x":59,"y":24,"fill":"#0043bf"},{"x":59,"y":23,"fill":"#0043bf"},{"x":59,"y":22,"fill":"#0043bf"},{"x":59,"y":21,"fill":"#0043bf"},{"x":59,"y":20,"fill":"#b3c4d3"},{"x":59,"y":19,"fill":"#b3c4d3"},{"x":59,"y":18,"fill":"#e0e1d4"},{"x":59,"y":17,"fill":"#e0e1d4"},{"x":59,"y":16,"fill":"#e0e1d4"},{"x":59,"y":15,"fill":"#e0e1d4"},{"x":59,"y":14,"fill":"#e0e1d4"},{"x":59,"y":13,"fill":"#e0e1d4"},{"x":59,"y":12,"fill":"#e0e1d4"},{"x":59,"y":11,"fill":"#e0e1d4"},{"x":59,"y":10,"fill":"#e0e1d4"},{"x":59,"y":9,"fill":"#e0e1d4"},{"x":59,"y":8,"fill":"#e0e1d4"},{"x":59,"y":7,"fill":"#e0e1d4"},{"x":59,"y":6,"fill":"#e0e1d4"},{"x":59,"y":5,"fill":"#e0e1d4"},{"x":59,"y":4,"fill":"#e0e1d4"},{"x":59,"y":3,"fill":"#e0e1d4"},{"x":59,"y":2,"fill":"#e0e1d4"},{"x":59,"y":1,"fill":"#e0e1d4"},{"x":60,"y":64,"fill":"#0043bf"},{"x":60,"y":63,"fill":"#0043bf"},{"x":60,"y":62,"fill":"#0043bf"},{"x":60,"y":61,"fill":"#0043bf"},{"x":60,"y":60,"fill":"#0043bf"},{"x":60,"y":59,"fill":"#0043bf"},{"x":60,"y":58,"fill":"#0043bf"},{"x":60,"y":57,"fill":"#0043bf"},{"x":60,"y":56,"fill":"#0043bf"},{"x":60,"y":55,"fill":"#0043bf"},{"x":60,"y":54,"fill":"#0043bf"},{"x":60,"y":53,"fill":"#0043bf"},{"x":60,"y":52,"fill":"#ffffff"},{"x":60,"y":51,"fill":"#ffffff"},{"x":60,"y":50,"fill":"#0043bf"},{"x":60,"y":49,"fill":"#0043bf"},{"x":60,"y":48,"fill":"#0043bf"},{"x":60,"y":47,"fill":"#0043bf"},{"x":60,"y":46,"fill":"#0043bf"},{"x":60,"y":45,"fill":"#0043bf"},{"x":60,"y":44,"fill":"#0043bf"},{"x":60,"y":43,"fill":"#0043bf"},{"x":60,"y":42,"fill":"#0043bf"},{"x":60,"y":41,"fill":"#0043bf"},{"x":60,"y":40,"fill":"#0043bf"},{"x":60,"y":39,"fill":"#0043bf"},{"x":60,"y":38,"fill":"#0043bf"},{"x":60,"y":37,"fill":"#0043bf"},{"x":60,"y":36,"fill":"#0043bf"},{"x":60,"y":35,"fill":"#0043bf"},{"x":60,"y":34,"fill":"#0043bf"},{"x":60,"y":33,"fill":"#0043bf"},{"x":60,"y":32,"fill":"#0043bf"},{"x":60,"y":31,"fill":"#0043bf"},{"x":60,"y":30,"fill":"#0043bf"},{"x":60,"y":29,"fill":"#0043bf"},{"x":60,"y":28,"fill":"#0043bf"},{"x":60,"y":27,"fill":"#0043bf"},{"x":60,"y":26,"fill":"#0043bf"},{"x":60,"y":25,"fill":"#0043bf"},{"x":60,"y":24,"fill":"#0043bf"},{"x":60,"y":23,"fill":"#0043bf"},{"x":60,"y":22,"fill":"#0043bf"},{"x":60,"y":21,"fill":"#0043bf"},{"x":60,"y":20,"fill":"#b3c4d3"},{"x":60,"y":19,"fill":"#b3c4d3"},{"x":60,"y":18,"fill":"#e0e1d4"},{"x":60,"y":17,"fill":"#e0e1d4"},{"x":60,"y":16,"fill":"#e0e1d4"},{"x":60,"y":15,"fill":"#e0e1d4"},{"x":60,"y":14,"fill":"#e0e1d4"},{"x":60,"y":13,"fill":"#e0e1d4"},{"x":60,"y":12,"fill":"#e0e1d4"},{"x":60,"y":11,"fill":"#e0e1d4"},{"x":60,"y":10,"fill":"#e0e1d4"},{"x":60,"y":9,"fill":"#e0e1d4"},{"x":60,"y":8,"fill":"#e0e1d4"},{"x":60,"y":7,"fill":"#e0e1d4"},{"x":60,"y":6,"fill":"#e0e1d4"},{"x":60,"y":5,"fill":"#e0e1d4"},{"x":60,"y":4,"fill":"#e0e1d4"},{"x":60,"y":3,"fill":"#e0e1d4"},{"x":60,"y":2,"fill":"#e0e1d4"},{"x":60,"y":1,"fill":"#e0e1d4"},{"x":61,"y":64,"fill":"#0043bf"},{"x":61,"y":63,"fill":"#0043bf"},{"x":61,"y":62,"fill":"#0043bf"},{"x":61,"y":61,"fill":"#0043bf"},{"x":61,"y":60,"fill":"#0043bf"},{"x":61,"y":59,"fill":"#0043bf"},{"x":61,"y":58,"fill":"#0043bf"},{"x":61,"y":57,"fill":"#0043bf"},{"x":61,"y":56,"fill":"#0043bf"},{"x":61,"y":55,"fill":"#0043bf"},{"x":61,"y":54,"fill":"#0043bf"},{"x":61,"y":53,"fill":"#0043bf"},{"x":61,"y":52,"fill":"#ffffff"},{"x":61,"y":51,"fill":"#ffffff"},{"x":61,"y":50,"fill":"#0043bf"},{"x":61,"y":49,"fill":"#0043bf"},{"x":61,"y":48,"fill":"#0043bf"},{"x":61,"y":47,"fill":"#0043bf"},{"x":61,"y":46,"fill":"#0043bf"},{"x":61,"y":45,"fill":"#0043bf"},{"x":61,"y":44,"fill":"#0043bf"},{"x":61,"y":43,"fill":"#0043bf"},{"x":61,"y":42,"fill":"#0043bf"},{"x":61,"y":41,"fill":"#0043bf"},{"x":61,"y":40,"fill":"#0043bf"},{"x":61,"y":39,"fill":"#0043bf"},{"x":61,"y":38,"fill":"#0043bf"},{"x":61,"y":37,"fill":"#0043bf"},{"x":61,"y":36,"fill":"#0043bf"},{"x":61,"y":35,"fill":"#0043bf"},{"x":61,"y":34,"fill":"#0043bf"},{"x":61,"y":33,"fill":"#0043bf"},{"x":61,"y":32,"fill":"#0043bf"},{"x":61,"y":31,"fill":"#0043bf"},{"x":61,"y":30,"fill":"#0043bf"},{"x":61,"y":29,"fill":"#0043bf"},{"x":61,"y":28,"fill":"#0043bf"},{"x":61,"y":27,"fill":"#0043bf"},{"x":61,"y":26,"fill":"#0043bf"},{"x":61,"y":25,"fill":"#0043bf"},{"x":61,"y":24,"fill":"#0043bf"},{"x":61,"y":23,"fill":"#0043bf"},{"x":61,"y":22,"fill":"#0043bf"},{"x":61,"y":21,"fill":"#0043bf"},{"x":61,"y":20,"fill":"#b3c4d3"},{"x":61,"y":19,"fill":"#b3c4d3"},{"x":61,"y":18,"fill":"#e0e1d4"},{"x":61,"y":17,"fill":"#e0e1d4"},{"x":61,"y":16,"fill":"#e0e1d4"},{"x":61,"y":15,"fill":"#e0e1d4"},{"x":61,"y":14,"fill":"#e0e1d4"},{"x":61,"y":13,"fill":"#e0e1d4"},{"x":61,"y":12,"fill":"#e0e1d4"},{"x":61,"y":11,"fill":"#e0e1d4"},{"x":61,"y":10,"fill":"#e0e1d4"},{"x":61,"y":9,"fill":"#e0e1d4"},{"x":61,"y":8,"fill":"#e0e1d4"},{"x":61,"y":7,"fill":"#e0e1d4"},{"x":61,"y":6,"fill":"#e0e1d4"},{"x":61,"y":5,"fill":"#e0e1d4"},{"x":61,"y":4,"fill":"#e0e1d4"},{"x":61,"y":3,"fill":"#e0e1d4"},{"x":61,"y":2,"fill":"#e0e1d4"},{"x":61,"y":1,"fill":"#e0e1d4"},{"x":62,"y":64,"fill":"#0043bf"},{"x":62,"y":63,"fill":"#0043bf"},{"x":62,"y":62,"fill":"#0043bf"},{"x":62,"y":61,"fill":"#0043bf"},{"x":62,"y":60,"fill":"#0043bf"},{"x":62,"y":59,"fill":"#0043bf"},{"x":62,"y":58,"fill":"#0043bf"},{"x":62,"y":57,"fill":"#0043bf"},{"x":62,"y":56,"fill":"#0043bf"},{"x":62,"y":55,"fill":"#0043bf"},{"x":62,"y":54,"fill":"#0043bf"},{"x":62,"y":53,"fill":"#0043bf"},{"x":62,"y":52,"fill":"#0043bf"},{"x":62,"y":51,"fill":"#0043bf"},{"x":62,"y":50,"fill":"#0043bf"},{"x":62,"y":49,"fill":"#0043bf"},{"x":62,"y":48,"fill":"#0043bf"},{"x":62,"y":47,"fill":"#0043bf"},{"x":62,"y":46,"fill":"#0043bf"},{"x":62,"y":45,"fill":"#0043bf"},{"x":62,"y":44,"fill":"#0043bf"},{"x":62,"y":43,"fill":"#0043bf"},{"x":62,"y":42,"fill":"#0043bf"},{"x":62,"y":41,"fill":"#0043bf"},{"x":62,"y":40,"fill":"#0043bf"},{"x":62,"y":39,"fill":"#0043bf"},{"x":62,"y":38,"fill":"#0043bf"},{"x":62,"y":37,"fill":"#0043bf"},{"x":62,"y":36,"fill":"#ffffff"},{"x":62,"y":35,"fill":"#ffffff"},{"x":62,"y":34,"fill":"#0043bf"},{"x":62,"y":33,"fill":"#0043bf"},{"x":62,"y":32,"fill":"#0043bf"},{"x":62,"y":31,"fill":"#0043bf"},{"x":62,"y":30,"fill":"#ffffff"},{"x":62,"y":29,"fill":"#ffffff"},{"x":62,"y":28,"fill":"#0043bf"},{"x":62,"y":27,"fill":"#0043bf"},{"x":62,"y":26,"fill":"#0043bf"},{"x":62,"y":25,"fill":"#0043bf"},{"x":62,"y":24,"fill":"#0043bf"},{"x":62,"y":23,"fill":"#0043bf"},{"x":62,"y":22,"fill":"#0043bf"},{"x":62,"y":21,"fill":"#0043bf"},{"x":62,"y":20,"fill":"#b1c2d1"},{"x":62,"y":19,"fill":"#adbecd"},{"x":62,"y":18,"fill":"#e0e1d4"},{"x":62,"y":17,"fill":"#e0e1d4"},{"x":62,"y":16,"fill":"#e0e1d4"},{"x":62,"y":15,"fill":"#e0e1d4"},{"x":62,"y":14,"fill":"#e0e1d4"},{"x":62,"y":13,"fill":"#e0e1d4"},{"x":62,"y":12,"fill":"#e0e1d4"},{"x":62,"y":11,"fill":"#e0e1d4"},{"x":62,"y":10,"fill":"#e0e1d4"},{"x":62,"y":9,"fill":"#e0e1d4"},{"x":62,"y":8,"fill":"#e0e1d4"},{"x":62,"y":7,"fill":"#e0e1d4"},{"x":62,"y":6,"fill":"#e0e1d4"},{"x":62,"y":5,"fill":"#e0e1d4"},{"x":62,"y":4,"fill":"#e0e1d4"},{"x":62,"y":3,"fill":"#e0e1d4"},{"x":62,"y":2,"fill":"#e0e1d4"},{"x":62,"y":1,"fill":"#e0e1d4"},{"x":63,"y":64,"fill":"#0043bf"},{"x":63,"y":63,"fill":"#0043bf"},{"x":63,"y":62,"fill":"#0043bf"},{"x":63,"y":61,"fill":"#0043bf"},{"x":63,"y":60,"fill":"#0043bf"},{"x":63,"y":59,"fill":"#0043bf"},{"x":63,"y":58,"fill":"#0043bf"},{"x":63,"y":57,"fill":"#0043bf"},{"x":63,"y":56,"fill":"#0043bf"},{"x":63,"y":55,"fill":"#0043bf"},{"x":63,"y":54,"fill":"#0043bf"},{"x":63,"y":53,"fill":"#0043bf"},{"x":63,"y":52,"fill":"#0043bf"},{"x":63,"y":51,"fill":"#0043bf"},{"x":63,"y":50,"fill":"#0043bf"},{"x":63,"y":49,"fill":"#0043bf"},{"x":63,"y":48,"fill":"#0043bf"},{"x":63,"y":47,"fill":"#0043bf"},{"x":63,"y":46,"fill":"#0043bf"},{"x":63,"y":45,"fill":"#0043bf"},{"x":63,"y":44,"fill":"#0043bf"},{"x":63,"y":43,"fill":"#0043bf"},{"x":63,"y":42,"fill":"#0043bf"},{"x":63,"y":41,"fill":"#0043bf"},{"x":63,"y":40,"fill":"#0043bf"},{"x":63,"y":39,"fill":"#0043bf"},{"x":63,"y":38,"fill":"#0043bf"},{"x":63,"y":37,"fill":"#0043bf"},{"x":63,"y":36,"fill":"#ffffff"},{"x":63,"y":35,"fill":"#ffffff"},{"x":63,"y":34,"fill":"#0043bf"},{"x":63,"y":33,"fill":"#0043bf"},{"x":63,"y":32,"fill":"#0043bf"},{"x":63,"y":31,"fill":"#0043bf"},{"x":63,"y":30,"fill":"#ffffff"},{"x":63,"y":29,"fill":"#ffffff"},{"x":63,"y":28,"fill":"#0043bf"},{"x":63,"y":27,"fill":"#0043bf"},{"x":63,"y":26,"fill":"#0043bf"},{"x":63,"y":25,"fill":"#0043bf"},{"x":63,"y":24,"fill":"#0043bf"},{"x":63,"y":23,"fill":"#0043bf"},{"x":63,"y":22,"fill":"#0043bf"},{"x":63,"y":21,"fill":"#0043bf"},{"x":63,"y":20,"fill":"#b1c2d1"},{"x":63,"y":19,"fill":"#adbecd"},{"x":63,"y":18,"fill":"#e0e1d4"},{"x":63,"y":17,"fill":"#e0e1d4"},{"x":63,"y":16,"fill":"#e0e1d4"},{"x":63,"y":15,"fill":"#e0e1d4"},{"x":63,"y":14,"fill":"#e0e1d4"},{"x":63,"y":13,"fill":"#e0e1d4"},{"x":63,"y":12,"fill":"#e0e1d4"},{"x":63,"y":11,"fill":"#e0e1d4"},{"x":63,"y":10,"fill":"#e0e1d4"},{"x":63,"y":9,"fill":"#e0e1d4"},{"x":63,"y":8,"fill":"#e0e1d4"},{"x":63,"y":7,"fill":"#e0e1d4"},{"x":63,"y":6,"fill":"#e0e1d4"},{"x":63,"y":5,"fill":"#e0e1d4"},{"x":63,"y":4,"fill":"#e0e1d4"},{"x":63,"y":3,"fill":"#e0e1d4"},{"x":63,"y":2,"fill":"#e0e1d4"},{"x":63,"y":1,"fill":"#e0e1d4"}]; function Santa() { const santa = santaData; return ( ); } const snowColor = "#E0E1D4"; function getSnowFlake(x, y) { return [ { x, y, fill: snowColor }, { x: x + 1, y, fill: snowColor }, { x, y: y - 1, fill: snowColor }, { x: x + 1, y: y - 1, fill: snowColor }, ]; } const snow = [ ...getSnowFlake(20, frameHeight), ...getSnowFlake(6, frameHeight - 2), ...getSnowFlake(50, frameHeight - 2), ...getSnowFlake(12, frameHeight - 4), ...getSnowFlake(24, frameHeight - 4), ...getSnowFlake(52, frameHeight - 4), ...getSnowFlake(58, frameHeight - 12), ...getSnowFlake(14, frameHeight - 14), ...getSnowFlake(18, frameHeight - 18), ...getSnowFlake(50, frameHeight - 22), ...getSnowFlake(0, frameHeight - 24), ...getSnowFlake(10, frameHeight - 26), ...getSnowFlake(36, frameHeight - 28), ...getSnowFlake(60, frameHeight - 28), ...getSnowFlake(50, frameHeight - 30), ]; function fall(flakes) { return flakes.map((flake) => ({ ...flake, y: flake.y - 1 < 14 ? frameHeight : flake.y - 1, })); } function useSnowflakes() { const prevTimestamp = React.useRef(0); const accum = React.useRef(0); const [snowFlakes, setSnowflakes] = React.useState(snow); function update() { setSnowflakes((snowFlakes) => [...fall(snowFlakes)]); } function tick(timestamp) { const elapsed = timestamp - prevTimestamp.current; prevTimestamp.current = timestamp; accum.current += elapsed; if (accum.current >= frameRate) { accum.current -= frameRate; update(); } window.requestAnimationFrame(tick); } React.useEffect(() => { tick(performance.now()); }, []); return { snowFlakes }; } function Snow() { const { snowFlakes } = useSnowflakes(); return ( ); } function App() { return (
); } render(); ``` ================================================ FILE: website/docs/examples/area-hover.mdx ================================================ --- title: Area - Advanced Hover --- ```jsx live noInline function CustomArea(props) { if (!props.active) { return ; } else { const { data, activeX, scale, style } = props; const index = data.findIndex(val => val._x.getTime() === activeX.getTime()); const previousPoint = index === 0 ? activeX : data[index - 1]._x; const nextPoint = index === data.length - 1 ? activeX : data[index + 1]._x; // create a copy of the x dimension scale, and set the range to [0, 100] to easily calculate a percentage for the gradient offsets const percentScale = scale.x.copy().range([0, 100]); // calculate the percentages for current, previous, and next points const currentPercent = percentScale(activeX); const previousPercent = percentScale(previousPoint); const nextPercent = percentScale(nextPoint); const minPercent = currentPercent - (currentPercent - previousPercent) / 2; const maxPercent = currentPercent + (nextPercent - currentPercent) / 2; const gradientId = Math.random(); const isBrowser = typeof window !== "undefined" && window.__STATIC_GENERATOR !== true; const loc = isBrowser ? window.location.href : ""; const newStyle = Object.assign({}, style, { fill: `url(${loc}#${gradientId})`, stroke: "none" }); return ( ); } } function App() { const [state, setState] = React.useState({ activeX: null }); function onActivated(points, props) { setState({ activeX: points[0]._x }); } return ( } theme={VictoryTheme.clean} > } /> } /> } /> ); } render(); ``` ================================================ FILE: website/docs/examples/area-stream-graph.mdx ================================================ --- title: Area - Steam Graph --- ```jsx live noInline // This custom path component is supplied to `Area` as the `pathComponent` prop function GradientPath(props) { const toGrayscale = color => { const integerColor = parseInt(color.replace("#", ""), 16); const r = (integerColor >> 16) & 255; // eslint-disable-line no-bitwise const g = (integerColor >> 8) & 255; // eslint-disable-line no-bitwise const b = integerColor & 255; // eslint-disable-line no-bitwise const gray = parseInt(0.299 * r + 0.587 * g + 0.114 * b, 10); return `rgb(${gray}, ${gray}, ${gray})`; }; const { percent, style = {}, ...rest } = props; const gradientId = `gradient-${Math.random()}`; const isBrowser = typeof window !== "undefined" && window.__STATIC_GENERATOR !== true; const loc = isBrowser ? window.location.href : ""; const areaStyle = Object.assign({}, style, { fill: `url(${loc}#${gradientId})`, stroke: "none" }); return ( ); } function App() { const [state, setState] = React.useState({ percent: 62 }); const streamData = getStreamData(); const colors = [ "#006064", "#00796B", "#8BC34A", "#DCE775", "#FFF59D", "#F4511E", "#c33409" ]; return ( {streamData.map((d, i) => ( } /> } /> ))} ); } function getStreamData() { return _.range(7).map(i => _.range(26).map(j => ({ x: j, y: (10 - i) * _.random(10 - i, 20 - 2 * i), _y0: -1 * (10 - i) * _.random(10 - i, 20 - 2 * i) })) ); } render(); ``` ================================================ FILE: website/docs/examples/axis-parallel-brush.mdx ================================================ --- title: Axis - Parallel Brush --- ```jsx live noInline const data = [ { name: "Adrien", strength: 5, intelligence: 30, speed: 500, luck: 3, }, { name: "Brice", strength: 1, intelligence: 13, speed: 550, luck: 2, }, { name: "Casey", strength: 4, intelligence: 15, speed: 80, luck: 1, }, { name: "Drew", strength: 3, intelligence: 25, speed: 600, luck: 5, }, { name: "Erin", strength: 9, intelligence: 50, speed: 350, luck: 4, }, { name: "Francis", strength: 2, intelligence: 40, speed: 200, luck: 2, }, ]; const attributes = [ "strength", "intelligence", "speed", "luck", ]; const height = 500; const width = 700; const padding = { top: 100, left: 50, right: 50, bottom: 50, }; function getMaximumValues() { // Find the maximum value for each axis. This will be used to normalize data and re-scale axis ticks return attributes.map((attribute) => { return data.reduce( (memo, datum) => { return datum[attribute] > memo ? datum[attribute] : memo; }, -Infinity, ); }); } function normalizeData(maximumValues) { // construct normalized datasets by dividing the value for each attribute by the maximum value return data?.map((datum) => ({ name: datum.name, data: attributes.map( (attribute, i) => ({ x: attribute, y: datum[attribute] / maximumValues[i], }), ), })); } function App() { const maximumValues = getMaximumValues(); const datasets = normalizeData( maximumValues, ); const [state, setState] = React.useState({ maximumValues, datasets, filters: {}, activeDatasets: [], isFiltered: false, }); function addNewFilters( domain, props, ) { const filters = state.filters || {}; const extent = domain && Math.abs(domain[1] - domain[0]); const minVal = 1 / Number.MAX_SAFE_INTEGER; filters[props.name] = extent <= minVal ? undefined : domain; return filters; } function getActiveDatasets(filters) { // Return the names from all datasets that have values within all filters const isActive = (dataset) => { return _.keys(filters).reduce( (memo, name) => { if ( !memo || !Array.isArray( filters[name], ) ) { return memo; } const point = _.find( dataset.data, (d) => d.x === name, ); return ( point && Math.max( ...filters[name], ) >= point.y && Math.min( ...filters[name], ) <= point.y ); }, true, ); }; return state.datasets .map((dataset) => { return isActive( dataset, filters, ) ? dataset.name : null; }) .filter(Boolean); } function onDomainChange( domain, props, ) { const filters = addNewFilters( domain, props, ); const isFiltered = !_.isEmpty( _.values(filters).filter(Boolean), ); const activeDatasets = isFiltered ? getActiveDatasets(filters) : state.datasets; setState((state) => ({ ...state, activeDatasets, filters, isFiltered, })); } function isActive(dataset) { // Determine whether a given dataset is active return !state.isFiltered ? true : _.includes( state.activeDatasets, dataset.name, ); } function getAxisOffset(index) { const step = (width - padding.left - padding.right) / (attributes.length - 1); return step * index + padding.left; } return ( } /> {state.datasets.map((dataset) => ( } style={{ data: { opacity: isActive(dataset) ? 1 : 0.2, }, }} /> ))} {attributes.map( (attribute, index) => ( } offsetX={getAxisOffset( index, )} style={{ tickLabels: { fontSize: 15, padding: 15, pointerEvents: "none", }, }} tickValues={[ 0.2, 0.4, 0.6, 0.8, 1, ]} tickFormat={(tick) => Math.round( tick * state.maximumValues[ index ], ) } /> ), )} ); } render(); ``` ================================================ FILE: website/docs/examples/bar-horizontal-stacked.mdx ================================================ --- title: Bar - Horizontal Stacked description: Horizontal Stacked Bars with Custom Tooltips --- ```jsx live noInline const axisTickValues = [30, 100]; const data = [[{ x: 0, y: 29 }], [{ x: 0, y: 70 }], [{ x: 0, y: 30 }]]; const styles = [ { data: { fill: "#f3d437", stroke: "#d1b322", strokeWidth: 1 } }, { data: { fill: "#0ca340", stroke: "#0ca340", strokeWidth: 1 } }, { data: { fill: "#f3d437", stroke: "#d1b322", strokeWidth: 1 } }, ]; const labelFn = ({ datum }) => datum.y < axisTickValues[0] ? `${datum.y} Low` : `${datum.y} Normal`; function App() { return (
{data.map((d, i) => ( } /> ))}
); } render(); ``` ================================================ FILE: website/docs/examples/bar-linked-brushing.mdx ================================================ --- title: Bar - Linked Brushing --- ```jsx live noInline const barData = [ { name: "SEA", range: [ new Date(2013, 1, 1), new Date(2019, 1, 1), ], }, { name: "HKG", range: [ new Date(2015, 1, 1), new Date(2015, 5, 1), ], }, { name: "LHR", range: [ new Date(2016, 5, 1), new Date(2019, 1, 1), ], }, { name: "DEN", range: [ new Date(2018, 8, 1), new Date(2019, 1, 1), ], }, ]; const pointData = [ { name: "SEA", date: new Date(2012, 9, 1), }, { name: "HKG", date: new Date(2014, 3, 1), }, { name: "LHR", date: new Date(2015, 6, 1), }, { name: "DEN", date: new Date(2018, 3, 1), }, ]; const containerStyle = { display: "flex", flexDirection: "row", flexWrap: "wrap", alignItems: "center", justifyContent: "center", paddingBottom: 50 }; const domain = { y: [ new Date(2012, 1, 1), new Date(2019, 1, 1), ], x: [0.5, 4.5], }; const sharedProps = { width: 800, domain, }; class DraggablePoint extends React.Component { static defaultEvents = [ { target: "data", eventHandlers: { onMouseOver: ( evt, targetProps, ) => { return [ { mutation: () => Object.assign( targetProps, { active: true }, ), }, ]; }, onMouseDown: ( evt, targetProps, ) => { return [ { mutation: () => Object.assign( targetProps, { dragging: true }, ), }, ]; }, onMouseMove: ( evt, targetProps, ) => { const { onPointChange, datum, scale, } = targetProps; if (targetProps.dragging) { const { x } = Selection.getSVGEventCoordinates( evt, ); const point = scale.y.invert(x); const name = datum.name; onPointChange({ name, date: point, }); return [ { mutation: () => Object.assign( targetProps, { x }, ), }, ]; } return null; }, onMouseUp: ( evt, targetProps, ) => { return [ { mutation: () => Object.assign( targetProps, { dragging: false, active: false, }, ), }, ]; }, onMouseLeave: ( evt, targetProps, ) => { return [ { mutation: () => Object.assign( targetProps, { dragging: false, active: false, }, ), }, ]; }, }, }, ]; render() { return ; } } function App() { const [zoomDomain, setZoomDomain] = React.useState({}); const [bars, setBars] = React.useState(barData); const [points, setPoints] = React.useState(pointData); function handleZoom(domain) { setZoomDomain(domain); } function onDomainChange( domain, props, ) { const { name } = props; const newBars = bars.map((bar) => bar.name === name ? { name, range: domain } : bar, ); setBars(newBars); } function onPointChange(point) { const newPoints = points.map((p) => p.name === point.name ? point : p, ); setPoints(newPoints); } return (
} /> } > {bars.map((bar, index) => ( active ? 1 : 0.5, }} /> } style={{ axis: { stroke: "none" }, }} axisValue={bar.name} tickFormat={() => ""} /> ))} } style={{ data: { fill: VictoryTheme.clean .palette?.colors?.cyan, opacity: ({ active }) => active ? 1 : 0.5, cursor: "move", }, }} x="name" y="date" size={10} /> } > t.getFullYear() } /> d.range[0]} y0={(d) => d.range[1]} />
); } render(); ``` ================================================ FILE: website/docs/examples/custom-charts.mdx ================================================ --- --- # Custom Charts Victory lets you create fully custom charts that integrate seamlessly with the look and feel of your project. The following guide demonstrates how custom styles and modular chart components are used to create a cohesive chart with distinctive branding. The following example shows how to create a chart with multiple independent axes without using the `VictoryChart` wrapper. This example also includes functional styles and axis customization. ```jsx live noInline function CustomTheme() { const styles = getStyles(); const dataSetOne = getDataSetOne(); const dataSetTwo = getDataSetTwo(); const tickValues = getTickValues(); return ( {/* Create stylistic elements */} {/* Define labels */} {/* Add shared independent axis */} { if (x.getFullYear() === 2000) { return x.getFullYear(); } if (x.getFullYear() % 5 === 0) { return x.getFullYear().toString().slice(2); } } } /> {/* Add the dependent axis for the first data set. Note that all components plotted against this axis will have the same y domain */} {/* Red annotation line */} {/* dataset one */} {/* Add the dependent axis for the second data set. Note that all components plotted against this axis will have the same y domain */} {/* dataset two */} ); } function getDataSetOne() { return [ {x: new Date(2000, 1, 1), y: 12}, {x: new Date(2000, 6, 1), y: 10}, {x: new Date(2000, 12, 1), y: 11}, {x: new Date(2001, 1, 1), y: 5}, {x: new Date(2002, 1, 1), y: 4}, {x: new Date(2003, 1, 1), y: 6}, {x: new Date(2004, 1, 1), y: 5}, {x: new Date(2005, 1, 1), y: 7}, {x: new Date(2006, 1, 1), y: 8}, {x: new Date(2007, 1, 1), y: 9}, {x: new Date(2008, 1, 1), y: -8.5}, {x: new Date(2009, 1, 1), y: -9}, {x: new Date(2010, 1, 1), y: 5}, {x: new Date(2013, 1, 1), y: 1}, {x: new Date(2014, 1, 1), y: 2}, {x: new Date(2015, 1, 1), y: -5} ]; } function getDataSetTwo() { return [ {x: new Date(2000, 1, 1), y: 5}, {x: new Date(2003, 1, 1), y: 6}, {x: new Date(2004, 1, 1), y: 4}, {x: new Date(2005, 1, 1), y: 10}, {x: new Date(2006, 1, 1), y: 12}, {x: new Date(2007, 2, 1), y: 48}, {x: new Date(2008, 1, 1), y: 19}, {x: new Date(2009, 1, 1), y: 31}, {x: new Date(2011, 1, 1), y: 49}, {x: new Date(2014, 1, 1), y: 40}, {x: new Date(2015, 1, 1), y: 21} ]; } function getTickValues() { return [ new Date(1999, 1, 1), new Date(2000, 1, 1), new Date(2001, 1, 1), new Date(2002, 1, 1), new Date(2003, 1, 1), new Date(2004, 1, 1), new Date(2005, 1, 1), new Date(2006, 1, 1), new Date(2007, 1, 1), new Date(2008, 1, 1), new Date(2009, 1, 1), new Date(2010, 1, 1), new Date(2011, 1, 1), new Date(2012, 1, 1), new Date(2013, 1, 1), new Date(2014, 1, 1), new Date(2015, 1, 1), new Date(2016, 1, 1) ]; } function getStyles() { const BLUE_COLOR = "#00a3de"; const RED_COLOR = "#7c270b"; return { parent: { background: "#ccdee8", boxSizing: "border-box", display: "inline", padding: 0, fontFamily: "'Fira Sans', sans-serif" }, title: { textAnchor: "start", verticalAnchor: "end", fill: "#000000", fontFamily: "inherit", fontSize: "18px", fontWeight: "bold" }, labelNumber: { textAnchor: "middle", fill: "#ffffff", fontFamily: "inherit", fontSize: "14px" }, // INDEPENDENT AXIS axisYears: { axis: { stroke: "black", strokeWidth: 1}, ticks: { size: ({ tick }) => { const tickSize = tick.getFullYear() % 5 === 0 ? 10 : 5; return tickSize; }, stroke: "black", strokeWidth: 1 }, tickLabels: { fill: "black", fontFamily: "inherit", fontSize: 16 } }, // DATA SET ONE axisOne: { grid: { stroke: ({ tick }) => tick === -10 ? "transparent" : "#ffffff", strokeWidth: 2 }, axis: { stroke: BLUE_COLOR, strokeWidth: 0 }, ticks: { strokeWidth: 0 }, tickLabels: { fill: BLUE_COLOR, fontFamily: "inherit", fontSize: 16 } }, labelOne: { fill: BLUE_COLOR, fontFamily: "inherit", fontSize: 12, fontStyle: "italic" }, lineOne: { data: { stroke: BLUE_COLOR, strokeWidth: 4.5 } }, axisOneCustomLabel: { fill: BLUE_COLOR, fontFamily: "inherit", fontWeight: 300, fontSize: 21 }, // DATA SET TWO axisTwo: { axis: { stroke: RED_COLOR, strokeWidth: 0 }, tickLabels: { fill: RED_COLOR, fontFamily: "inherit", fontSize: 16 } }, labelTwo: { textAnchor: "end", fill: RED_COLOR, fontFamily: "inherit", fontSize: 12, fontStyle: "italic" }, lineTwo: { data: { stroke: RED_COLOR, strokeWidth: 4.5 } }, // HORIZONTAL LINE lineThree: { data: { stroke: "#e95f46", strokeWidth: 2 } } }; } render(); ``` ================================================ FILE: website/docs/examples/histogram-with-slider.mdx ================================================ --- title: Histogram - Slider --- ```jsx live noInline const LIGHT_GREY = "hsl(355, 20%, 90%)"; const PRIMARY_COLOR = "hsl(355, 92%, 67%)"; const yearToSeason = (year) => `${year}-${(year + 1 + "").slice( 2, 4, )}`; const basketballData = _.range(2000, 2020).reduce( (accum, year) => { accum[year] = _.range(1, 200).map( (idx) => ({ player: `player ${idx}`, "3pa": _.random(0, 100) / 12, }), ); return accum; }, {}, ); const YEARS = Object.keys( basketballData, ).map((year) => parseInt(year, 10)); const FIRST_YEAR = YEARS[0]; const LAST_YEAR = YEARS[YEARS.length - 1]; const TOTAL_YEARS = LAST_YEAR - FIRST_YEAR; const getTooltipText = ({ datum }) => { const { binnedData, x0, x1 } = datum; const playerCount = binnedData.length; if (!playerCount) { return null; } const playerNames = binnedData .slice(0, 2) .map(({ player }) => { const [firstName, lastName] = player.split(" "); return lastName ? `${firstName.slice( 0, 1, )}. ${lastName}` : firstName; }) .join(", "); const playerNamesList = `\n (${playerNames}${ playerCount > 2 ? `, and ${ playerCount - 2 } more players` : "" })`; return `${playerCount} player${ playerCount === 1 ? "" : "s" } averaged between ${x0}-${x1} 3PT attempts ${playerNamesList}`; }; const sharedAxisStyles = { axis: { stroke: "transparent", }, tickLabels: { fill: LIGHT_GREY, fontSize: 14, }, axisLabel: { fill: LIGHT_GREY, padding: 36, fontSize: 15, fontStyle: "italic", }, }; const App = () => { const [year, setYear] = React.useState(FIRST_YEAR); return (
} /> } height={280} >
); }; const getYear = (percent) => Math.round( FIRST_YEAR + TOTAL_YEARS * (percent / 100), ); const SEASONS = YEARS.map((year) => yearToSeason(year), ); const YearSlider = ({ year, setYear, }) => { const [value, setValue] = React.useState(0); return (
{ setValue(newValue); const calculatedYear = getYear(newValue); if (year !== calculatedYear) { setYear(calculatedYear); } }} color={PRIMARY_COLOR} value={value} maxValue={100} tooltipValues={SEASONS} />
); }; render(); ``` ================================================ FILE: website/docs/examples/polar-progress-bar.mdx ================================================ --- title: Polar - Animated Progress Bar --- ```jsx live noInline function App() { const [state, setState] = React.useState({ percent: 25, data: getData(0) }); React.useState(() => { const setStateInterval = window.setInterval(() => { let percent = 25; percent += (Math.random() * 25); percent = (percent > 100) ? 0 : percent; setState({ percent, data: getData(percent) }); }, 2000); return () => { window.clearInterval(setStateInterval); } }, []); return (
null} style={{ data: { fill: ({ datum }) => { const color = datum.y > 30 ? "green" : "red"; return datum.x === 1 ? color : "transparent"; } } }} /> {(newProps) => { return ( ); }}
); } function getData(percent) { return [{ x: 1, y: percent }, { x: 2, y: 100 - percent }]; } render(); ``` ================================================ FILE: website/docs/examples/voronoi-tooltips-grouped.mdx ================================================ --- title: Voronoi - Grouped Tooltips --- ```jsx live } theme={VictoryTheme.clean} > `y: ${datum.y}`} labelComponent={ } data={[ { x: 1, y: -3 }, { x: 2, y: 5 }, { x: 3, y: 3 }, { x: 4, y: 0 }, { x: 5, y: -2 }, { x: 6, y: -2 }, { x: 7, y: 5 } ]} > active ? 8 : 3} /> `y: ${datum.y}`} labelComponent={ } data={[ { x: 1, y: 3 }, { x: 2, y: 1 }, { x: 3, y: 2 }, { x: 4, y: -2 }, { x: 5, y: -1 }, { x: 6, y: 2 }, { x: 7, y: 3 } ]} > active ? 8 : 3} /> ``` ================================================ FILE: website/docs/guides/_category_.json ================================================ { "label": "Guides", "position": 2, "link": null } ================================================ FILE: website/docs/guides/accessibility.mdx ================================================ --- title: Accessibility --- Victory provides a number of features to make your charts more accessible. This guide will walk you through some of the most important features. ## Basic Containers like `VictoryChart` set the `role` attribute to `img` and expose the `desc` prop to provide a description of the chart for screen readers. Chart types like `VictoryLine` and `VictoryBar` expose aria props like `aria-label` to provide additional context for screen readers. Adding a `tabIndex` attribute will make the data components focusable. (click on chart and press TAB key to focus) ```jsx live `x: ${datum.x}` } tabIndex={0} /> } /> ``` ## Groups Use `VictoryAccessibleGroup` to wrap a group of chart components. This will add a `role="group"` attribute to the SVG element, which will make the chart more accessible to screen readers. Adding a `tabIndex` attribute will make the group focusable. (click on chart and press TAB key to focus) ```jsx live } > `x: ${datum.x}` } tabIndex={0} /> } /> `x: ${datum.x}` } tabIndex={0} /> } /> ``` ================================================ FILE: website/docs/guides/animations.mdx ================================================ --- title: Animations --- Victory is able to animate changes in props using [d3-interpolate][]. Victory components define their animations via the `animate` prop. `duration`, `delay`, `easing` and `onEnd` functions may all be specified via the `animate` prop. An `animationWhitelist` may also be specified on the `animate` prop. When given, only props specified in the whitelist will animate. ```jsx live noInline function App() { const [state, setState] = React.useState({ scatterData: getScatterData() }); React.useState(() => { const setStateInterval = window.setInterval(() => { setState({ scatterData: getScatterData() }); }, 3000); return () => { window.clearInterval(setStateInterval); } }, []); return ( datum.fill, opacity: ({ datum }) => datum.opacity } }} /> ); } function getScatterData() { const colors =[ "violet", "cornflowerblue", "gold", "orange", "turquoise", "tomato", "greenyellow" ]; const symbols = [ "circle", "star", "square", "triangleUp", "triangleDown", "diamond", "plus" ]; return _.range(25).map((index) => { const scaledIndex = Math.floor(index % 7); return { x: _.random(10, 50), y: _.random(2, 100), size: _.random(8) + 3, symbol: symbols[scaledIndex], fill: colors[_.random(0, 6)], opacity: 0.6 }; }); } render(); ``` ## Transitions Victory components define default transitions for entering and exiting nodes, but these may be overridden with the `onEnter` and `onExit` properties of the `animate` object. The `before` and `after` properties take functions whose return values alter the datum of the transitioning node before or after the transition. These functions are called with the original datum of the transitioning node, the index of that datum, and the entire data array. *note:* Use private variables `_x`, `_y`, `_y0` and `_y1` when altering position data during transitions. ```jsx live noInline function App() { const [state, setState] = React.useState({ data: getData() }); React.useState(() => { const setStateInterval = window.setInterval(() => { setState({ data: getData() }); }, 3000); return () => { window.clearInterval(setStateInterval); } }, []); return ( ({ _y: 0, fill: "orange", label: "BYE" }) } }} /> ); } function getData() { const bars = _.random(6, 10); return _.range(bars).map((bar) => { return {x: bar + 1, y: _.random(2, 10)}; }); } render(); ``` [d3-interpolate]: https://github.com/d3/d3-interpolate ================================================ FILE: website/docs/guides/annotations.mdx ================================================ --- title: Annotations --- When composing charts with `VictoryChart`, annotations can be added to your chart using `VictoryAnnotation`. This guide will cover the basic usage of `VictoryAnnotation` and its props. ## Labels Use `VictoryLabel` as a child of `VictoryChart` to add arbitrary labels. Labels can be positioned with the `x` and `y` props, or with `datum` when used within `VictoryChart` or `VictoryGroup`. ```jsx live d.x} /> ``` ## Lines & Markers Victory doesn't have specific components for annotations. Instead, use standard components such as `VictoryLine` and `VictoryScatter` to add lines and markers to your chart. ```jsx live d.x} /> } x={() => 5} /> ``` ================================================ FILE: website/docs/guides/axis.mdx ================================================ --- title: Axis --- When composing charts with `VictoryChart`, axes will be automatically added to your chart. Optionally, you also can directly configure the axes using the [`VictoryAxis`](/docs/api/victory-axis) and [`VictoryPolarAxis`](/docs/api/victory-polar-axis) components by following this guide. ## VictoryAxis Creates linear independent and dependent axes. :::info See the full API for [`VictoryAxis`](/docs/api/victory-axis) for more details. ::: --- ### Basic The `VictoryAxis` component can be used to render a basic axis. ```jsx live ``` --- ### Axis - Single The `crossAxis` prop can be used to render a horizontal axis, and the `dependentAxis` prop can be used to render a vertical axis. ```jsx live ``` ### Axis - Gridlines Gridlines can be shown by styling the axis component. ```jsx live tick === 5 ? "#2d7ff9" : "#CFD8DC", strokeDasharray: "10, 5", }, }} /> ``` --- ### Axis - Tick Values You can specify the specific tick values you would like to display on the axis using the `tickValues` prop. ```jsx live ``` --- ### Axis - Tick Label Format Use the `tickFormat` prop to customize axis labels. This prop can be given as an array of strings, or as a function that returns a string. :::caution `VictoryChart` automatically applies "smart" formatting to an axis for dates. When using a custom `VictoryAxis` or `VictoryPolarAxis`, you will need to format the tick values and labels manually as shown below. ::: ```jsx live `$${Math.round(tick)}M` } /> ``` #### Multiline Label Support You can also return an array of strings to create multiline labels. ```jsx live [ `$${Math.round(tick)}`, "Million", ]} /> ``` #### Time formats using `d3-time` To replicate the behaviour of automatically formatting times in `VictoryChart`, you can use `d3-scale` to format the tick values and labels. ```jsx live noInline const data = [ { x: new Date(2021, 5, 1), y: 8 }, { x: new Date(2021, 5, 2), y: 10 }, { x: new Date(2021, 5, 3), y: 7 }, { x: new Date(2021, 5, 4), y: 4 }, { x: new Date(2021, 5, 7), y: 6 }, { x: new Date(2021, 5, 8), y: 3 }, { x: new Date(2021, 5, 9), y: 7 }, { x: new Date(2021, 5, 10), y: 9 }, { x: new Date(2021, 5, 11), y: 6 }, ]; const domain = { x: [ Math.min(...data.map((d) => d.x)), Math.max(...data.map((d) => d.x)), ], }; // ref: https://d3js.org/d3-scale/time const timeScaledomain = d3Scale .scaleTime() .domain(domain.x); // ref: https://d3js.org/d3-scale/time#time_ticks const ticks = timeScaledomain.ticks(6); // ref: https://d3js.org/d3-scale/time#time_tickFormat const formatter = timeScaledomain.tickFormat(); function App() { return ( ); } render(); ``` ### Axis - Offset Position You can offset the position of the axis using the `offsetX` and `offsetY` props. ```jsx live ``` --- ### Axis - Orientation The axis orientation can be set to `top`, `bottom`, `left`, or `right` using the `orientation` prop. ```jsx live ``` --- ### Axis - Labels The axis supports labels using the `label` prop. ```jsx live ``` --- ### Axis - Multiple Multiple axes can be added to a chart by nesting `VictoryAxis` components within `VictoryChart`. :::info The domain is shared between all dependent axes, so you need to normalize the data to fit the domain of each axis. ::: ```jsx live noInline const data = [ { x: 1, amps: 4, temp: 44 }, { x: 2, amps: 6, temp: 51 }, { x: 3, amps: 11, temp: 65 }, { x: 4, amps: 12, temp: 71 }, { x: 5, amps: 10, temp: 71 }, { x: 6, amps: 13, temp: 71 }, { x: 7, amps: 11, temp: 71 }, ]; const ampRange = [0, 20]; const ampAxisColor = VictoryTheme.clean.palette.blue[3]; const tempRange = [0, 100]; const tempAxisColor = VictoryTheme.clean.palette.red[3]; const ticks = 10; const tickValues = _.range(ticks + 1); const domain = { y: [0, ticks] }; const tickFormat = (range) => (t) => (t * (range[1] - range[0])) / ticks; const normalize = (range, props) => (datum) => datum[props] / ((range[1] - range[0]) / ticks); function App() { return ( ); } render(); ``` --- ### Axis - Dependent Dependent axes can be aligned to their corresponding data points by setting the `axisValue` prop. ```jsx live {[ "cat", "dog", "bird", "dog", "frog", "fish", ].map((d, i) => { return ( ); })} ``` --- ### Axis - Small Values When a dataset only has a single value, or when all values on an axis have the same value, the single-point domain for that axis will be converted to a two-point domain. Victory does this by offsetting the domain value by a very small number. To solve this, you will need to manually set sensible defaults on the domain of your chart. ```jsx live ``` --- ### Axis - Common Label Problems Long axis labels can be problematic. There are several ways to address the issue. The best solution will depend on the specific requirements of your project. The following examples demonstrate: :::info Using `padding` properties can help to adjust the position of the axis labels. ::: ```jsx live ``` :::info Splitting the labels onto multiple lines can help to make the labels more readable. ::: ```jsx live ``` :::info Using angled labels can help to make the labels more readable. ::: ```jsx live ``` :::info Fixing axis label and tick label overlap using the style prop. ::: ```jsx live ``` --- ### Axis - Brush Lines Brush lines can be added to the axis using the `VictoryBrushLine` component for selecting a range of the domain. :::info See the full API for [`VictoryBrushLine`](/docs/api/victory-brush-line) for more details. ::: ```jsx live } /> ``` --- ## VictoryPolarAxis Creates a circular axis for a chart. :::info See the full API for [`VictoryPolarAxis`](/docs/api/victory-polar-axis) for more details. ::: --- ### Basic The `VictoryPolarAxis` component can be used to render a basic axis for polar charts. ```jsx live ``` --- ### Axis - Angle The dependent axis can be rendered at different angles. ```jsx live ``` --- ### Axis - Labels The label placement can be adjusted by using the [`labelPlacement`](/docs/api/victory-polar-axis#labelplacement) prop. ```jsx live ``` --- ### Axis - Half Circle The polar axis can also be rendered in a confined set of angles. When `VictoryPolarAxis` is a child of `VictoryChart`, the `startAngle` and `endAngle` props will be set by the domain data. ```jsx live
``` ================================================ FILE: website/docs/guides/containers.mdx ================================================ --- title: Containers --- ## Defaults Victory containers have default `width`, `height`, and `padding` props defined in the default [theme](/docs/guides/themes). Victory renders components into responsive `svg` containers by default using `VictoryContainer`. `VictoryContainer` is a responsive container with a `viewBox` attribute set to `viewBox={"0 0 width, height"}` and styles `width: "100%" height: "auto"` in addition to any styles provided via props. Because Victory renders responsive containers, the `width` and `height` props do not determine the width and height of the chart in number of pixels, but instead define an aspect ratio for the chart. The exact number of pixels will depend on the size of the container the chart is rendered into. ## Fixed Size Containers Responsive containers are not appropriate for every application, so Victory provides a couple of options for rendering static containers. The easiest way to render a static container rather than a responsive one is by setting the `responsive` prop to false directly on the `containerComponent` instance. ```jsx live } theme={VictoryTheme.clean} > Math.sin(2 * Math.PI * data.x) } /> ``` ## Render Order Victory renders svg elements, so there is no concept of z-index. Instead the render order of components determines which elements will appear above others. Changing the order of rendered components can significantly alter the appearance of a chart. Compare the following charts. :::note The difference is the order of the children in `VictoryChart`. ::: ```jsx live noInline const sampleFn = (data) => Math.sin(2 * Math.PI * data.x); const scatterStyle = { data: { fill: "red" }, }; function App() { return ( <> ); } render(); ``` ## Render on Top Some components should _always_ render above others. Use `VictoryPortal` to render components in a top level container so that they appear above all other elements. `VictoryTooltip` uses `VictoryPortal`, by default, but any component may be wrapped in `VictoryPortal` to alter its rendering. :::warning `VictoryPortal` only works with components that are rendered within a [Victory Container](/docs/guides/containers) component. ::: ```jsx live } > ``` ## Basic Container Types Victory renders charts into top-level container components. The most commonly used container is `VictoryChart`. Containers are responsible for rendering children into a responsive svg, and providing a portal component for rendering tooltips, or any other elements that should be rendered above everything else. ### VictoryContainer `VictoryContainer` provides a top-level `` element for other Victory components to render within. Most containers extend `VictoryContainer` to add extra functionality. See the [full API here](/docs/api/victory-container). ### VictoryChart `VictoryChart` is a container that renders a set of children on a set of Cartesian or polar axes. `VictoryChart` reconciles the domain for all its children, controls the layout of the chart, and coordinates animations and shared events. If no children are provided, `VictoryChart` will render a set of empty default axes. See the [full API here](/docs/api/victory-chart). ## Advanced Container Types - [VictorySelectionContainer](#victoryselectioncontainer) - [VictoryZoomContainer](#victoryzoomcontainer) - [VictoryVoronoiContainer](#victoryvoronoicontainer) ### VictoryGroup `VictoryGroup` is a container that renders a given set of children with shared props. This is useful for creating a group of components that share styles or data, or rendering multiple charts without axes. See the [full API here](/docs/api/victory-group). ### VictoryBrushContainer `VictoryBrushContainer` adds the ability to highlight a region of a chart, and interact with highlighted regions, either by moving the region, expanding the region, or selecting a new region. See the [Brush Selection](/docs/guides/data-selection) guide. ### VictoryCursorContainer `VictoryCursorContainer` adds a cursor to a chart to inspect coordinates. The cursor can either be a 2-dimensional crosshair, or a 1-dimensional line. The cursor moves with the mouse (or on touch on mobile devices) along the visible domain of the chart. See the [Cursor Tooltips](/docs/guides/tooltips) guide. ### VictorySelectionContainer `VictorySelectionContainer` is used to enable selecting data points within a highlighted region. Clicking and dragging will select an x-y region, and add the `active` prop to any elements corresponding to data points within the region. Create a select-box control by tying the set of selected data points to other elements, such as a filtered table. See the [Data Selection](/docs/guides/data-selection) guide. ### VictoryZoomContainer `VictoryZoomContainer` provides pan and zoom behavior for any Victory component that works with an x, y axis. Zoom events are controlled by scrolling, and panning events are controlled by dragging. See the [Pan and Zoom](/docs/guides/pan-and-zoom) guide. ### VictoryVoronoiContainer `VictoryVoronoiContainer` adds the ability to associate a mouse position with the data point(s) closest to it. When this container is added to a chart, changes in mouse position will add the `active` prop to data and label components closest to the current mouse position. See the [Tooltips](/docs/guides/tooltips) guide. ### Multiple Containers Victory includes a `createContainer` helper that is used to create hybrid containers. `createContainer` can be used to create a new container with behaviors from two existing Victory containers. It allows you to effectively combine any two of the following containers: `VictoryBrushContainer`, `VictoryCursorContainer`, `VictorySelectionContainer`, `VictoryVoronoiContainer`, or `VictoryZoomContainer`. ```js const VictoryZoomVoronoiContainer = createContainer("zoom", "voronoi"); ``` #### Arguments The function takes two `behavior` arguments as strings: ```js createContainer(behaviorA, behaviorB) ``` #### Behavior Each `behavior` must be one of the following strings: `"brush"`, `"cursor"`, `"selection"`, `"voronoi"`, and `"zoom"`. The resulting container uses the events from both behaviors. For example, if both behaviors use the click event (like zoom and selection) the combined container will trigger both behaviors' events on each click. *Note*: Order of the behaviors matters in a few cases. It is recommended to use `"zoom"` before any other behaviors: for example, `createContainer("zoom", "voronoi")` instead of `createContainer("voronoi", "zoom")`. #### Example The following example creates a custom container that combines `VictoryVoronoiContainer` and `VictoryZoomContainer`. Hovering over the chart will use Voronoi to highlight data points, while scrolling and dragging will zoom and pan. ```jsx live noInline const VictoryZoomVoronoiContainer = createContainer("zoom", "voronoi"); const data = _.range(100).map((x) => ({x, y: 100 + x + _.random(10)})); const App = () => ( `${datum.x}, ${datum.y}`} /> } theme={VictoryTheme.clean} > ); render(); ``` ================================================ FILE: website/docs/guides/custom-components.mdx ================================================ --- title: Custom Components --- Every element that a Victory component renders may be altered or completely replaced. Most components expose `dataComponent`, `labelComponent`, `groupComponent`, and `containerComponent` props. The primitive components that Victory components render by default are simple, stateless components with a consistent set of props whenever possible. These [primitive components][] are exported for users to alter, wrap, extend and reference when creating custom components. ## Altering default components Victory components set props on their primitive components, but these may be overridden or augmented by setting props directly on the primitive component instances. ```jsx live } theme={VictoryTheme.clean} /> ``` ## Wrapping components Victory components may be wrapped to customize or change behavior. Wrapper components should apply any props they receive from other Victory components to the components they render. ```jsx live noInline function WrapperComponent(props) { function renderChildren() { const children = React.Children.toArray(props.children); return children.map((child) => { // children should be rendered with props from their parent Victory components assigned // Components like `VictoryChart` expect to control props like `domain` for their children // Some props should be merged rather than overridden const style = _.merge(child.props.style, props.style); return React.cloneElement(child, Object.assign({}, child.props, props, { style })); }); } return ( { renderChildren() } ); } function App() { return ( Math.sin(2 * Math.PI * d.x)} samples={15} symbol="square" size={6} style={{ data: { stroke: "lightblue", strokeWidth: 3 }}} /> ); } render(); ``` ## Creating new components Any component that renders valid svg elements (or elements wrapped in ``) may be used as a `dataComponent` or `labelComponent` in Victory components. Custom components will be provided with the same props as default components. In the following example, a custom `CatPoint` component is used in place of `Point` in `VictoryScatter`. ```jsx live noInline function CatPoint(props) { const {x, y, datum} = props; const cat = datum._y >= 0 ? "😻" : "😹"; return ( {cat} ); } function App() { return ( Math.sin(2 * Math.PI * d.x) } samples={25} dataComponent={} /> ); } render(); ``` More complex components may be supplied as direct children of `VictoryChart`. These components will have access to shared chart props such as `scale`. In the example below, the custom `Polygon` components draws a polygon based on a collection of points. The scale provided by `VictoryChart` is used to correctly position the points within the chart. ```jsx live noInline const SAMPLE_DATA = [ {x: 2, y: 1}, {x: 3, y: 5}, {x: 6, y: 3} ]; function getPoints(data, scale) { return data.reduce((pointStr, {x, y}) => `${pointStr} ${scale.x(x)},${scale.y(y)}` , ''); } function Polygon(props) { // data and style are explicitly supplied to the Polygon component // scale is provided by VictoryChart const { data, style, scale } = props; const points = getPoints(data, scale); return ; } function App() { return ( ); } render(); ``` Other Victory components may even be used in creating custom components, as in the example below. ```jsx live noInline function CustomPie(props) { const {datum, x, y} = props; const pieWidth = 120; return ( ); } function CustomDataComponent() { const data = [ {x: "Jan", y: 30}, {x: "Feb", y: 32}, {x: "Mar", y: 65}, {x: "Apr", y: 38}, {x: "May", y: 50}, {x: "Jun", y: 47}, {x: "Jul", y: 38}, {x: "Aug", y: 48}, {x: "Sep", y: 80}, {x: "Oct", y: 73}, {x: "Nov", y: 76}, {x: "Dec", y: 100} ]; const pieData = data.map((datum) => { datum.pie = [ {x: "Lions", y: Math.round(Math.random() * 10)}, {x: "Tigers", y: Math.round(Math.random() * 10)}, {x: "Bears", y: Math.round(Math.random() * 10)} ]; return datum; }); return ( } /> ); } render(); ``` Since any custom SVG element can be used as a Victory component, any styling system can be used to style custom components, including styled components, CSS modules, or inline styles. Here's an example using SVG + styled components. ```jsx live noInline const colors = ["#A8E6CE", "#DCEDC2", "#FFD3B5", "#FFAAA6", "#FF8C94"]; const ScatterPoint = ({ x, y, datum, min, max }) => { const i = React.useMemo(() => { return Math.floor(((datum.y - min) / (max - min)) * (colors.length - 1)); }, [datum, min, max]); return ; }; const App = () => { const data = [ { x: "Jan", y: 43 }, { x: "Feb", y: 44 }, { x: "Mar", y: 47 }, { x: "Apr", y: 51 }, { x: "May", y: 57 }, { x: "Jun", y: 62 }, { x: "Jul", y: 67 }, { x: "Aug", y: 68 }, { x: "Sep", y: 63 }, { x: "Oct", y: 54 }, { x: "Nov", y: 47 }, { x: "Dec", y: 42 } ]; const temperatures = data.map(({ y }) => y); const min = Math.min(...temperatures); const max = Math.max(...temperatures); return ( } /> ); } render(); ``` [primitive components]: /docs/api/victory-primitives ================================================ FILE: website/docs/guides/data-accessors.mdx ================================================ --- title: Data Accessors --- Most Victory components expect data in the form of an array of data objects with values specified for `x` and `y`. Victory components expose data accessor props that may be used when data is not readily available in this format. Data accessor props may be used to specify how a data prop should be used, process a elements in a data array, or to plot math functions even when no data prop is given. ## Specifying x and y data Some Victory components like `VictoryCandlestick` and `VictoryErrorBar` have unusual accessor props that match their expected data formats, but most Victory components expose standard `x` and `y` data accessor props. These props may be used to specify which properties or elements of the data array should be plotted on the x and y axes. When given as strings, these accessors will specify which properties of a data object to plot. The following example will plot employees on the x axis and salaries on the y axis: ```jsx ``` If data is given as an array of arrays, data accessors may be given as integers to specify the index of the nested array that should be plotted. ```jsx ``` Data accessors may also be given as path strings or arrays to specify deeply nested data. ```jsx ``` ## Processing data Data accessor props may be given as functions and used to process data, as in the following example. ```jsx live (d.actual / d.expected) * 100 } /> ``` ## Sorting data Sorting can be applied to the final data via the sortKey prop. This prop corresponds to the lodash [sortBy][] function. This prop can be provided as a string, function, or array of either. ```jsx live ({ t }))} sortKey="t" theme={VictoryTheme.clean} x={({ t }) => Math.sin(3 * t + 2 * Math.PI) } y={({ t }) => Math.sin(2 * t)} /> ``` ## Plotting functions If data is not given, data accessor props may be used to plot math functions. In this scenarios, initial data will be generated based on the domain and number of samples. Alter the `samples` and `domain` props to change how functions are plotted. ```jsx live Math.sin(2 * Math.PI * data.x) } /> Math.cos(2 * Math.PI * data.x) } /> ``` [sortby]: https://lodash.com/docs/4.17.4#sortBy ================================================ FILE: website/docs/guides/data-selection.mdx ================================================ --- title: Data Selection --- Victory allows multiple ways to select data points on a chart. `VictorySelectionContainer` is a container component that allows users to select data points within a region of a chart. Use `VictoryBrushContainer` to identify the domain of a selected region. :::info `VictorySelectionContainer` is similar to `VictoryBrushContainer`. `VictoryBrushContainer` may be used to identify the domain of a selected region, whereas `VictorySelectionContainer` may be used to identify a list of data points within a selected region. `VictoryBrushContainer` will also create persistent highlighted regions, whereas regions created by `VictorySelectionContainer` disappear after `onMouseUp` events. ::: ## VictorySelectionContainer Use `VictorySelectionContainer` to add data selection behavior to any Victory components that work with an x-y coordinate system. See the [full API here](/docs/api/victory-selection-container). ### Basic `VictorySelectionContainer` may be used with any Victory component that works with an x-y coordinate system, and should be added as the `containerComponent` of the top-level component. However, the component that uses it must be standalone (`standalone={true}`), which is the default for all top-level Victory components. ```jsx live } theme={VictoryTheme.clean} > active ? "tomato" : "gray", }, }} /> ``` ### Active Points The `VictorySelectionContainer` will automatically set an `active` prop on the data points that are within the selected region. This prop can be used to style the selected points differently. ```jsx live } > active ? VictoryTheme.clean.palette?.colors.purple : "none", strokeWidth: 2, }, }} data={[ { x: 1, y: -5 }, { x: 2, y: 4 }, { x: 3, y: 2 }, { x: 4, y: 3 }, { x: 5, y: 1 }, { x: 6, y: -3 }, { x: 7, y: 3 }, ]} /> active ? VictoryTheme.clean.palette?.colors.purple : "none", strokeWidth: 2, }, }} data={[ { x: 1, y: -3 }, { x: 2, y: 5 }, { x: 3, y: 3 }, { x: 4, y: 0 }, { x: 5, y: -2 }, { x: 6, y: -2 }, { x: 7, y: 5 }, ]} /> active ? VictoryTheme.clean.palette?.colors.purple : "none", strokeWidth: 2, }, }} data={[ { x: 1, y: 5 }, { x: 2, y: -4 }, { x: 3, y: -2 }, { x: 4, y: -3 }, { x: 5, y: -1 }, { x: 6, y: 3 }, { x: 7, y: -3 }, ]} /> ``` ### Selection Limits The `selectionDimension` prop may be used to limit brushing behavior to a single dimension. In the example below, the `selectionDimension` prop is set to `"y"`, allowing users to select a region of the chart along the y-axis only. ```jsx live } theme={VictoryTheme.clean} > active ? "tomato" : "gray", }, }} /> ``` ### Events Use the `onSelection` prop to define a function that will be called with the selected domain when the selection area changes. ```jsx live noInline function App() { const [selection, setSelection] = React.useState({}); const handleSelection = ( datasets, ) => { const points = datasets.reduce( (memo, dataset) => memo.concat(dataset.data), [], ); setSelection({ points }); }; return ( } > {selection && ( ({ x, y }), ), )} /> )} ); } render(); ``` ## VictoryBrushContainer Use `VictoryBrushContainer` to add highlighting and selection to any Victory components that work with an x-y coordinate system. See the [full API here](/docs/api/victory-brush-container). ### Basic In the example below, the `VictoryBrushContainer` component is used to highlight a region of a line chart. The brush behavior is unconstrained by default, allowing users to click and drag to select a region of the chart. ```jsx live } style={{ data: { stroke: "lightblue" }, }} data={[ { x: 1, y: -3 }, { x: 2, y: 5 }, { x: 3, y: -3 }, { x: 4, y: 0 }, { x: 5, y: -5 }, { x: 6, y: 2 }, { x: 7, y: 0 }, ]} /> ``` ### Selection Limits The `brushDimension` prop may be used to limit brushing behavior to a single dimension. In the example below, the `brushDimension` prop is set to `"y"`, allowing users to select a region of the chart along the y-axis only and the `brushDomain` restricts the highlighted area to the specified range. ```jsx live } style={{ data: { stroke: "lightblue" }, }} data={[ { x: 1, y: -3 }, { x: 2, y: 5 }, { x: 3, y: -3 }, { x: 4, y: 0 }, { x: 5, y: -5 }, { x: 6, y: 2 }, { x: 7, y: 0 }, ]} /> ``` ### Events `VictoryBrushContainer` exposes several events you can use to access the selection range. In the example below, you can monitor your browser's console to see the events in action. ```jsx live console.log( "[onBrushCleared]", { domain, props }, ) } onBrushDomainChange={( domain, props, ) => console.log( "[onBrushDomainChange]", { domain, props }, ) } onBrushDomainChangeEnd={( domain, props, ) => console.log( "[onBrushDomainChangeEnd]", { domain, props }, ) } /> } > ``` ================================================ FILE: website/docs/guides/events.mdx ================================================ --- title: Events --- Victory uses a flexible event system that is agnostic of event type. Browser events like `onClick` are handled identically to mobile touch events like `onPressIn`. Victory's event system allows users to attach events to any rendered element, and trigger mutations on any other rendered element. This guide will demonstrate how to use Victory's event system within a single component, between several components nested within wrapper components like `VictoryChart` or `VictoryGroup`, and between several components using the `VictorySharedEvents` wrapper. This guide will also explain how to bypass Victory's event system entirely, and attach simple events directly to rendered components. ## Component Events Events within a single component like `VictoryBar` may be defined by the `events` prop of the component. The component will be responsible for storing event-driven mutations on its state object. The `events` prop should be given as an array of event objects. Each object defines an event or set of events to attach to a particular target element, or set of target elements. Target elements are specified by the `target` and `eventKey` properties. Valid `target` properties match the namespaces of the style element of any given component. For most components valid target properties are "data", "labels", and "parent". The `target` property is required. The optional `eventKey` property may be given as a value or array of values. Events are defined by the `eventHandlers` property which should be given as an object whose properties are named events such as `onClick`, and whose values are event handlers. Event handlers are called with the event, the props defining the element that triggered the event, and the event key of the element that triggered the event. Return values from event handlers are used to define mutations affecting rendered elements. Return values from event handlers should be given as an array of mutation objects. Mutation objects may have `target` and `eventKey` properties to specify an element to mutate. If these properties are not given, the mutation will effect the element that triggered the event. Mutation objects should also have a `mutation` property whose value is a function. The mutation function will be called with the event, the props defining the element that will be mutated, and the event key of the element that will be mutated. The mutation function should return an object of props to be modified, and the new values for those props. **In the example below, clicking on any of the bars will trigger a change in the text of the corresponding labels.** ```jsx live { return [ { target: "labels", mutation: (props) => { return props.text === "clicked" ? null : { text: "clicked" }; }, }, ]; }, }, }, ]} /> ``` ## Nested Events Wrapper components like `VictoryChart`, `VictoryGroup`, and `VictoryStack` may define events for their children. Component events defined by wrappers operate much the same as single component events, except that the events are defined on the parent component, and event-driven mutations are stored in the parent's state. Events on child components are specified with the `childName` property. Components that have a `name` prop specified will be referenced by name. If child components do not have a `name` specified they will be referenced by index. In the example below, clicking on either of the bottom two areas in the stack will change the color of the top area. ```jsx live { return [ { childName: "area-4", mutation: (props) => { const fill = props.style.fill; return fill === "tomato" ? null : { style: { fill: "tomato", }, }; }, }, ]; }, }, }, ]} > ``` ## Target Events Events can target specific elements within a component by using the `eventKey` property. The `eventKey` is a zero indexed integer that specifies the index of the element to target. The `eventKey` may also be given as an array of integers to target multiple elements. In the example below, clicking on the first bar will change the color of the second bar. ```jsx live { evt.stopPropagation(); return [ { mutation: (props) => { return { style: { ...props.style, fill: VictoryTheme .clean.palette ?.colors.teal, }, }; }, }, { target: "labels", eventKey: 3, mutation: (props) => { return { ...props.style, text: "now click me", }; }, }, ]; }, }, }, { target: "parent", eventHandlers: { onClick: () => { return [ { target: "data", mutation: (props) => { return { style: { ...props.style, fill: VictoryTheme .clean.palette ?.colors.red, }, }; }, }, ]; }, }, }, ]} /> ``` ## Parent Events Wrapper components like `VictoryChart`, `VictoryGroup`, and `VictoryStack` may also define events for themselves which can target child components for mutation. ```jsx live { evt.stopPropagation(); return [ { mutation: (props) => { return { style: { ...props.style, fill: VictoryTheme .clean.palette ?.colors.yellow, }, }; }, }, ]; }, }, }, { target: "parent", eventHandlers: { onClick: () => { return [ { childName: "bar", target: "labels", mutation: () => { return { text: "You Clicked Me!", }; }, }, ]; }, }, }, ]} > null} /> ``` ## Shared Events Components like `VictoryChart` use the `VictorySharedEvents` wrapper automatically, but the wrapper may also be used on its own. Nest child components within the `VictorySharedEvents` wrapper, and reference them as you would when using `VictoryChart` ```jsx live { return [ { childName: [ "pie", "bar", ], mutation: (props) => { return { style: Object.assign( {}, props.style, { fill: "lightblue", }, ), }; }, }, ]; }, onMouseOut: () => { return [ { childName: [ "pie", "bar", ], mutation: () => { return null; }, }, ]; }, }, }, ]} > } /> ``` ## External Event Mutations Occasionally is it necessary to trigger events in Victory's event system from some external element such as a button or a form field. Use the `externalEventMutation` prop to specify a set of mutations to apply to a given chart. The `externalEventMutations` should be given in the following form: ```jsx externalEventMutations: PropTypes.arrayOf( PropTypes.shape({ callback: PropTypes.func, childName: PropTypes.oneOfType([ PropTypes.string, PropTypes.array, ]), eventKey: PropTypes.oneOfType([ PropTypes.array, CustomPropTypes.allOfType([ CustomPropTypes.integer, CustomPropTypes.nonNegative, ]), PropTypes.string, ]), mutation: PropTypes.func, target: PropTypes.oneOfType([ PropTypes.string, PropTypes.array, ]), }), ); ``` The `target`, `eventKey`, and `childName` (when applicable) must always be specified. The `mutation` function will be called with the current props of the element specified by the `target`, `eventKey` and `childName` provided. The mutation function should return a mutation object for that element. The `callback` prop should be used to clear the `externalEventMutations` prop once the mutation has been applied. Clearing `externalEventMutations` is crucial for charts that animate. ```jsx live noInline function App() { const [state, setState] = React.useState({ externalMutations: undefined, }); function removeMutation() { setState({ externalMutations: undefined, }); } function clearClicks() { setState({ externalMutations: [ { childName: "Bar-1", target: ["data"], eventKey: "all", mutation: () => ({ style: undefined, }), callback: removeMutation, }, ], }); } const buttonStyle = { backgroundColor: "black", color: "white", padding: "10px", marginTop: "10px", }; return (
({ target: "data", mutation: () => ({ style: { fill: "orange", }, }), }), }, }, ]} theme={VictoryTheme.clean} > "click me!"} data={[ { x: 1, y: 2 }, { x: 2, y: 4 }, { x: 3, y: 1 }, { x: 4, y: 5 }, ]} />
); } render(); ``` _Note_ External mutations are applied to the same state object that is used to control events in Victory, so depending on the order in which they are triggered, external event mutations may override mutations caused by internal Victory events or vice versa. ## Simple Events For simple events, it may be desirable to bypass Victory's event system. To do so, specify `events` props directly on primitive components rather than using the `events` prop on Victory components. The simple `events` prop should be given as an object whose properties are event names like `onClick`, and whose values are event handlers. Events specified this way will only be called with the standard event objects. ```jsx live alert( `(${evt.clientX}, ${evt.clientY})`, ), }} /> } theme={VictoryTheme.clean} /> ``` ## Events on Custom Components For custom SVG components, Victory's event system is not the only option. It's possible to bypass it altogether in favor of using native React events on the custom SVG components. This might be preferable when lots of custom styling and interactions are needed. Notice how the `circle` component in this example is a basic SVG element with React event props rather than a Victory component. ```jsx live noInline const ScatterPoint = ({ x, y, datum, }) => { const [selected, setSelected] = React.useState(false); const [hovered, setHovered] = React.useState(false); return ( setSelected(!selected) } onMouseEnter={() => setHovered(true) } onMouseLeave={() => setHovered(false) } /> ); }; const App = () => { return ( } /> ); }; render(); ``` ================================================ FILE: website/docs/guides/legends.mdx ================================================ --- title: Legends --- Legends can be added to any chart by nesting a `VictoryLegend` component within `VictoryChart`. The legend can be styled and positioned using props. ## Basic See the [full API here](/docs/api/victory-legend). ```jsx live datum.fill, }, }} /> ``` ## Legend - Titles The legend can include multiline title by setting the `title` prop to an array. ```jsx live ``` ## Legend - Symbols Victory includes basic symbols for legend items. The `symbol` prop can be used to specify the symbol type and fill color. ```jsx live ``` ## Legend - Custom Icons Victory supports custom icons for legend items such as React components from SVG libraries like [react-icons](https://react-icons.github.io/react-icons/). ```jsx live noInline const { FaSun, FaMoon } = reactIconsFa; const CustomMoon = (props) => ( ); function App() { return ( } theme={VictoryTheme.clean} /> ); } render(); ``` ## Legend - Orientation The legend also supports vertical orientations. ```jsx live ``` ## Legend - Columns The legend can be displayed in multiple rows by setting the `itemsPerRow` prop. ```jsx live ``` ================================================ FILE: website/docs/guides/localization.mdx ================================================ --- title: Localization --- Victory provides extensive support for localization, allowing you to customize your data visualizations to match local conventions for dates, numbers, and text direction. This ensures that your charts are easily understandable and culturally appropriate for users around the world. ## Date and Number Formatting Different regions have varying conventions for displaying dates, numbers, and currencies. Victory components can be customized to respect these local preferences. The example below demonstrates French formatting conventions for dates (e.g., "1 janvier 2023") and currency values (e.g., "1 234,56 €"). ```jsx live noInline const numbersData = [ { x: new Date(2023, 0, 1), y: 1500 }, { x: new Date(2023, 2, 15), y: 2800 }, { x: new Date(2023, 5, 1), y: 2200 }, { x: new Date(2023, 8, 20), y: 3100 }, { x: new Date(2023, 11, 31), y: 4000 } ]; const dateFormatter = new Intl.DateTimeFormat("fr-FR", { year: "numeric", month: "long", day: "numeric", }); const numberFormatter = new Intl.NumberFormat("fr-FR", { style: "currency", currency: "EUR" }); function App() { return ( dateFormatter.format(t)} style={{ tickLabels: { fontSize: 10, padding: 15, angle: -45, textAnchor: "end" }, }} /> numberFormatter.format(t)} style={{ tickLabels: { fontSize: 10, padding: 10 }, }} /> ); } render(); ``` ## Text Direction (LTR/RTL) Victory supports right-to-left (RTL) layouts for languages like Arabic, Hebrew, and Persian. The following example demonstrates an Arabic sales dashboard with RTL text direction and proper Arabic number formatting. ```jsx live noInline const rtlData = [ { x: "المبيعات الشهرية", y: 120 }, { x: "إجمالي الإيرادات", y: 250 }, { x: "عدد العملاء", y: 180 }, { x: "المنتجات النشطة", y: 90 } ]; function App() { return ( `${datum.y}`} style={{ data: { fill: "#c43a31" } }} labelComponent={ } /> ); } render(); ``` ## Custom Formatting for Labels and Tooltips Labels and tooltips often require specialized formatting to match local conventions. This example demonstrates a sales data visualization with Brazilian Portuguese formatting for dates and currency values, including interactive tooltips. ```jsx live noInline const salesData = [ { x: new Date(2023, 0, 1), y: 25000 }, { x: new Date(2023, 1, 1), y: 28000 }, { x: new Date(2023, 2, 1), y: 32000 }, { x: new Date(2023, 3, 1), y: 30000 }, { x: new Date(2023, 4, 1), y: 35000 }, { x: new Date(2023, 5, 1), y: 42000 }, { x: new Date(2023, 6, 1), y: 38000 }, { x: new Date(2023, 7, 1), y: 45000 }, { x: new Date(2023, 8, 1), y: 47000 }, { x: new Date(2023, 9, 1), y: 52000 }, { x: new Date(2023, 10, 1), y: 58000 }, { x: new Date(2023, 11, 1), y: 65000 } ]; const dateFormatter = new Intl.DateTimeFormat("pt-BR", { month: "long", year: "numeric" }); const currencyFormatter = new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }); const formatTooltip = ({ datum }) => { const date = new Intl.DateTimeFormat("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric" }).format(datum.x); return ` Data: ${date} Valor: ${currencyFormatter.format(datum.y)} `; }; function App() { return ( dateFormatter.format(t)} style={{ tickLabels: { fontSize: 10, padding: 5 } }} /> currencyFormatter.format(t)} style={{ tickLabels: { fontSize: 10, padding: 5 } }} /> } /> ); } render(); ``` ================================================ FILE: website/docs/guides/pan-and-zoom.mdx ================================================ --- title: Pan and Zoom --- Use `VictoryZoomContainer` to add panning and zooming behavior to any Victory components that work with an x-y coordinate system. See the [full API here](/docs/api/victory-zoom-container). ## Basic In the example below, an initial domain is set with the `zoomDomain` prop. This prop may also be used to trigger pan and zoom behavior from other components. ```jsx live noInline function App() { return ( } > datum.y % 5 === 0 ? 1 : 0.7, fill: ({ datum }) => datum.y % 5 === 0 ? "lightblue" : "lightgreen", }, }} /> ); } function getScatterData() { return _.range(50).map((index) => { return { x: _.random(1, 50), y: _.random(10, 90), size: _.random(8) + 3, }; }); } render(); ``` ## Limits The `allowZoom` and `allowPan` props may be used to restrict zooming and panning behavior. In the example below, zooming is disabled, but panning is allowed. ```jsx live noInline function App() { return ( } > datum.y % 5 === 0 ? 1 : 0.7, fill: ({ datum }) => datum.y % 5 === 0 ? "lightblue" : "lightgreen", }, }} /> ); } function getScatterData() { return _.range(50).map((index) => { return { x: _.random(1, 50), y: _.random(10, 90), size: _.random(8) + 3, }; }); } render(); ``` ## Combined with Brushing In the next example, `VictoryZoomContainer` and `VictoryBrushContainer` are used to create a zoomable chart with a mini-map brush control. Here, the `onZoomDomainChange` prop on `VictoryZoomContainer` alters the `brushDomain` prop on `VictoryBrushContainer` tying highlighted brush region of the mini-map to the zoom level of the chart. The `onBrushDomainChange` prop on `VictoryBrushContainer` alters the `zoomDomain` prop on `VictoryZoomContainer` so that the zoomed level of the chart matches the highlighted region of the mini-map. :::info For more information on brushing, see the [Data Selection](/docs/guides/data-selection) guide. ::: ```jsx live noInline function App() { const [state, setState] = React.useState({}); function handleZoom(domain) { setState({ selectedDomain: domain, }); } function handleBrush(domain) { setState({ zoomDomain: domain }); } return (
} > } > new Date(x).getFullYear() } />
); } render(); ``` ================================================ FILE: website/docs/guides/polar-charts.mdx ================================================ --- title: Polar Charts --- Victory supports polar charts for many of its chart types. Polar charts are a type of radial chart, where the data is displayed in a circular graph. ## Bar Charts Bar charts are a common type of polar chart. In the following example, we use a `VictoryPolarAxis` to create a polar chart with a bar series. ```jsx live ""} /> ``` ## Bar Charts - Events Bar charts can be interactive with the use of events. In the following example, we use a `VictoryPolarAxis` to create a polar chart with a bar series that changes color on hover. ```jsx live } > active ? VictoryTheme.clean.palette ?.colors.blue || "blue" : VictoryTheme.clean.palette ?.colors.red || "red", }, }} labelComponent={} data={[ { x: "strength", y: 10, label: "one", }, { x: "intelligence", y: 25, label: "two", }, { x: "stealth", y: 40, label: "three", }, { x: "luck", y: 50, label: "four", }, { x: "charisma", y: 50, label: "five", }, ]} /> ``` ## Bar Charts - Stacked Bar charts can be stacked in a polar format. In the following example, we use a `VictoryPolarAxis` to create a polar chart with a stacked bar series. ```jsx live { return [ { childName: "bar-2", mutation: () => { return { style: Object.assign( {}, props.style, { fill: VictoryTheme .clean.palette ?.colors.cyan, }, ), }; }, }, { childName: "bar-3", mutation: () => { return { style: Object.assign( {}, props.style, { fill: VictoryTheme .clean.palette ?.colors.blue, }, ), }; }, }, ]; }, onMouseOut: () => { return [ { childName: "all", mutation: () => { return { style: undefined, }; }, }, ]; }, }, }, ]} > ""} /> ``` ## Bar & Scatter - Combo Bar and Scatter charts are another common combination for polar charts. In the following example, we use a `VictoryPolarAxis` to create a polar chart with a bar and scatter series, and a `VictoryGroup` to apply styles and data to both components. ```jsx live VictoryTheme.clean.palette ?.colors[datum.fill], }, }} data={[ { x: 1, y: 2, label: 1, fill: "red", }, { x: 2, y: 3, label: 2, fill: "orange", }, { x: 3, y: 6, label: 3, fill: "yellow", }, { x: 4, y: 5, label: 4, fill: "blue", }, { x: 5, y: 4, label: 5, fill: "cyan", }, { x: 6, y: 3, label: 6, fill: "green", }, ]} /> ``` ## Line Charts Line charts are another common type of polar chart. In the following example, we use a `VictoryPolarAxis` to create a polar chart with a line series. ```jsx live } > ""} /> } interpolation="linear" data={[ { x: 1, y: 2 }, { x: 2, y: 5 }, { x: 3, y: 9 }, { x: 4, y: 6 }, { x: 5, y: 8 }, { x: 6, y: 8 }, { x: 7, y: 2 }, { x: 8, y: 6 }, ]} /> ``` ## Line & Scatter Combo Line and Scatter charts are a common combination for polar charts. In the following example, we use a `VictoryPolarAxis` to create a polar chart with a line and scatter series, and a `VictoryGroup` to apply styles and data to both components. ```jsx live } > datum.y} labelComponent={ } /> `y: ${datum.y}` } labelComponent={ } /> datum.y} labelComponent={ } /> ``` ## Area Charts Area charts can also be displayed in a polar format. In the following example, we use a `VictoryPolarAxis` to create a polar chart with an area series. ```jsx live ""} /> ``` ## Area Charts - Events Area charts can be interactive with the use of events. In the following example, we use a `VictoryPolarAxis` to create a polar chart with an area series that changes color on hover. ```jsx live { return [ { mutation: () => { return { style: Object.assign( {}, props.style, { fill: VictoryTheme .clean.palette ?.colors.cyan, }, ), }; }, }, ]; }, onMouseOut: () => { return [ { mutation: () => { return { style: undefined, }; }, }, ]; }, }, }, ]} > ""} /> ``` ## Area & Scatter Combo Area and Scatter charts are another common combination for polar charts. In the following example, we use a `VictoryPolarAxis` to create a polar chart with an area and scatter series, and a `VictoryGroup` to apply styles and data to both components. ```jsx live ``` ================================================ FILE: website/docs/guides/themes.mdx ================================================ --- title: Themes & Styling --- ## Themes Victory themes are reusable style configurations that enable you to standardize the appearance of charts across your application. Themes simplify chart development by reducing repetitive styling logic and ensuring visual consistency. Themes are applied at the chart level and automatically cascade to all child components, such as axes, bars, lines, and legends. Victory provides built-in themes like `clean`, `material` and `grayscale`, but you can also create custom themes to match your branding. ### How Themes Work in Victory Victory themes are essentially JavaScript objects that define styling properties for various chart elements. These theme configurations enable consistent and reusable styling across charts. Each theme configuration variable corresponds to a specific chart element and takes props that define its styles. Theme configuration variables include: - [`.palette`](/docs/api/victory-theme#palette) - Defines color schemes used across the theme. - [`.chart`](/docs/api/victory-chart) - Global styles like background and padding. - [`.area`](/docs/api/victory-area) - [`.axis`](/docs/api/victory-axis) - [`.polarAxis`](/docs/api/victory-polar-axis) - [`.polarDependentAxis`](/docs/api/victory-polar-axis) - [`.bar`](/docs/api/victory-bar) - [`.boxplot`](/docs/api/victory-boxplot) - [`.candlestick`](/docs/api/victory-candlestick) - [`.errorbar`](/docs/api/victory-error-bar) - [`.group`](/docs/api/victory-group) - [`.histogram`](/docs/api/victory-histogram) - [`.legend`](/docs/api/victory-legend) - [`.line`](/docs/api/victory-line) - [`.pie`](/docs/api/victory-pie) - [`.scatter`](/docs/api/victory-scatter) - [`.stack`](/docs/api/victory-stack) - [`.tooltip`](/docs/api/victory-tooltip) - [`.voronoi`](/docs/api/victory-voronoi) Each configuration variable takes props specific to its chart element, allowing customization of styles like colors, padding, labels, and more. For detailed information about each variable, refer to the [VictoryTheme API page](/docs/api/victory-theme). When a theme is passed to a chart via the `theme` prop, the styles from the theme object are applied to the corresponding child components unless they are explicitly overridden by inline props. ```jsx live ``` ### Predefined Themes Victory includes several built-in themes to help you quickly style your charts: | Theme | Description | | ------------------------ | --------------------------------------------------------------------------------------------------- | | `VictoryTheme.clean` | A minimalist theme with no gridlines or extra styling, perfect for clean and modern visualizations. | | `VictoryTheme.material` | Inspired by Google's Material Design, this theme includes bold colors and a structured grid. | | `VictoryTheme.grayscale` | A neutral theme featuring grayscale tones, ideal for muted and professional-looking charts. | Each of these themes can be applied to your Victory components by passing it into the `theme` prop. ```jsx live noInline const result = [...Array(10).keys()]; const scatterData = [ ...Array(20).keys(), ].forEach((i) => ({ x: (i - 10) / 3, y: i / 2 - 2 * Math.random() - 4, })); const toInteger = (number) => parseInt(number).toString(); const DemoComponent = () => { const [theme, setTheme] = React.useState( VictoryTheme.grayscale, ); const positions = [ { transform: "translate(0, -15)" }, { transform: "translate(180, -40)", }, { transform: "translate(-10, 140)", }, { transform: "translate(180, 140)", }, ]; return (
data.x * data.x } />
); }; render(); ``` You can also customize these themes or use them as a base to create your own. To build upon a predefined theme, you can extend it using the spread operator: ```jsx const extendedTheme = { ...VictoryTheme.material, axis: { ...VictoryTheme.material.axis, style: { ...VictoryTheme.material.axis .style, tickLabels: { fill: "#444", fontSize: 10, fontStyle: "italic", }, }, }, }; ``` ### Creating a Custom Theme To create a completely custom theme, define a JavaScript object that includes styles for the components you want to theme. You can omit components that use default styling. ```jsx live noInline const customTheme = { axis: { style: { grid: { stroke: "none" }, axis: { stroke: "#333", strokeWidth: 2, }, ticks: { stroke: "#555", size: 5, }, tickLabels: { fill: "#222", fontSize: 12, padding: 5, }, }, }, bar: { style: { data: { fill: "#0074d9", width: 15, }, }, }, }; render( , ); ``` To enhance the "Styles" section of the Victory Themes guide, consider incorporating the following detailed explanations and examples: ## Styling Individual Components To customize the appearance of Victory components, you can use the `style` prop, which accepts an object containing styles for various component elements like `data`, `labels`, and `parent`. For a detailed breakdown of the style options available, refer to the [Victory Style Interface](/docs/api/victory-style-interface). **Example: Customizing Bar and Line Colors** ```jsx live d.x} /> ``` ### Styling Data To style individual elements within a dataset, you can include style attributes directly in your data objects and utilize functional styles. **Note:** Continuous data components like `VictoryLine` and `VictoryArea` render a single SVG element for the entire dataset, so individual styling of data points within these components is not applicable. ```jsx live datum.fill, }, }} data={[ { x: 1, y: 2, fill: "red" }, { x: 2, y: 4, fill: "orange" }, { x: 3, y: 6, fill: "gold" }, ]} /> index % 2 === 0 ? "blue" : "grey", stroke: ({ datum }) => datum.y < 6 ? "red" : "black", strokeWidth: 2, }, }} symbol={({ datum }) => datum.x > 1 ? "plus" : "square" } size={({ datum }) => datum.y + 2} data={[ { x: 0, y: 2 }, { x: 1, y: 4 }, { x: 2, y: 6 }, { x: 3, y: 8 }, { x: 4, y: 10 }, ]} /> ``` ### Using Gradient Fills To apply gradient fills to your charts, define a gradient in the SVG `defs` section and reference it by id in your component's style. Gradients can be used to give continuous charts (i.e. line or area charts) the appearance of discrete data elements and hover states. ```jsx live
``` ================================================ FILE: website/docs/guides/tooltips.mdx ================================================ --- title: Tooltips --- Victory supports multiple ways to show tooltips on your charts. Tooltips can be added to any Victory component, and can be customized to suit your needs. This guide will cover the basics of adding tooltips to your charts, as well as more advanced configurations. ## Basic The simplest way to add tooltips to a chart is to use `VictoryTooltip` as a `labelComponent`. By default, the `labelComponent` will display the `label` prop of the data point it is associated with, unless you specify a custom `labels` function. ```jsx live } data={[ { x: 2, y: 5 }, { x: 4, y: -6 }, { x: 6, y: 4 }, { x: 8, y: -5 }, { x: 10, y: 7 }, ]} labels={({ datum }) => datum.y} /> ``` ## Styles `VictoryTooltip` can be styled using the `style` prop. The `style` prop accepts an object with `data`, `labels`, and `flyout` keys. The `data` key styles the data point, the `labels` key styles the text of the tooltip, and the `flyout` key styles the background of the tooltip. ```jsx live datum.x > 6 ? 0 : 20 } pointerLength={({ datum }) => datum.y > 0 ? 5 : 20 } flyoutStyle={{ stroke: ({ datum }) => datum.x === 10 ? "tomato" : "black", }} /> } data={[ { x: 2, y: 5, label: "right-side-up", }, { x: 4, y: -6, label: "upside-down", }, { x: 6, y: 4, label: "tiny" }, { x: 8, y: -5, label: "or a little \n BIGGER", }, { x: 10, y: 7, label: "automatically", }, ]} style={{ data: { width: 20 }, }} /> ``` ## Events `VictoryTooltip` automatically attaches events to data components. When events of the same type are specified for data components, it is necessary to reconcile events so that tooltips still work. For web, the default tooltip events are: ```jsx static defaultEvents = [{ target: "data", eventHandlers: { onMouseOver: () => ({ target: "labels", mutation: () => ({ active: true }) }), onMouseOut: () => ({ target: "labels", mutation: () => ({ active: undefined }) }), onFocus: () => ({ target: "labels", mutation: () => ({ active: true }) }), onBlur: () => ({ target: "labels", mutation: () => ({ active: undefined }) }) } }]; ``` :::warning When other `onMouseOver` and `onMouseOut` events are specified for data, the event returns described above must be added to the events for tooltips to continue to work properly. ::: ```jsx live } data={[ { x: 2, y: 5, label: "A" }, { x: 4, y: -6, label: "B" }, { x: 6, y: 4, label: "C" }, { x: 8, y: -5, label: "D" }, { x: 10, y: 7, label: "E" }, ]} style={{ data: { width: 20 }, }} events={[ { target: "data", eventHandlers: { onMouseOver: () => { return [ { target: "data", mutation: () => ({ style: { fill: "gold", width: 30, }, }), }, { target: "labels", mutation: () => ({ active: true, }), }, ]; }, onMouseOut: () => { return [ { target: "data", mutation: () => {}, }, { target: "labels", mutation: () => ({ active: false, }), }, ]; }, }, }, ]} /> ``` --- ## Voronoi Use `VictoryVoronoiContainer` to associate a mouse position with the data point(s) closest to it. When this container is added to a chart, changes in mouse position will add the `active` prop to data and label components closest to the current mouse position. The closeness of data points to a given position is determined by calculating a voronoi diagram based on the data of every child `VictoryVoronoiContainer` renders. This container is useful for adding hover interactions, like tooltips, to small data points, or charts with dense or overlapping data. See the [full API here](/docs/api/victory-voronoi-container). ### Basic `VictoryVoronoiContainer` may be used with any Victory component that works with an x-y coordinate system, and should be added as the `containerComponent` of the top-level component. However, the component that uses it must be standalone (`standalone={true}`), which is the default for all top-level Victory components. ```jsx live } theme={VictoryTheme.clean} > active ? 5 : 3 } labels={({ datum }) => datum.y} labelComponent={} data={[ { x: 1, y: -4 }, { x: 2, y: 4 }, { x: 3, y: 2 }, { x: 4, y: 1 }, ]} /> active ? 5 : 3 } labels={({ datum }) => datum.y} labelComponent={} data={[ { x: 1, y: -3 }, { x: 2, y: 3 }, { x: 3, y: 3 }, { x: 4, y: 0 }, ]} /> datum.y} labelComponent={} size={({ active }) => active ? 5 : 3 } /> ``` ### Follow Tooltips When using `VictoryVoronoiContainer` with `VictoryTooltip`, you can add tooltips to your chart that follow the mouse position. ```jsx live `Data ${datum.y}` } mouseFollowTooltips labelComponent={ } /> } > active ? 8 : 3 } /> active ? 5 : 3 } /> ``` ### Active Points `VictoryVoronoiContainer` adds the `active` prop to any data point closest to the current mouse position. This prop can be used to style the active data point differently from the rest. ```jsx live `${_.round(datum.x, 2)}, ${_.round( datum.y, 2, )}` } /> } theme={VictoryTheme.clean} > Math.sin(2 * Math.PI * datum.x) } style={{ data: { fill: ({ active }) => active ? VictoryTheme.clean.palette ?.colors.red : VictoryTheme.clean.palette ?.colors.blue, }, }} /> ``` ### Multipoint Labels `VictoryVoronoiContainer` can also be used to create multi-point labels when the `labels` prop is provided. In the example below the `voronoiDimension` prop indicates that the voronoi diagram will only be specific to the x dimension. For a given mouse position, all data matching the associated x value will be activated regardless of y value. In the following example, this leads to several tooltips being active at the same time. Provide a `labels` and (optionally) a `labelComponent` prop to configure multi-point labels. ```jsx live `y: ${datum.y}` } labelComponent={ } /> } theme={VictoryTheme.clean} > active ? 4 : 2, }, labels: { fill: "tomato" }, }} /> active ? 4 : 2, }, labels: { fill: "blue" }, }} /> active ? 4 : 2, }, labels: { fill: "black" }, }} /> ``` --- ## Cursor Use `VictoryCursorContainer` to add a cursor to a chart to inspect coordinates. The cursor can either be a 2-dimensional crosshair, or a 1-dimensional line. The cursor moves with the mouse (or on touch on mobile devices) along the visible domain of the chart. The cursor can also display a label for the active coordinates using the `cursorLabel` prop. See the [full API here](/docs/api/victory-cursor-container). ### Line Charts Using the `VictoryCursorContainer` component, you can add a 2D cursor to a line chart. ```jsx live `${_.round(datum.x, 2)}, ${_.round( datum.y, 2, )}` } /> } theme={VictoryTheme.clean} > d.x * d.x} /> ``` ### Dimension Limits You can also use the `cursorDimension` prop to create a 1D cursor. This is useful for line charts where you only want to inspect one dimension. Note you can also set a `defaultCursorValue` to set the initial position of the cursor. ```jsx live `${_.round(datum.x, 2)}, ${_.round( datum.y, 2, )}` } cursorDimension="x" defaultCursorValue={0.3} /> } theme={VictoryTheme.clean} > d.x * d.x} /> ``` ### Bar Charts You can also use the `VictoryCursorContainer` component with bar charts. ```jsx live _.round(datum.x, 2) } cursorDimension="y" defaultCursorValue={3} /> } > ``` ### Scatter Charts You can also use the `VictoryCursorContainer` component with scatter charts. Note how we can apply the container directly to the `VictoryScatter` component. ```jsx live active ? VictoryTheme.clean.palette ?.colors.teal || "teal" : VictoryTheme.clean.palette ?.colors.purple || "purple", }, }} containerComponent={ _.round(datum.x, 2) } defaultCursorValue={1} /> } data={[ { x: -3, y: 2 }, { x: 0, y: -2 }, { x: -8, y: 1 }, { x: -2, y: -3 }, { x: 7, y: 5 }, { x: -8, y: 6 }, { x: -1, y: 3 }, { x: -4, y: -5 }, { x: -6, y: -5 }, ]} /> ``` ### Events You can also use the `onCursorChange` prop to listen to cursor changes and inspect the values. ```jsx live noInline function App() { const [cursorValue, setCursorValue] = React.useState(null); function onCursorChange(value) { setCursorValue(value); } return ( _.round(datum.x, 2) } cursorLabelOffset={15} onCursorChange={ onCursorChange } /> } > {cursorValue && ( )} ); } render(); ``` --- ## Custom Components `VictoryTooltip` is composed of [`VictoryLabel`](/docs/api/victory-label) and the primitive [`Flyout`](/docs/api/victory-primitives#flyout) component. Both of these components are highly configurable, but may also be replaced if necessary. ### Custom Flyout An example of replacing the default `Flyout` component with a custom component. ```jsx live noInline const colors = VictoryTheme.clean.palette.cool; function CustomFlyout(props) { const { x, y, orientation } = props; const newY = orientation === "bottom" ? y - 35 : y + 35; return ( ); } function App() { return ( } /> } data={[ { x: 2, y: 5, label: "A" }, { x: 4, y: -6, label: "B" }, { x: 6, y: 4, label: "C" }, { x: 8, y: -5, label: "D" }, { x: 10, y: 7, label: "E" }, ]} style={{ data: { width: 20 }, }} /> ); } render(); ``` ### Custom Label An example of using custom labels with a donut chart. ```jsx live noInline function CustomLabel(props) { return ( ); } CustomLabel.defaultEvents = VictoryTooltip.defaultEvents; function App() { return ( } data={[ { x: 1, y: 5, label: "Dogs" }, { x: 2, y: 4, label: "Cats" }, { x: 3, y: 2, label: "Rabbits", }, { x: 4, y: 3, label: "Birds" }, { x: 5, y: 1, label: "Snakes" }, ]} /> ); } render(); ``` ### Custom Wrapping The events that control `VictoryTooltip` are stored on the static `defaultEvents` property. Wrapped instances of `VictoryTooltip` will need to replicate or hoist this property in order to add automatic events to the components that use them. ```jsx live noInline function CustomTooltip(props) { const { x, y } = props; const rotation = `rotate(45 ${x} ${y})`; return ( ); } CustomTooltip.defaultEvents = VictoryTooltip.defaultEvents; function App() { return ( } data={[ { x: 2, y: 5, label: "A" }, { x: 4, y: -6, label: "B" }, { x: 6, y: 4, label: "C" }, { x: 8, y: -5, label: "D" }, { x: 10, y: 7, label: "E" }, ]} /> ); } render(); ``` ## Victory Native In Victory Native tooltips are much more reliable when using `VictoryVoronoiContainer`. Using `VictoryVoronoiContainer` registers all touch events on the container itself, which mitigates interference with other chart elements, which can be a problem on some platforms. Showing the closest data point with `VictoryVoronoiContainer` also increases the tap targets for the tooltip, which can otherwise be quite small. Set `VictoryVoronoiContainer` as the `containerComponent` prop on the outermost Victory component. ```jsx } > } labels={({ datum }) => datum.y} style={{ data: { fill: ({ datum }) => datum.fill, }, }} data={[ { x: 1, y: 3 }, { x: 3, y: 5 }, ]} /> ``` ================================================ FILE: website/docs/guides/zoom-large-data.mdx ================================================ --- title: Zoom on Large Datasets --- Victory can handle hundreds of data points, but what if you'd like chart thousands of points? [VictoryZoomContainer][] can be useful here, allowing the user to focus on the subset of data they are most interested in. By default Victory will render all data points in a dataset. For large datasets this behavior can be overridden, and this guide will show you how. If you haven't used it before, read about the [VictoryZoomContainer][] first, then come back to this guide. In a hurry? [Skip to the demo][]. ## Basic scenario: time-series data In this guide, we'll be working with time-series data. We'll make a few basic assumptions: 1. zooming will be done only on the x (or time) dimension, 2. our data will be ordered by x, from earliest to latest, and 3. the dataset is static - no new data will arrive while the user is interacting with the chart. These just serve to simplify the example. We'll start with a simple chart: ```jsx function CustomChart(props) { const [state, setState] = React.useState({}); return ( } ); } ``` ## Render only visible points Rather than passing all our data to the `data` prop, we'll first remove any data that isn't currently visible. To do this, we must keep track of the chart's visible domain; [VictoryZoomContainer][] has an `onZoomDomainChange` prop that will allow us to do exactly that: ```jsx } > ``` Update the state every time the domain changes (note that we are only keeping track of the `x` dimension): ```js onDomainChange(domain) { setState({ zoomedXDomain: domain.x, }); } ``` Use this `zoomedXDomain` state to filter out all data that isn't currently visible. Here we're making a simple `getData` method; note the data array's `filter` function: ```js function getData() { const { zoomedXDomain } = state; const { data } = props; return data.filter( // is d "between" the ends of the visible x-domain? (d) => (d.x >= zoomedXDomain[0] && d.x <= zoomedXDomain[1])); } ``` Because we are dynamically changing the `data` prop on [VictoryChart][], we must also explicitly set its `domain`. By default `VictoryChart` will calculate a domain from `data`; in this case that would mean that the chart would "forget" about the rest of the data as the user zoomed in. In fact, there would be no way to zoom back out! To remedy this, we must calculate the domain of the entire dataset: ```js function getEntireDomain(props) { const { data } = props; return { y: [_.minBy(data, d => d.y).y, _.maxBy(data, d => d.y).y], x: [ data[0].x, _.last(data).x ] }; } ``` We use [Lodash][]'s `minBy` and `maxBy` functions to find the domain of `y`. Because we assume that the data is ordered by `x`, we can use the first and last points for the `x` domain. Because we are assuming the data is static we just need to call this function once, in the constructor of `CustomChart`. The calculated `x` domain will also be used as the initial value for `state.zoomedXDomain`: ```js const entireDomain = getEntireDomain(props); const [state, setState] = React.useState({ zoomedXDomain: entireDomain.x, }); ``` The static value `entireDomain` can then be used by `VictoryChart`: ```jsx } > ``` Now we are only rendering the visible points, but this step isn't nearly enough: when the chart is zoomed out we still render all of the data points! ## Render a small sample of points There are a number of possible methods to reduce the number of visible data points rendered. We'll use the simplest method: selecting only every `k`th point, and discarding all others. `k` is determined simply: if there are 5,000 points and we only want to show 100, then `k` is 50. We'll add a `maxPoints` prop to `CustomChart` which will determine the maximum number of points returned by `getData`. All of the existing logic in `getData` will stay and another `filter` will be added afterwards: ```js function getData() { const { zoomedXDomain } = state; const { data, maxPoints } = props; const filtered = data.filter( (d) => (d.x >= zoomedXDomain[0] && d.x <= zoomedXDomain[1])); // new code here... if (filtered.length > maxPoints ) { const k = Math.ceil(filtered.length / maxPoints); return filtered.filter( (d, i) => ((i % k) === 0) ); } return filtered; } ``` Now the chart will always render at most `maxPoints`, no matter the zoom level. ## Demo ```jsx live noInline // 10000 points (10 / 0.001 = 10000) // see what happens when you render 50k or 100k const allData = _.range(0, 10, 0.001).map(x => ({ x: x, y: Math.sin(Math.PI*x/2) * x / 10 })); function getEntireDomain(props) { const { data } = props; return { y: [_.minBy(data, d => d.y).y, _.maxBy(data, d => d.y).y], x: [ data[0].x, _.last(data).x ] }; } function CustomChart(props) { const entireDomain = getEntireDomain(props); const [state, setState] = React.useState({ zoomedXDomain: entireDomain.x, }); function onDomainChange(domain) { setState({ zoomedXDomain: domain.x, }); } function getData() { const { zoomedXDomain } = state; const { data, maxPoints } = props; const filtered = data.filter( (d) => (d.x >= zoomedXDomain[0] && d.x <= zoomedXDomain[1])); if (filtered.length > maxPoints ) { const k = Math.ceil(filtered.length / maxPoints); return filtered.filter( (d, i) => ((i % k) === 0) ); } return filtered; } function getZoomFactor() { const { zoomedXDomain } = state; const factor = 10 / (zoomedXDomain[1] - zoomedXDomain[0]); return _.round(factor, factor < 3 ? 1 : 0); } const renderedData = getData(); return (
} theme={VictoryTheme.clean} >
{getZoomFactor()}x zoom; rendering {renderedData.length} of {props.data.length}
); } render(); ``` ## Extending this Demo This guide serves as a start, but you might have some questions: * _How big should `maxPoints` be?_ For most situations between 50 and 150 is ideal. * _What if I want to render millions of data points?_ This concept can be extended to millions of points, but you'll need the help of a library to handle the sampling. Try [Crossfilter][]. * _Can I remove the "flicker" of points as I zoom in?_ Yes, but `getData()` will have to be a little more complex. This apparent movement of the points while zooming happens because different points are chosen to be displayed. Here is an example that reduces flicker by reliably choosing the same data points to display: ```js function getData() { const { zoomedXDomain } = state; const { data, maxPoints } = props; const startIndex = data.findIndex((d) => d.x >= zoomedXDomain[0]); const endIndex = data.findIndex((d) => d.x > zoomedXDomain[1]); const filtered = data.slice(startIndex, endIndex); if (filtered.length > maxPoints ) { // limit k to powers of 2, e.g. 64, 128, 256 // so that the same points will be chosen reliably, reducing flicker const k = Math.pow(2, Math.ceil(Math.log2(filtered.length / maxPoints))); return filtered.filter( // ensure modulo is always calculated from same reference: i + startIndex (d, i) => (((i + startIndex) % k) === 0) ); } return filtered; } ``` [VictoryZoomContainer]: /docs/api/victory-zoom-container [VictoryChart]: /docs/api/victory-chart [Skip to the demo]: /docs/guides/zoom-large-data [Lodash]: https://lodash.com/ [Crossfilter]: http://square.github.io/crossfilter/ ================================================ FILE: website/docs/introduction/_category_.json ================================================ { "label": "Introduction", "position": 0, "link": null } ================================================ FILE: website/docs/introduction/index.mdx ================================================ --- title: Getting Started sidebar_position: 1 hide_table_of_contents: true --- # Getting Started with Victory Victory is an opinionated, but fully overridable, ecosystem of composable React components for building interactive data visualizations. The following tutorial explains how to set up a basic chart. ```jsx live noInline const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, { name: "Germany", data: [ 26.903002, 28.712002, 30.979002, 33.477, 38.614002, 44.58, 49.435, 55.58, 58.721004, 60.742004, 62.201004, 63.833004, 66.315, ], }, { name: "India", data: [ 13.184001, 16.179, 17.2997, 18.4204, 22.465302, 25.08819, 28.700441, 32.84846, 35.288105, 37.50518, 38.558605, 40.06727, 41.929783, ], }, { name: "United States", data: [ 39.349697, 45.79497, 59.453304, 60.198166, 64.43019, 72.767235, 81.502365, 87.83079, 94.66619, 103.835556, 118.66354, 133.01929, 140.86162, ], }, { name: "Italy", data: [ 5.794, 6.918, 8.102, 8.542001, 8.683001, 9.137, 9.384001, 9.736579, 10.230247, 10.679461, 10.870623, 11.253734, 11.779734, ], }, { name: "Japan", data: [ 2.2940001, 2.4190001, 2.562, 2.6460001, 2.753, 2.808, 3.2470002, 3.4830003, 3.4980001, 3.9540002, 4.367, 4.467, 4.577, ], }, { name: "Spain", data: [ 20.693, 21.529001, 22.789001, 22.958, 22.925001, 22.943, 22.990002, 23.12448, 23.405056, 25.590076, 26.819191, 27.907652, 29.307837, ], }, ]; const symbols = [ "circle", "diamond", "plus", "square", "triangleUp", ]; function App() { return ( `${value} GW` } style={{ axis: { stroke: "transparent", }, axisLabel: { fontSize: 8, padding: 50, }, tickLabels: { fontSize: 8, }, grid: { stroke: "#d9d9d9", size: 5, }, }} /> {series.map((s, i) => ( ({ x: i + 2010, y: d, }))} key={s.name} > ))} ({ name: s.name, symbol: { fill: VictoryTheme.clean .palette.qualitative[ series.indexOf(s) ], type: symbols[ series.indexOf(s) ], }, }))} style={{ data: { fill: ({ datum }) => datum.symbol.fill, }, labels: { fontSize: 8, }, border: { stroke: "transparent", }, }} /> ); } render(); ``` ## Tutorial In this guide, we’ll show you how to get started with Victory and walk you through the creation and customization of a composed chart. ### 1. Import Victory Add Victory to your project with the command `npm install victory`, then import it into your component. For now, let's start with a simple Line Chart. ```jsx import React from "react"; import { VictoryChart, VictoryLine, } from "victory"; ``` ### 2. Start with a basic chart Components include sensible defaults, so even without data the chart will render with samples. To add some basic styling, we will use our built in `clean` theme. When a theme is applied to [`VictoryChart`](/docs/api/victory-chart), it will be inherited by all child components. ```jsx live noInline function App() { return ( ); } render(); ``` ### 3. Add your data Let's add some data. Victory cartesian charts look for `x` and `y` values in data points, which our data doesn't have. We can work around this by adding accessor props to our [`VictoryLine`](/docs/api/victory-line) component. Our data contains the country name, and an array of values from 2010 to 2022. ```ts type Data = { name: string; data: number[]; }; ``` ```jsx live noInline const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, ]; function App() { return ( ({ x: i + 2010, y: d, }), )} /> ); } render(); ``` ### 4. Customize the X axis [`VictoryChart`](/docs/api/victory-chart) is a wrapper component that plots all of its children on the same scale. The default axes may not always be what you need, but they can be customized with [`VictoryAxis`](/docs/api/victory-axis) components. We will start by adding a dependent axis to our chart. This axis will be the vertical axis, and we will customize it with a label and tick values. ```jsx `${value} GW`} /> ``` Our data is an array of values from the year 2010 to 2022, so we will also add a horizontal axis with tick values. ```tsx ``` Finally, we will customize the axis styles to make it more readable. We will adjust the font size of the tick labels and the axis ticks, and add grid lines to the dependent axis. ```jsx live noInline const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, ]; function App() { return ( `${value} GW` } style={{ axis: { stroke: "transparent", }, axisLabel: { fontSize: 8, padding: 50, }, tickLabels: { fontSize: 8, }, grid: { stroke: "#d9d9d9", size: 5, }, }} /> ({ x: i + 2010, y: d, }), )} /> ); } render(); ``` ### 5. Multiple Series Lets expand our data to include multiple countries by iterating over the `series` array and adding a [`VictoryLine`](/docs/api/victory-line) component for each country. Since we need to identify the unique trend lines, we will also adjust the stroke color of each line. ```jsx { series.map((s, i) => ( ({ x: i + 2010, y: d, }))} style={{ data: { stroke: VictoryTheme.clean.palette .qualitative[i], strokeWidth: 1, }, }} /> )); } ``` ```jsx live noInline const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, { name: "Germany", data: [ 26.903002, 28.712002, 30.979002, 33.477, 38.614002, 44.58, 49.435, 55.58, 58.721004, 60.742004, 62.201004, 63.833004, 66.315, ], }, { name: "India", data: [ 13.184001, 16.179, 17.2997, 18.4204, 22.465302, 25.08819, 28.700441, 32.84846, 35.288105, 37.50518, 38.558605, 40.06727, 41.929783, ], }, { name: "United States", data: [ 39.349697, 45.79497, 59.453304, 60.198166, 64.43019, 72.767235, 81.502365, 87.83079, 94.66619, 103.835556, 118.66354, 133.01929, 140.86162, ], }, { name: "Italy", data: [ 5.794, 6.918, 8.102, 8.542001, 8.683001, 9.137, 9.384001, 9.736579, 10.230247, 10.679461, 10.870623, 11.253734, 11.779734, ], }, { name: "Japan", data: [ 2.2940001, 2.4190001, 2.562, 2.6460001, 2.753, 2.808, 3.2470002, 3.4830003, 3.4980001, 3.9540002, 4.367, 4.467, 4.577, ], }, { name: "Spain", data: [ 20.693, 21.529001, 22.789001, 22.958, 22.925001, 22.943, 22.990002, 23.12448, 23.405056, 25.590076, 26.819191, 27.907652, 29.307837, ], }, ]; function App() { return ( `${value} GW` } style={{ axis: { stroke: "transparent", }, axisLabel: { fontSize: 8, padding: 50, }, tickLabels: { fontSize: 8, }, grid: { stroke: "#d9d9d9", size: 5, }, }} /> {series.map((s, i) => ( ({ x: i + 2010, y: d, }))} key={s.name} /> ))} ); } render(); ``` ### 6. Combining Chart Types To make each data point stand out more, we can combine our [`VictoryLine`](/docs/api/victory-line) chart with a [`VictoryScatter`](/docs/api/victory-scatter) chart. Victory provides a specialized wrapper component [`VictoryGroup`](/docs/api/victory-group) that helps us apply properties to multiple components at once. ```jsx { series.map((s, i) => ( ({ x: i + 2010, y: d, }))} key={s.name} > )); } ``` ```jsx live noInline const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, { name: "Germany", data: [ 26.903002, 28.712002, 30.979002, 33.477, 38.614002, 44.58, 49.435, 55.58, 58.721004, 60.742004, 62.201004, 63.833004, 66.315, ], }, { name: "India", data: [ 13.184001, 16.179, 17.2997, 18.4204, 22.465302, 25.08819, 28.700441, 32.84846, 35.288105, 37.50518, 38.558605, 40.06727, 41.929783, ], }, { name: "United States", data: [ 39.349697, 45.79497, 59.453304, 60.198166, 64.43019, 72.767235, 81.502365, 87.83079, 94.66619, 103.835556, 118.66354, 133.01929, 140.86162, ], }, { name: "Italy", data: [ 5.794, 6.918, 8.102, 8.542001, 8.683001, 9.137, 9.384001, 9.736579, 10.230247, 10.679461, 10.870623, 11.253734, 11.779734, ], }, { name: "Japan", data: [ 2.2940001, 2.4190001, 2.562, 2.6460001, 2.753, 2.808, 3.2470002, 3.4830003, 3.4980001, 3.9540002, 4.367, 4.467, 4.577, ], }, { name: "Spain", data: [ 20.693, 21.529001, 22.789001, 22.958, 22.925001, 22.943, 22.990002, 23.12448, 23.405056, 25.590076, 26.819191, 27.907652, 29.307837, ], }, ]; const symbols = [ "circle", "diamond", "plus", "square", "triangleUp", ]; function App() { return ( `${value} GW` } style={{ axis: { stroke: "transparent", }, axisLabel: { fontSize: 8, padding: 50, }, tickLabels: { fontSize: 8, }, grid: { stroke: "#d9d9d9", size: 5, }, }} /> {series.map((s, i) => ( ({ x: i + 2010, y: d, }))} key={s.name} > ))} ); } render(); ``` ### 7. Adding a Legend To make it easier to identify each country, we can add a legend to our chart. Victory provides a [`VictoryLegend`](/docs/api/victory-legend) component that can be used to display a legend for the chart. ```jsx ({ name: s.name, symbol: { fill: VictoryTheme.clean.palette .qualitative[i], type: symbols[i], }, }))} /> ``` Since our Legend is going to take up some space in our chart, we also need to adjust the padding to provide enough space for the legend. ```jsx ``` ```jsx live noInline const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, { name: "Germany", data: [ 26.903002, 28.712002, 30.979002, 33.477, 38.614002, 44.58, 49.435, 55.58, 58.721004, 60.742004, 62.201004, 63.833004, 66.315, ], }, { name: "India", data: [ 13.184001, 16.179, 17.2997, 18.4204, 22.465302, 25.08819, 28.700441, 32.84846, 35.288105, 37.50518, 38.558605, 40.06727, 41.929783, ], }, { name: "United States", data: [ 39.349697, 45.79497, 59.453304, 60.198166, 64.43019, 72.767235, 81.502365, 87.83079, 94.66619, 103.835556, 118.66354, 133.01929, 140.86162, ], }, { name: "Italy", data: [ 5.794, 6.918, 8.102, 8.542001, 8.683001, 9.137, 9.384001, 9.736579, 10.230247, 10.679461, 10.870623, 11.253734, 11.779734, ], }, { name: "Japan", data: [ 2.2940001, 2.4190001, 2.562, 2.6460001, 2.753, 2.808, 3.2470002, 3.4830003, 3.4980001, 3.9540002, 4.367, 4.467, 4.577, ], }, { name: "Spain", data: [ 20.693, 21.529001, 22.789001, 22.958, 22.925001, 22.943, 22.990002, 23.12448, 23.405056, 25.590076, 26.819191, 27.907652, 29.307837, ], }, ]; const symbols = [ "circle", "diamond", "plus", "square", "triangleUp", ]; function App() { return ( `${value} GW` } style={{ axis: { stroke: "transparent", }, axisLabel: { fontSize: 8, padding: 50, }, tickLabels: { fontSize: 8, }, grid: { stroke: "#d9d9d9", size: 5, }, }} /> {series.map((s, i) => ( ({ x: i + 2010, y: d, }))} key={s.name} > ))} ({ name: s.name, symbol: { fill: VictoryTheme.clean .palette.qualitative[ series.indexOf(s) ], type: symbols[ series.indexOf(s) ], }, }))} style={{ data: { fill: ({ datum }) => datum.symbol.fill, }, labels: { fontSize: 8, }, border: { stroke: "transparent", }, }} /> ); } render(); ``` ### 8. Adding Labels Finally, we can add labels to our chart to provide more context. We will add a title and a source link to our chart as well as axes labels. ```jsx ``` ```jsx live noInline const series = [ { name: "Canada", data: [ 3.9670002, 5.2650003, 6.201, 7.8010006, 9.694, 11.214001, 11.973001, 12.250001, 12.816001, 13.413001, 13.626961, 14.30356, 15.295461, ], }, { name: "Germany", data: [ 26.903002, 28.712002, 30.979002, 33.477, 38.614002, 44.58, 49.435, 55.58, 58.721004, 60.742004, 62.201004, 63.833004, 66.315, ], }, { name: "India", data: [ 13.184001, 16.179, 17.2997, 18.4204, 22.465302, 25.08819, 28.700441, 32.84846, 35.288105, 37.50518, 38.558605, 40.06727, 41.929783, ], }, { name: "United States", data: [ 39.349697, 45.79497, 59.453304, 60.198166, 64.43019, 72.767235, 81.502365, 87.83079, 94.66619, 103.835556, 118.66354, 133.01929, 140.86162, ], }, { name: "Italy", data: [ 5.794, 6.918, 8.102, 8.542001, 8.683001, 9.137, 9.384001, 9.736579, 10.230247, 10.679461, 10.870623, 11.253734, 11.779734, ], }, { name: "Japan", data: [ 2.2940001, 2.4190001, 2.562, 2.6460001, 2.753, 2.808, 3.2470002, 3.4830003, 3.4980001, 3.9540002, 4.367, 4.467, 4.577, ], }, { name: "Spain", data: [ 20.693, 21.529001, 22.789001, 22.958, 22.925001, 22.943, 22.990002, 23.12448, 23.405056, 25.590076, 26.819191, 27.907652, 29.307837, ], }, ]; const symbols = [ "circle", "diamond", "plus", "square", "triangleUp", ]; function App() { return ( `${value} GW` } style={{ axis: { stroke: "transparent", }, axisLabel: { fontSize: 8, padding: 50, }, tickLabels: { fontSize: 8, }, grid: { stroke: "#d9d9d9", size: 5, }, }} /> {series.map((s, i) => ( ({ x: i + 2010, y: d, }))} key={s.name} > ))} ({ name: s.name, symbol: { fill: VictoryTheme.clean .palette.qualitative[ series.indexOf(s) ], type: symbols[ series.indexOf(s) ], }, }))} style={{ data: { fill: ({ datum }) => datum.symbol.fill, }, labels: { fontSize: 8, }, border: { stroke: "transparent", }, }} /> ); } render(); ``` ## Next Steps Congratulations! You’ve created your first chart with Victory. Happy charting. ## Documentation, Contributing, and Source For more information about Victory and its components, check out the docs - see [VictoryChart](/docs/api/victory-chart) to get started. Interested in helping out or seeing what's happening under the hood? Victory is maintained at [github.com/FormidableLabs/victory](https://github.com/FormidableLabs/victory), and you can [start contributing here](https://github.com/FormidableLabs/victory/#contributing). ================================================ FILE: website/docs/introduction/native.mdx ================================================ --- sidebar_position: 2 title: React Native --- :::warning These docs are for the legacy versions of Victory Native. Victory Native XL is our rewrite of Victory Native designed specifically for React Native. Please [see here for more information](https://commerce.nearform.com/open-source/victory-native/). If you would like to continue to use this version of Victory with React Native, follow the legacy guide below. ::: ## Getting Started In this guide, we’ll show you how to get started with Victory Native and the React Native SVG dependency running in your React Native app for iOS and Android. ### 1. Adding Victory Native to your React Native app Visit [the guide on getting started](https://reactnative.dev/docs/getting-started) with React Native if you’re just getting started with React Native. Victory Native is compatible with React Native 0.50 or higher. To add Victory Native to your React Native app install `victory-native`. ```bash $ yarn add victory-native@legacy # or npm install --save victory-native@legacy ``` ### 2. Add React Native SVG to your app If you are building a project with native code, you will need to link the native dependencies of React Native SVG to the iOS and Android projects. This step is not required if you are using Expo (SDK 23.0.0 or higher) as it is already included. React Native 0.60 or newer: ```bash $ yarn add react-native-svg # or npm install --save react-native-svg $ cd ios $ pod install ``` React Native below 0.60: ```bash $ react-native install react-native-svg ``` *note:* If you run the iOS app and see a linker error for `-lRNSVG-tvOS` you will need to remove `libRNSVG-tvOS.a` from the “Link Binary with Libraries” section within your iOS app’s target’s properties. ### 3. Using Victory Native in your React Native app Victory Native behaves and functions the same way for React Native as it does for the web. Just import components from `victory-native` to get started. To learn more about how to use Victory visit the [Getting Started Guide][]. The example below shows how Victory Native easily integrates within your React Native app. ```jsx import React from "react"; import { StyleSheet, View } from "react-native"; import { VictoryBar, VictoryChart, VictoryTheme } from "victory-native"; const data = [ { quarter: 1, earnings: 13000 }, { quarter: 2, earnings: 16500 }, { quarter: 3, earnings: 14250 }, { quarter: 4, earnings: 19000 } ]; export default function App() { return ( ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: "center", alignItems: "center", backgroundColor: "#f5fcff" } }); ``` ### 4. Ignoring require cycles - see [https://github.com/FormidableLabs/victory/issues/2230](https://github.com/FormidableLabs/victory/issues/2230) As of victory@36.4.0, React Native apps (on both iOS and Android) will warn about require cycles. These warnings will not affect the functionality of `victory-native` or your app, and can be safely disabled. To disable the warnings, modify your app's entry point (usually `index.js`) to include ```js LogBox.ignoreLogs([ "Require cycle: node_modules/victory", ]); ``` as shown below ```diff +import { AppRegistry, LogBox } from "react-native"; import App from "./App"; import { name as appName } from "./app.json"; +LogBox.ignoreLogs(['Require cycle: node_modules/victory']); AppRegistry.registerComponent(appName, () => App); ``` ### 5. Testing Components that use Victory Native You can test your components that render Victory Native using Jest and React Test Renderer which is included out–of–the box with React Native. The `jest` configuration section in `package.json` will need to be modified to ensure dependencies are transformed otherwise you will see an error when tests are run. ```json "jest": { "preset": "react-native", "transformIgnorePatterns": [ "node_modules/(?!victory|react-native-svg|react-native)" ], "transform": { "^.+\\.jsx?$": "babel-jest" } } ``` To test the above `App` component you can simply do: ```jsx import "react-native"; import React from "react"; import App from "../App.js"; import renderer from "react-test-renderer"; it("renders correctly", () => { const tree = renderer.create(); expect(tree).toMatchSnapshot(); }); ``` *note:* `renderer` must be imported _after_ `react-native` for tests to work. ## Expo Web Apps Whilst using `victory-native` in Expo apps that target iOS & Android is fully supported, we do not support building for the web with `victory-native`. However as both `victory-native` and `victory` share the same public API, it's possible to configure your Expo project so that it automatically uses `victory-native` when building your native apps for iOS & Android, and `victory` when building your web app. > ☣️ Please note that while you can follow the instructions below to configure your Expo project to make this work, Victory does not officially support Expo Web apps. ```sh yarn add -D @expo/webpack-config ``` Then, create a `webpack.config.js` file in the root of your Expo project ```js const createExpoWebpackConfigAsync = require('@expo/webpack-config'); module.exports = async function(env, argv) { const config = await createExpoWebpackConfigAsync(env, argv); // resolve victory-native as victory for the Web app config.resolve.alias['victory-native'] = 'victory'; return config; }; ``` [getting started guide]: /docs/introduction ================================================ FILE: website/docs/introduction/ssr.mdx ================================================ --- sidebar_position: 3 title: Server Side Rendering --- # Server Side Rendering In frameworks such as Next.js 13+, context is fully supported within Client Components, but it cannot be created or consumed directly within Server Components. This is because Server Components have no React state (since they're not interactive), and context is primarily used for rerendering interactive components deep in the tree after some React state has been updated. Victory uses `createContext` to perform its operations and it must be rendered client side by adding the `use client` directive in your components when used in a framework with Server Side Component support. ```jsx "use client"; import React from 'react'; import { VictoryBar, VictoryChart, VictoryTheme } from "victory"; const data = [ { quarter: 1, earnings: 13000 }, { quarter: 2, earnings: 16500 }, { quarter: 3, earnings: 14250 }, { quarter: 4, earnings: 19000 } ]; const App = ()=>{ return (
); } export default App; ``` ================================================ FILE: website/docusaurus.config.ts ================================================ import { themes as prismThemes } from "prism-react-renderer"; import { Config } from "@docusaurus/types"; const title = "Victory"; const tagline = "Intuitive React components for advanced charting and data visualization."; const config: Config = { title, tagline, favicon: "favicon.ico", url: "https://commerce.nearform.com/", baseUrl: "/open-source/victory", onBrokenAnchors: "throw", onBrokenLinks: "throw", onBrokenMarkdownLinks: "throw", onDuplicateRoutes: "throw", i18n: { defaultLocale: "en", locales: ["en"], }, presets: [ [ "classic", /** @type {import('@docusaurus/preset-classic').Options} */ { docs: { sidebarPath: "./sidebars.ts", sidebarCollapsed: true, }, theme: { customCss: "./src/css/custom.css", }, gtag: { trackingID: "G-M971D063B9", }, googleTagManager: { containerId: "GTM-MD32945", }, }, ], ], themes: [ [ "@docusaurus/theme-live-codeblock", { liveCodeBlock: { playgroundPosition: "top", }, }, ], [ require.resolve("@easyops-cn/docusaurus-search-local"), /** @type {import("@easyops-cn/docusaurus-search-local").PluginOptions} */ { hashed: true, indexBlog: false, }, ], ], plugins: [ async function tailwindPlugin() { return { name: "tailwind-plugin", configurePostCss(postcssOptions) { postcssOptions.plugins = [ /* eslint-disable @typescript-eslint/no-require-imports */ require("postcss-import"), require("tailwindcss"), require("autoprefixer"), /* eslint-enable @typescript-eslint/no-require-imports */ ]; return postcssOptions; }, }; }, ], themeConfig: { /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ metadata: [ { name: "title", content: `${title} - React Charting Components`, }, { name: "description", content: tagline, }, { name: "viewport", content: "width=device-width, initial-scale=1, maximum-scale=1", }, { name: "keywords", content: "victory, documentation, react, charts, charting, data, viz, d3", }, { property: "og:type", content: "website" }, { property: "og:url", content: "https://commerce.nearform.com/open-source/victory/", }, { property: "og:title", content: `${title} - React Charting Components` }, { property: "og:description", content: tagline, }, { property: "og:image", content: "https://commerce.nearform.com/open-source/victory/open-graph.png", }, { property: "twitter:card", content: "summary_large_image" }, { property: "twitter:url", content: "https://commerce.nearform.com/open-source/victory/", }, { property: "twitter:title", content: `${title} - React Charting Components`, }, { property: "twitter:description", content: tagline, }, { property: "twitter:image", content: "https://commerce.nearform.com/open-source/victory/open-graph.png", }, ], docs: { sidebar: { hideable: false, }, }, navbar: { title: "VICTORY", logo: { alt: "Victory", src: "favicon/favicon-32x32.png", }, items: [ { type: "docSidebar", sidebarId: "sidebar", position: "left", label: "DOCS", }, { to: "/themes", label: "THEMES", position: "left", }, { href: "https://github.com/FormidableLabs/victory", "aria-label": "GitHub Repository", className: "header-github-link", position: "right", }, ], }, footer: { logo: { alt: "Nearform logo", src: "img/nearform-logo-white.svg", href: "https://commerce.nearform.com", width: 100, height: 100, }, copyright: `Copyright © 2013-${new Date().getFullYear()} Nearform`, }, prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, additionalLanguages: ["diff", "diff-ts"], }, colorMode: { defaultMode: "light", disableSwitch: true, }, }, headTags: [ { tagName: "link", attributes: { rel: "preconnect", href: "https://fonts.googleapis.com", }, }, { tagName: "link", attributes: { rel: "preconnect", href: "https://fonts.gstatic.com", crossOriginIsolated: "true", }, }, { tagName: "link", attributes: { rel: "stylesheet", href: 'https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,100..900&display=swap" rel="stylesheet', }, }, ], }; export default config; ================================================ FILE: website/package.json ================================================ { "name": "victory-docs", "version": "1.0.0", "private": true, "scripts": { "docusaurus": "docusaurus", "start": "docusaurus start --port 5855 --no-open", "build": "docusaurus build --out-dir build/open-source/victory", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "clear": "docusaurus clear", "serve": "docusaurus serve --dir build/open-source/victory", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", "typecheck": "tsc" }, "dependencies": { "@algolia/client-search": "^5.3.1", "@docusaurus/core": "^3.5.2", "@docusaurus/plugin-content-docs": "^3.5.2", "@docusaurus/plugin-google-gtag": "^3.5.2", "@docusaurus/plugin-google-tag-manager": "^3.5.2", "@docusaurus/preset-classic": "^3.5.2", "@docusaurus/theme-common": "^3.5.2", "@docusaurus/theme-live-codeblock": "^3.5.2", "@easyops-cn/docusaurus-search-local": "^0.44.5", "@mdx-js/react": "^3.0.0", "@monaco-editor/react": "^4.6.0", "axios": "^1.7.7", "clsx": "^2.0.0", "date-fns": "^3.6.0", "formidable-oss-badges": "^1.6.0", "prism-react-renderer": "^2.4.0", "react": "^18.0.0", "react-dom": "^18.0.0", "react-icons": "^5.3.0", "react-live": "^4.1.7", "search-insights": "^2.17.1" }, "devDependencies": { "@d3fc/d3fc-discontinuous-scale": "^4.1.1", "@docusaurus/module-type-aliases": "^3.5.2", "@docusaurus/tsconfig": "^3.5.2", "@docusaurus/types": "^3.5.2", "@heroicons/react": "^2.2.0", "@types/react": "^18.0.0", "autoprefixer": "^10.4.20", "d3-array": "^2.4.0", "d3-scale": "^3.2.1", "d3-time": "^1.1.0", "find-cache-dir": "5.0.0", "mdast-util-from-markdown": "^2.0.1", "postcss": "^8.4.38", "prismjs": "^1.29.0", "tailwindcss": "^3.4.3", "typedoc": "^0.26.10", "typescript": "~5.2.2", "victory": "workspace:*" }, "browserslist": { "production": [ ">0.5%", "not dead", "not op_mini all" ], "development": [ "last 3 chrome version", "last 3 firefox version", "last 5 safari version" ] } } ================================================ FILE: website/sidebars.ts ================================================ import { SidebarsConfig } from "@docusaurus/plugin-content-docs"; const sidebars: SidebarsConfig = { sidebar: [{ type: "autogenerated", dirName: "." }], }; export default sidebars; ================================================ FILE: website/src/components/CalloutBanner.tsx ================================================ import React from "react"; import LearnMoreLink from "./LearnMoreLink"; const CalloutBanner = ({ fullWidth = false }) => { return (

Like this project? You'll love working with us.

Contact us to learn more about our full range of services and offerings.

); }; export default CalloutBanner; ================================================ FILE: website/src/components/LearnMoreLink.tsx ================================================ import React from "react"; import { ComponentProps } from "react"; import { FaArrowRight } from "react-icons/fa"; export default function LearnMoreLink( props: Omit, "href">, ) { return ( Learn More ); } ================================================ FILE: website/src/components/SidebarLeadBanner.tsx ================================================ import React from "react"; import LearnMoreLink from "./LearnMoreLink"; export default function SidebarLeadBanner() { return ( ); } ================================================ FILE: website/src/components/badges.tsx ================================================ /* eslint-disable react/no-multi-comp */ import React from "react"; export function Badges({ children }) { return
{children}
; } export function Badge({ children, className }) { return ( {children} ); } export function TypeBadge({ value }) { return ( type: {value} ); } export function OverriddenBadge() { return ( overridden ); } export function RequiredBadge() { return ( Required ); } export function DefaultsBadge({ value }) { return ( default: {value} ); } ================================================ FILE: website/src/components/button.tsx ================================================ import clsx from "clsx"; import React from "react"; type ButtonProps = { onClick: () => void; children: React.ReactNode; className?: string; ariaLabel?: string; disabled?: boolean; size?: "sm" | "md"; }; export const Button = ({ onClick, children, className = "", ariaLabel = "", disabled = false, size = "md", ...props }: ButtonProps) => { const baseClasses = "py-2 px-4 font-semibold rounded-md cursor-pointer text-sm border-2 border-solid border-button-border bg-button-bg text-button-fg hover:bg-button-bg-hover hover:text-button-fg-hover disabled:bg-grayscale-300 disabled:text-grayscale-400 disabled:cursor-not-allowed"; return ( ); }; ================================================ FILE: website/src/components/common-props.tsx ================================================ /* eslint-disable react/no-multi-comp */ import React from "react"; import { Badge, OverriddenBadge } from "./badges"; type Prop = { name: string; url?: string; default?: string; }; type PropsMeta = { name: string; url: string; props: Prop[]; }; function extend(meta: PropsMeta, props: PropsMeta): PropsMeta { return { ...props, props: [ ...meta.props.map((x) => ({ ...x, url: `${meta.url}#${x.name}` })), ...props.props, ].sort((a, b) => a.name.localeCompare(b.name)), }; } const VictoryAxisCommonProps: PropsMeta = { name: "VictoryAxisCommonProps", url: "/open-source/victory/docs/api/victory-axis-common-props", props: [ { name: "axisComponent" }, { name: "axisLabelComponent" }, { name: "axisValue" }, { name: "dependentAxis" }, { name: "disableInlineStyles" }, { name: "gridComponent" }, { name: "invertAxis" }, { name: "style" }, { name: "tickComponent" }, { name: "tickCount" }, { name: "tickFormat" }, { name: "tickLabelComponent" }, { name: "tickValues" }, ], }; const VictoryContainerProps: PropsMeta = { name: "VictoryContainerProps", url: "/open-source/victory/docs/api/victory-container-props", props: [ { name: "aria-describedby" }, { name: "aria-labelledby" }, { name: "children" }, { name: "className" }, { name: "containerId" }, { name: "containerRef" }, { name: "desc" }, { name: "events" }, { name: "height" }, { name: "name" }, { name: "origin" }, { name: "ouiaId" }, { name: "ouiaSafe" }, { name: "ouiaType" }, { name: "polar" }, { name: "portalComponent" }, { name: "portalZIndex" }, { name: "preserveAspectRatio" }, { name: "responsive" }, { name: "role" }, { name: "scale" }, { name: "style" }, { name: "tabIndex" }, { name: "theme" }, { name: "title" }, { name: "width" }, ], }; const VictoryLabelableProps: PropsMeta = { name: "VictoryLabelableProps", url: "/open-source/victory/docs/api/victory-labelable-props", props: [{ name: "labelComponent" }], }; const VictoryMultiLabelableProps: PropsMeta = extend(VictoryLabelableProps, { name: "VictoryMultiLabelableProps", url: "/open-source/victory/docs/api/victory-multi-labelable-props", props: [{ name: "labels" }], }); const VictorySingleLabelableProps: PropsMeta = extend(VictoryLabelableProps, { name: "VictorySingleLabelableProps", url: "/open-source/victory/docs/api/victory-single-labelable-props", props: [{ name: "label" }], }); const VictoryEventProps: PropsMeta = { name: "VictoryEventProps", url: "/open-source/victory/docs/api/victory-event-props", props: [{ name: "eventKey" }, { name: "events" }], }; const VictoryCommonThemeProps: PropsMeta = { name: "VictoryCommonThemeProps", url: "/open-source/victory/docs/api/victory-common-theme-props", props: [ { name: "animate" }, { name: "colorScale" }, { name: "containerComponent", default: "" }, { name: "disableInlineStyles" }, { name: "domainPadding" }, { name: "externalEventMutations" }, { name: "groupComponent" }, { name: "height" }, { name: "horizontal" }, { name: "maxDomain" }, { name: "minDomain" }, { name: "name" }, { name: "origin" }, { name: "padding" }, { name: "polar" }, { name: "range" }, { name: "scale" }, { name: "sharedEvents" }, { name: "singleQuadrantDomainPadding" }, { name: "standalone" }, { name: "width" }, ], }; const VictoryCommonProps: PropsMeta = extend(VictoryCommonThemeProps, { name: "VictoryCommonProps", url: "/open-source/victory/docs/api/victory-common-props", props: [{ name: "theme" }], }); const VictoryDatableProps: PropsMeta = { name: "VictoryDatableProps", url: "/open-source/victory/docs/api/victory-datatable-props", props: [ { name: "categories" }, { name: "data" }, { name: "dataComponent" }, { name: "domain" }, { name: "domainPadding" }, { name: "samples" }, { name: "sortKey" }, { name: "sortOrder" }, { name: "x" }, { name: "y" }, { name: "y0" }, ], }; const VictoryCommons = [ VictoryAxisCommonProps, VictoryContainerProps, VictoryDatableProps, VictoryLabelableProps, VictorySingleLabelableProps, VictoryMultiLabelableProps, VictoryCommonThemeProps, VictoryCommonProps, VictoryEventProps, ]; function PropertyListItem({ x, prop, overridden, notImplemented }) { if (notImplemented) { return ( <> {prop.name} not-implemented ); } if (overridden) { return ( <> {prop.name} ); } return ( {prop.name} ); } export function CommonProps({ interfaces, overrides, notImplemented }) { const result = VictoryCommons.filter((x) => (interfaces || []).includes(x.name), ); return (
{result.map((x) => (

{x.name}

    {x.props.map((prop) => (
  • ))}
))}
); } ================================================ FILE: website/src/components/link-button.tsx ================================================ import clsx from "clsx"; import React from "react"; interface ButtonProps { className?: string; children: React.ReactNode; link: string; screenReaderLabel?: string; } export const LinkButton = ({ children, link, className }: ButtonProps) => { const classes = clsx( "bg-button-bg text-button-fg border-button-border hover:text-button-fg-hover after:bg-button-bg-hover border-2 font-bold rounded-full text-lg z-0 transition-colors delay-75 w-fit overflow-hidden py-[14px] px-[23px] relative flex gap-2.5 justify-between leading-[21px] after:absolute after:w-[200%] after:h-full after:bottom-0 after:transform-gpu after:-skew-x-[50deg] after:-right-[250%] after:-z-10 after:transition-transform after:duration-200 hover:after:-translate-x-[100%] hover:after:[-webkit-transform:translate3d(-100%,0,0)_!important]", className, ); return ( {children} ); }; ================================================ FILE: website/src/components/slider.tsx ================================================ /* eslint no-magic-numbers: ["error", { "ignore": [0, 1, 100] }]*/ import React, { useLayoutEffect, useState, useRef, useCallback } from "react"; import clamp from "lodash/clamp"; const LIGHT_GREY = "hsl(355, 32%, 87%)"; const isTouchEvent = (event) => { return event.touches !== undefined; }; const Slider = ({ tooltipValues, color, value, maxValue, onChange }) => { const [dragging, setDragging] = useState(false); const [percentage, setPercentage] = useState(value / maxValue); const containerRef = useRef(null); const handleDrag = useCallback( (ev) => { if (dragging && containerRef.current) { const left = containerRef.current.getBoundingClientRect().left; const sliderWidth = containerRef.current.clientWidth; const location = isTouchEvent(ev) ? ev.touches[0].clientX - left : ev.clientX - left; const newPercentage = clamp(location / sliderWidth, 0, 1); window.requestAnimationFrame(() => { setPercentage(newPercentage); onChange(percentage * maxValue); }); } }, [dragging, maxValue, onChange, percentage], ); const handleDragDone = useCallback(() => { setDragging(false); onChange(percentage * maxValue); }, [maxValue, onChange, percentage]); const handleDragStart = (ev) => { if (!containerRef.current) { return; } const left = containerRef.current.getBoundingClientRect().left; const sliderWidth = containerRef.current.clientWidth; const location = isTouchEvent(ev) ? ev.touches[0].clientX - left : ev.clientX - left; const newPercentage = location / sliderWidth; setPercentage(newPercentage); setDragging(true); }; const getTooltipText = () => { const length = tooltipValues.length; const index = Math.round((length - 1) * percentage); return tooltipValues[index]; }; useLayoutEffect(() => { window.addEventListener("mousemove", handleDrag); window.addEventListener("touchmove", handleDrag); window.addEventListener("touchend", handleDragDone); window.addEventListener("mouseup", handleDragDone); return () => { window.removeEventListener("mousemove", handleDrag); window.removeEventListener("touchmove", handleDrag); window.removeEventListener("touchend", handleDragDone); window.removeEventListener("mouseup", handleDragDone); }; }, [handleDrag, handleDragDone]); return (
{tooltipValues.map((tooltip, index) => { const tooltipPercentage = index / (tooltipValues.length - 1); return (
); })}
{getTooltipText()}
); }; export default Slider; ================================================ FILE: website/src/css/custom.css ================================================ /** * Any CSS included here will be global. The classic template * bundles Infima by default. Infima is a CSS framework designed to * work well for content-centric websites. */ @tailwind base; @tailwind components; @tailwind utilities; body { scroll-behavior: smooth; text-rendering: optimizeSpeed; } @font-face { font-family: "Inter"; src: url("/font/InterRegular.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; } @font-face { font-family: "Inter"; src: url("/font/InterMedium.woff2") format("woff2"); font-weight: 500; font-style: normal; font-display: swap; } @font-face { font-family: "Inter"; src: url("/font/InterBold.woff2") format("woff2"); font-weight: 700; font-style: normal; font-display: swap; } .hero-pattern { background-image: url("/img/hero-background.svg"); } :root { --ifm-color-primary: #ff684f; --ifm-color-primary-dark: #ff4b2e; --ifm-color-primary-darker: #ff3d1d; --ifm-color-primary-darkest: #ea2100; --ifm-color-primary-light: #ff8570; --ifm-color-primary-lighter: #ff9381; --ifm-color-primary-lightest: #ffbeb3; --ifm-code-font-size: 95%; --ifm-list-item-margin: 0; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); --ifm-navbar-background-color: #242526; --ifm-footer-background-color: #242526; --ifm-navbar-link-color: #ffffff; --ifm-footer-padding-vertical: 1rem; } [data-theme="dark"] { --ifm-color-primary: #ff684f; --ifm-color-primary-dark: #ff4b2e; --ifm-color-primary-darker: #ff3d1d; --ifm-color-primary-darkest: #ea2100; --ifm-color-primary-light: #ff8570; --ifm-color-primary-lighter: #ff9381; --ifm-color-primary-lightest: #ffbeb3; --ifm-list-item-margin: 0; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); --ifm-navbar-background-color: #242526; --ifm-footer-background-color: #242526; } /* Nav */ .header-github-link::before { content: ""; width: 24px; height: 24px; display: flex; background: url("data:image/svg+xml;charset=utf-8,%3Csvg fill='white' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat; } .navbar__inner button svg { color: white; } /* Override right margin on logo */ .navbar__logo { flex: 0 0 auto; height: 2rem; margin-right: 2rem; } /* Custom Footer */ .footer__bottom.text--center { display: flex; justify-content: space-between; align-items: center; } .footer__bottom.text--center a { opacity: 1 !important; } .footer__copyright { color: white; } @layer base { /* Needed for theme builder since we're disabling Tailwind's reset for docs */ .theme-builder * { border-width: 0; border-style: solid; h1, h2, h3, h4, h5, h6 { font-family: Inter, Helvetica, Arial, sans-serif; } } } ================================================ FILE: website/src/hooks/useClickOutside.tsx ================================================ import { useEffect, useLayoutEffect, useRef } from "react"; export function useClickOutside(cb: (e: Event) => void) { const ref = useRef(null); const refCb = useRef(cb); useLayoutEffect(() => { refCb.current = cb; }); useEffect(() => { const handler = (e: Event) => { const element = ref.current; if (element && !element.contains(e.target as Node)) { refCb.current(e); } }; document.addEventListener("mousedown", handler); document.addEventListener("touchstart", handler); return () => { document.removeEventListener("mousedown", handler); document.removeEventListener("touchstart", handler); }; }, []); return ref; } ================================================ FILE: website/src/hooks/useLocalStorage.tsx ================================================ import { useEffect, useState } from "react"; export const useLocalStorage = (key: string, initialValue: any) => { const [storedValue, setStoredValue] = useState(() => { if (typeof window === "undefined") return initialValue; try { const item = localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch { return initialValue; } }); useEffect(() => { if (typeof window !== "undefined") { try { localStorage.setItem(key, JSON.stringify(storedValue)); } catch (error) { // eslint-disable-next-line no-console console.error("Error saving to localStorage", error); } } }, [storedValue, key]); return [storedValue, setStoredValue]; }; ================================================ FILE: website/src/pages/_components/data/downloads.js ================================================ const data = { data: [ { day: "2015-11-28", downloads: 3, }, { day: "2015-11-29", downloads: 4, }, { day: "2015-11-30", downloads: 5, }, { day: "2015-12-01", downloads: 4, }, { day: "2015-12-02", downloads: 2, }, { day: "2015-12-03", downloads: 6, }, { day: "2015-12-04", downloads: 37, }, { day: "2015-12-05", downloads: 14, }, { day: "2015-12-06", downloads: 2, }, { day: "2015-12-07", downloads: 14, }, { day: "2015-12-08", downloads: 4, }, { day: "2015-12-09", downloads: 13, }, { day: "2015-12-10", downloads: 18, }, { day: "2015-12-11", downloads: 10, }, { day: "2015-12-12", downloads: 6, }, { day: "2015-12-13", downloads: 7, }, { day: "2015-12-14", downloads: 7, }, { day: "2015-12-15", downloads: 14, }, { day: "2015-12-16", downloads: 11, }, { day: "2015-12-17", downloads: 12, }, { day: "2015-12-18", downloads: 75, }, { day: "2015-12-19", downloads: 14, }, { day: "2015-12-20", downloads: 13, }, { day: "2015-12-21", downloads: 20, }, { day: "2015-12-22", downloads: 13, }, { day: "2015-12-23", downloads: 9, }, { day: "2015-12-24", downloads: 4, }, { day: "2015-12-25", downloads: 3, }, { day: "2015-12-26", downloads: 14, }, { day: "2015-12-27", downloads: 1, }, { day: "2015-12-28", downloads: 11, }, { day: "2015-12-29", downloads: 36, }, { day: "2015-12-30", downloads: 95, }, { day: "2015-12-31", downloads: 12, }, { day: "2016-01-01", downloads: 12, }, { day: "2016-01-02", downloads: 15, }, { day: "2016-01-03", downloads: 14, }, { day: "2016-01-04", downloads: 35, }, { day: "2016-01-05", downloads: 19, }, { day: "2016-01-06", downloads: 33, }, { day: "2016-01-07", downloads: 47, }, { day: "2016-01-08", downloads: 29, }, { day: "2016-01-09", downloads: 33, }, { day: "2016-01-10", downloads: 19, }, { day: "2016-01-11", downloads: 48, }, { day: "2016-01-12", downloads: 38, }, { day: "2016-01-13", downloads: 38, }, { day: "2016-01-14", downloads: 21, }, { day: "2016-01-15", downloads: 37, }, { day: "2016-01-16", downloads: 45, }, { day: "2016-01-17", downloads: 33, }, { day: "2016-01-18", downloads: 14, }, { day: "2016-01-19", downloads: 33, }, { day: "2016-01-20", downloads: 48, }, { day: "2016-01-21", downloads: 38, }, { day: "2016-01-22", downloads: 36, }, { day: "2016-01-23", downloads: 16, }, { day: "2016-01-24", downloads: 48, }, { day: "2016-01-25", downloads: 43, }, { day: "2016-01-26", downloads: 61, }, { day: "2016-01-27", downloads: 106, }, { day: "2016-01-28", downloads: 73, }, { day: "2016-01-29", downloads: 43, }, { day: "2016-01-30", downloads: 34, }, { day: "2016-01-31", downloads: 25, }, { day: "2016-02-01", downloads: 124, }, { day: "2016-02-02", downloads: 78, }, { day: "2016-02-03", downloads: 64, }, { day: "2016-02-04", downloads: 47, }, { day: "2016-02-05", downloads: 57, }, { day: "2016-02-06", downloads: 23, }, { day: "2016-02-07", downloads: 27, }, { day: "2016-02-08", downloads: 54, }, { day: "2016-02-09", downloads: 68, }, { day: "2016-02-10", downloads: 94, }, { day: "2016-02-11", downloads: 54, }, { day: "2016-02-12", downloads: 72, }, { day: "2016-02-13", downloads: 4, }, { day: "2016-02-14", downloads: 9, }, { day: "2016-02-15", downloads: 53, }, { day: "2016-02-16", downloads: 62, }, { day: "2016-02-17", downloads: 74, }, { day: "2016-02-18", downloads: 103, }, { day: "2016-02-19", downloads: 70, }, { day: "2016-02-20", downloads: 27, }, { day: "2016-02-21", downloads: 41, }, { day: "2016-02-22", downloads: 93, }, { day: "2016-02-23", downloads: 124, }, { day: "2016-02-24", downloads: 108, }, { day: "2016-02-25", downloads: 72, }, { day: "2016-02-26", downloads: 95, }, { day: "2016-02-27", downloads: 36, }, { day: "2016-02-28", downloads: 9, }, { day: "2016-02-29", downloads: 118, }, { day: "2016-03-01", downloads: 98, }, { day: "2016-03-02", downloads: 112, }, { day: "2016-03-03", downloads: 133, }, { day: "2016-03-04", downloads: 105, }, { day: "2016-03-05", downloads: 27, }, { day: "2016-03-06", downloads: 34, }, { day: "2016-03-07", downloads: 176, }, { day: "2016-03-08", downloads: 134, }, { day: "2016-03-09", downloads: 117, }, { day: "2016-03-10", downloads: 89, }, { day: "2016-03-11", downloads: 65, }, { day: "2016-03-12", downloads: 8, }, { day: "2016-03-13", downloads: 10, }, { day: "2016-03-14", downloads: 63, }, { day: "2016-03-15", downloads: 34, }, { day: "2016-03-16", downloads: 66, }, { day: "2016-03-17", downloads: 146, }, { day: "2016-03-18", downloads: 63, }, { day: "2016-03-19", downloads: 30, }, { day: "2016-03-20", downloads: 19, }, { day: "2016-03-21", downloads: 56, }, { day: "2016-03-22", downloads: 49, }, { day: "2016-03-23", downloads: 78, }, { day: "2016-03-24", downloads: 61, }, { day: "2016-03-25", downloads: 21, }, { day: "2016-03-26", downloads: 12, }, { day: "2016-03-27", downloads: 24, }, { day: "2016-03-28", downloads: 68, }, { day: "2016-03-29", downloads: 92, }, { day: "2016-03-30", downloads: 77, }, { day: "2016-03-31", downloads: 46, }, { day: "2016-04-01", downloads: 47, }, { day: "2016-04-02", downloads: 15, }, { day: "2016-04-03", downloads: 6, }, { day: "2016-04-04", downloads: 48, }, { day: "2016-04-05", downloads: 29, }, { day: "2016-04-06", downloads: 56, }, { day: "2016-04-07", downloads: 47, }, { day: "2016-04-08", downloads: 54, }, { day: "2016-04-09", downloads: 8, }, { day: "2016-04-10", downloads: 28, }, { day: "2016-04-11", downloads: 88, }, { day: "2016-04-12", downloads: 59, }, { day: "2016-04-13", downloads: 73, }, { day: "2016-04-14", downloads: 141, }, { day: "2016-04-15", downloads: 222, }, { day: "2016-04-16", downloads: 20, }, { day: "2016-04-17", downloads: 11, }, { day: "2016-04-18", downloads: 126, }, { day: "2016-04-19", downloads: 160, }, { day: "2016-04-20", downloads: 110, }, { day: "2016-04-21", downloads: 80, }, { day: "2016-04-22", downloads: 54, }, { day: "2016-04-23", downloads: 39, }, { day: "2016-04-24", downloads: 3, }, { day: "2016-04-25", downloads: 59, }, { day: "2016-04-26", downloads: 94, }, { day: "2016-04-27", downloads: 61, }, { day: "2016-04-28", downloads: 96, }, { day: "2016-04-29", downloads: 64, }, { day: "2016-04-30", downloads: 40, }, { day: "2016-05-01", downloads: 9, }, { day: "2016-05-02", downloads: 67, }, { day: "2016-05-03", downloads: 145, }, { day: "2016-05-04", downloads: 155, }, { day: "2016-05-05", downloads: 102, }, { day: "2016-05-06", downloads: 56, }, { day: "2016-05-07", downloads: 34, }, { day: "2016-05-08", downloads: 10, }, { day: "2016-05-09", downloads: 65, }, { day: "2016-05-10", downloads: 79, }, { day: "2016-05-11", downloads: 79, }, { day: "2016-05-12", downloads: 82, }, { day: "2016-05-13", downloads: 231, }, { day: "2016-05-14", downloads: 47, }, { day: "2016-05-15", downloads: 40, }, { day: "2016-05-16", downloads: 116, }, { day: "2016-05-17", downloads: 180, }, { day: "2016-05-18", downloads: 127, }, { day: "2016-05-19", downloads: 145, }, { day: "2016-05-20", downloads: 150, }, { day: "2016-05-21", downloads: 44, }, { day: "2016-05-22", downloads: 20, }, { day: "2016-05-23", downloads: 90, }, { day: "2016-05-24", downloads: 109, }, { day: "2016-05-25", downloads: 105, }, { day: "2016-05-26", downloads: 123, }, { day: "2016-05-27", downloads: 161, }, { day: "2016-05-28", downloads: 33, }, { day: "2016-05-29", downloads: 20, }, { day: "2016-05-30", downloads: 133, }, { day: "2016-05-31", downloads: 148, }, { day: "2016-06-01", downloads: 117, }, { day: "2016-06-02", downloads: 347, }, { day: "2016-06-03", downloads: 175, }, { day: "2016-06-04", downloads: 44, }, { day: "2016-06-05", downloads: 34, }, { day: "2016-06-06", downloads: 188, }, { day: "2016-06-07", downloads: 240, }, { day: "2016-06-08", downloads: 252, }, { day: "2016-06-09", downloads: 199, }, { day: "2016-06-10", downloads: 138, }, { day: "2016-06-11", downloads: 85, }, { day: "2016-06-12", downloads: 106, }, { day: "2016-06-13", downloads: 222, }, { day: "2016-06-14", downloads: 187, }, { day: "2016-06-15", downloads: 234, }, { day: "2016-06-16", downloads: 212, }, { day: "2016-06-17", downloads: 314, }, { day: "2016-06-18", downloads: 76, }, { day: "2016-06-19", downloads: 13, }, { day: "2016-06-20", downloads: 171, }, { day: "2016-06-21", downloads: 213, }, { day: "2016-06-22", downloads: 360, }, { day: "2016-06-23", downloads: 185, }, { day: "2016-06-24", downloads: 144, }, { day: "2016-06-25", downloads: 22, }, { day: "2016-06-26", downloads: 25, }, { day: "2016-06-27", downloads: 207, }, { day: "2016-06-28", downloads: 575, }, { day: "2016-06-29", downloads: 362, }, { day: "2016-06-30", downloads: 358, }, { day: "2016-07-01", downloads: 397, }, { day: "2016-07-02", downloads: 33, }, { day: "2016-07-03", downloads: 31, }, { day: "2016-07-04", downloads: 96, }, { day: "2016-07-05", downloads: 151, }, { day: "2016-07-06", downloads: 315, }, { day: "2016-07-07", downloads: 270, }, { day: "2016-07-08", downloads: 213, }, { day: "2016-07-09", downloads: 40, }, { day: "2016-07-10", downloads: 24, }, { day: "2016-07-11", downloads: 224, }, { day: "2016-07-12", downloads: 199, }, { day: "2016-07-13", downloads: 262, }, { day: "2016-07-14", downloads: 287, }, { day: "2016-07-15", downloads: 163, }, { day: "2016-07-16", downloads: 47, }, { day: "2016-07-17", downloads: 9, }, { day: "2016-07-18", downloads: 173, }, { day: "2016-07-19", downloads: 177, }, { day: "2016-07-20", downloads: 321, }, { day: "2016-07-21", downloads: 265, }, { day: "2016-07-22", downloads: 104, }, { day: "2016-07-23", downloads: 21, }, { day: "2016-07-24", downloads: 26, }, { day: "2016-07-25", downloads: 127, }, { day: "2016-07-26", downloads: 172, }, { day: "2016-07-27", downloads: 239, }, { day: "2016-07-28", downloads: 196, }, { day: "2016-07-29", downloads: 318, }, { day: "2016-07-30", downloads: 25, }, { day: "2016-07-31", downloads: 16, }, { day: "2016-08-01", downloads: 383, }, { day: "2016-08-02", downloads: 194, }, { day: "2016-08-03", downloads: 418, }, { day: "2016-08-04", downloads: 438, }, { day: "2016-08-05", downloads: 385, }, { day: "2016-08-06", downloads: 46, }, { day: "2016-08-07", downloads: 29, }, { day: "2016-08-08", downloads: 225, }, { day: "2016-08-09", downloads: 241, }, { day: "2016-08-10", downloads: 179, }, { day: "2016-08-11", downloads: 146, }, { day: "2016-08-12", downloads: 193, }, { day: "2016-08-13", downloads: 62, }, { day: "2016-08-14", downloads: 24, }, { day: "2016-08-15", downloads: 217, }, { day: "2016-08-16", downloads: 327, }, { day: "2016-08-17", downloads: 317, }, { day: "2016-08-18", downloads: 197, }, { day: "2016-08-19", downloads: 420, }, { day: "2016-08-20", downloads: 47, }, { day: "2016-08-21", downloads: 25, }, { day: "2016-08-22", downloads: 260, }, { day: "2016-08-23", downloads: 321, }, { day: "2016-08-24", downloads: 223, }, { day: "2016-08-25", downloads: 237, }, { day: "2016-08-26", downloads: 302, }, { day: "2016-08-27", downloads: 33, }, { day: "2016-08-28", downloads: 57, }, { day: "2016-08-29", downloads: 294, }, { day: "2016-08-30", downloads: 324, }, { day: "2016-08-31", downloads: 283, }, { day: "2016-09-01", downloads: 203, }, { day: "2016-09-02", downloads: 237, }, { day: "2016-09-03", downloads: 51, }, { day: "2016-09-04", downloads: 26, }, { day: "2016-09-05", downloads: 154, }, { day: "2016-09-06", downloads: 230, }, { day: "2016-09-07", downloads: 315, }, { day: "2016-09-08", downloads: 248, }, { day: "2016-09-09", downloads: 394, }, { day: "2016-09-10", downloads: 35, }, { day: "2016-09-11", downloads: 48, }, { day: "2016-09-12", downloads: 188, }, { day: "2016-09-13", downloads: 303, }, { day: "2016-09-14", downloads: 270, }, { day: "2016-09-15", downloads: 555, }, { day: "2016-09-16", downloads: 256, }, { day: "2016-09-17", downloads: 43, }, { day: "2016-09-18", downloads: 38, }, { day: "2016-09-19", downloads: 215, }, { day: "2016-09-20", downloads: 235, }, { day: "2016-09-21", downloads: 298, }, { day: "2016-09-22", downloads: 277, }, { day: "2016-09-23", downloads: 306, }, { day: "2016-09-24", downloads: 31, }, { day: "2016-09-25", downloads: 32, }, { day: "2016-09-26", downloads: 225, }, { day: "2016-09-27", downloads: 321, }, { day: "2016-09-28", downloads: 275, }, { day: "2016-09-29", downloads: 411, }, { day: "2016-09-30", downloads: 641, }, { day: "2016-10-01", downloads: 48, }, { day: "2016-10-02", downloads: 36, }, { day: "2016-10-03", downloads: 252, }, { day: "2016-10-04", downloads: 349, }, { day: "2016-10-05", downloads: 303, }, { day: "2016-10-06", downloads: 415, }, { day: "2016-10-07", downloads: 293, }, { day: "2016-10-08", downloads: 47, }, { day: "2016-10-09", downloads: 48, }, { day: "2016-10-10", downloads: 232, }, { day: "2016-10-11", downloads: 263, }, { day: "2016-10-12", downloads: 331, }, { day: "2016-10-13", downloads: 379, }, { day: "2016-10-14", downloads: 654, }, { day: "2016-10-15", downloads: 89, }, { day: "2016-10-16", downloads: 57, }, { day: "2016-10-17", downloads: 581, }, { day: "2016-10-18", downloads: 379, }, { day: "2016-10-19", downloads: 340, }, { day: "2016-10-20", downloads: 342, }, { day: "2016-10-21", downloads: 431, }, { day: "2016-10-22", downloads: 94, }, { day: "2016-10-23", downloads: 100, }, { day: "2016-10-24", downloads: 444, }, { day: "2016-10-25", downloads: 523, }, { day: "2016-10-26", downloads: 635, }, { day: "2016-10-27", downloads: 487, }, { day: "2016-10-28", downloads: 669, }, { day: "2016-10-29", downloads: 117, }, { day: "2016-10-30", downloads: 123, }, { day: "2016-10-31", downloads: 643, }, { day: "2016-11-01", downloads: 578, }, { day: "2016-11-02", downloads: 504, }, { day: "2016-11-03", downloads: 431, }, { day: "2016-11-04", downloads: 389, }, { day: "2016-11-05", downloads: 92, }, { day: "2016-11-06", downloads: 70, }, { day: "2016-11-07", downloads: 283, }, { day: "2016-11-08", downloads: 408, }, { day: "2016-11-09", downloads: 720, }, { day: "2016-11-10", downloads: 712, }, { day: "2016-11-11", downloads: 701, }, { day: "2016-11-12", downloads: 190, }, { day: "2016-11-13", downloads: 135, }, { day: "2016-11-14", downloads: 521, }, { day: "2016-11-15", downloads: 562, }, { day: "2016-11-16", downloads: 491, }, { day: "2016-11-17", downloads: 619, }, { day: "2016-11-18", downloads: 443, }, { day: "2016-11-19", downloads: 168, }, { day: "2016-11-20", downloads: 88, }, { day: "2016-11-21", downloads: 388, }, { day: "2016-11-22", downloads: 542, }, { day: "2016-11-23", downloads: 504, }, { day: "2016-11-24", downloads: 275, }, { day: "2016-11-25", downloads: 197, }, { day: "2016-11-26", downloads: 55, }, { day: "2016-11-27", downloads: 50, }, { day: "2016-11-28", downloads: 493, }, { day: "2016-11-29", downloads: 642, }, { day: "2016-11-30", downloads: 498, }, { day: "2016-12-01", downloads: 472, }, { day: "2016-12-02", downloads: 556, }, { day: "2016-12-03", downloads: 265, }, { day: "2016-12-04", downloads: 117, }, { day: "2016-12-05", downloads: 527, }, { day: "2016-12-06", downloads: 667, }, { day: "2016-12-07", downloads: 601, }, { day: "2016-12-08", downloads: 629, }, { day: "2016-12-09", downloads: 689, }, { day: "2016-12-10", downloads: 139, }, { day: "2016-12-11", downloads: 80, }, { day: "2016-12-12", downloads: 746, }, { day: "2016-12-13", downloads: 843, }, { day: "2016-12-14", downloads: 1045, }, { day: "2016-12-15", downloads: 681, }, { day: "2016-12-16", downloads: 671, }, { day: "2016-12-17", downloads: 167, }, { day: "2016-12-18", downloads: 108, }, { day: "2016-12-19", downloads: 593, }, { day: "2016-12-20", downloads: 697, }, { day: "2016-12-21", downloads: 641, }, { day: "2016-12-22", downloads: 645, }, { day: "2016-12-23", downloads: 341, }, { day: "2016-12-24", downloads: 77, }, { day: "2016-12-25", downloads: 52, }, { day: "2016-12-26", downloads: 151, }, { day: "2016-12-27", downloads: 287, }, { day: "2016-12-28", downloads: 294, }, { day: "2016-12-29", downloads: 297, }, { day: "2016-12-30", downloads: 163, }, { day: "2016-12-31", downloads: 39, }, { day: "2017-01-01", downloads: 70, }, { day: "2017-01-02", downloads: 178, }, { day: "2017-01-03", downloads: 475, }, { day: "2017-01-04", downloads: 1037, }, { day: "2017-01-05", downloads: 744, }, { day: "2017-01-06", downloads: 681, }, { day: "2017-01-07", downloads: 162, }, { day: "2017-01-08", downloads: 109, }, { day: "2017-01-09", downloads: 583, }, { day: "2017-01-10", downloads: 718, }, { day: "2017-01-11", downloads: 840, }, { day: "2017-01-12", downloads: 730, }, { day: "2017-01-13", downloads: 653, }, { day: "2017-01-14", downloads: 410, }, { day: "2017-01-15", downloads: 185, }, { day: "2017-01-16", downloads: 450, }, { day: "2017-01-17", downloads: 588, }, { day: "2017-01-18", downloads: 786, }, { day: "2017-01-19", downloads: 692, }, { day: "2017-01-20", downloads: 648, }, { day: "2017-01-21", downloads: 159, }, { day: "2017-01-22", downloads: 114, }, { day: "2017-01-23", downloads: 512, }, { day: "2017-01-24", downloads: 1197, }, { day: "2017-01-25", downloads: 731, }, { day: "2017-01-26", downloads: 770, }, { day: "2017-01-27", downloads: 679, }, { day: "2017-01-28", downloads: 146, }, { day: "2017-01-29", downloads: 155, }, { day: "2017-01-30", downloads: 592, }, { day: "2017-01-31", downloads: 1095, }, { day: "2017-02-01", downloads: 775, }, { day: "2017-02-02", downloads: 658, }, { day: "2017-02-03", downloads: 670, }, { day: "2017-02-04", downloads: 634, }, { day: "2017-02-05", downloads: 110, }, { day: "2017-02-06", downloads: 1141, }, { day: "2017-02-07", downloads: 698, }, { day: "2017-02-08", downloads: 833, }, { day: "2017-02-09", downloads: 830, }, { day: "2017-02-10", downloads: 1215, }, { day: "2017-02-11", downloads: 120, }, { day: "2017-02-12", downloads: 103, }, { day: "2017-02-13", downloads: 724, }, { day: "2017-02-14", downloads: 738, }, { day: "2017-02-15", downloads: 686, }, { day: "2017-02-16", downloads: 708, }, { day: "2017-02-17", downloads: 1054, }, { day: "2017-02-18", downloads: 154, }, { day: "2017-02-19", downloads: 108, }, { day: "2017-02-20", downloads: 540, }, { day: "2017-02-21", downloads: 782, }, { day: "2017-02-22", downloads: 899, }, { day: "2017-02-23", downloads: 928, }, { day: "2017-02-24", downloads: 925, }, { day: "2017-02-25", downloads: 155, }, { day: "2017-02-26", downloads: 163, }, { day: "2017-02-27", downloads: 1259, }, { day: "2017-02-28", downloads: 725, }, { day: "2017-03-01", downloads: 848, }, { day: "2017-03-02", downloads: 724, }, { day: "2017-03-03", downloads: 859, }, { day: "2017-03-04", downloads: 213, }, { day: "2017-03-05", downloads: 149, }, { day: "2017-03-06", downloads: 836, }, { day: "2017-03-07", downloads: 861, }, { day: "2017-03-08", downloads: 1001, }, { day: "2017-03-09", downloads: 1027, }, { day: "2017-03-10", downloads: 969, }, { day: "2017-03-11", downloads: 157, }, { day: "2017-03-12", downloads: 142, }, { day: "2017-03-13", downloads: 987, }, { day: "2017-03-14", downloads: 937, }, { day: "2017-03-15", downloads: 1350, }, { day: "2017-03-16", downloads: 1318, }, { day: "2017-03-17", downloads: 767, }, { day: "2017-03-18", downloads: 159, }, { day: "2017-03-19", downloads: 102, }, { day: "2017-03-20", downloads: 991, }, { day: "2017-03-21", downloads: 1489, }, { day: "2017-03-22", downloads: 1040, }, { day: "2017-03-23", downloads: 964, }, { day: "2017-03-24", downloads: 1036, }, { day: "2017-03-25", downloads: 138, }, { day: "2017-03-26", downloads: 167, }, { day: "2017-03-27", downloads: 1207, }, { day: "2017-03-28", downloads: 1466, }, { day: "2017-03-29", downloads: 1264, }, { day: "2017-03-30", downloads: 1037, }, { day: "2017-03-31", downloads: 1252, }, { day: "2017-04-01", downloads: 127, }, { day: "2017-04-02", downloads: 200, }, { day: "2017-04-03", downloads: 1140, }, { day: "2017-04-04", downloads: 1600, }, { day: "2017-04-05", downloads: 1399, }, { day: "2017-04-06", downloads: 1366, }, { day: "2017-04-07", downloads: 1307, }, { day: "2017-04-08", downloads: 263, }, { day: "2017-04-09", downloads: 153, }, { day: "2017-04-10", downloads: 1338, }, { day: "2017-04-11", downloads: 1406, }, { day: "2017-04-12", downloads: 1181, }, { day: "2017-04-13", downloads: 1252, }, { day: "2017-04-14", downloads: 843, }, { day: "2017-04-15", downloads: 172, }, { day: "2017-04-16", downloads: 121, }, { day: "2017-04-17", downloads: 836, }, { day: "2017-04-18", downloads: 1226, }, { day: "2017-04-19", downloads: 1317, }, { day: "2017-04-20", downloads: 1497, }, { day: "2017-04-21", downloads: 1208, }, { day: "2017-04-22", downloads: 162, }, { day: "2017-04-23", downloads: 126, }, { day: "2017-04-24", downloads: 1339, }, { day: "2017-04-25", downloads: 1510, }, { day: "2017-04-26", downloads: 1417, }, { day: "2017-04-27", downloads: 1323, }, { day: "2017-04-28", downloads: 1970, }, { day: "2017-04-29", downloads: 212, }, { day: "2017-04-30", downloads: 180, }, { day: "2017-05-01", downloads: 667, }, { day: "2017-05-02", downloads: 1698, }, { day: "2017-05-03", downloads: 1126, }, { day: "2017-05-04", downloads: 1274, }, { day: "2017-05-05", downloads: 1142, }, { day: "2017-05-06", downloads: 179, }, { day: "2017-05-07", downloads: 147, }, { day: "2017-05-08", downloads: 1097, }, { day: "2017-05-09", downloads: 1207, }, { day: "2017-05-10", downloads: 1334, }, { day: "2017-05-11", downloads: 1183, }, { day: "2017-05-12", downloads: 1344, }, { day: "2017-05-13", downloads: 232, }, { day: "2017-05-14", downloads: 208, }, { day: "2017-05-15", downloads: 976, }, { day: "2017-05-16", downloads: 1078, }, { day: "2017-05-17", downloads: 1033, }, { day: "2017-05-18", downloads: 1257, }, { day: "2017-05-19", downloads: 1069, }, { day: "2017-05-20", downloads: 178, }, { day: "2017-05-21", downloads: 112, }, { day: "2017-05-22", downloads: 1036, }, { day: "2017-05-23", downloads: 1084, }, { day: "2017-05-24", downloads: 1646, }, { day: "2017-05-25", downloads: 899, }, { day: "2017-05-26", downloads: 1028, }, { day: "2017-05-27", downloads: 293, }, { day: "2017-05-28", downloads: 163, }, { day: "2017-05-29", downloads: 619, }, { day: "2017-05-30", downloads: 1051, }, { day: "2017-05-31", downloads: 1318, }, { day: "2017-06-01", downloads: 1195, }, { day: "2017-06-02", downloads: 862, }, { day: "2017-06-03", downloads: 903, }, { day: "2017-06-04", downloads: 151, }, { day: "2017-06-05", downloads: 872, }, { day: "2017-06-06", downloads: 1710, }, { day: "2017-06-07", downloads: 1504, }, { day: "2017-06-08", downloads: 1267, }, { day: "2017-06-09", downloads: 1169, }, { day: "2017-06-10", downloads: 375, }, { day: "2017-06-11", downloads: 190, }, { day: "2017-06-12", downloads: 1035, }, { day: "2017-06-13", downloads: 1254, }, { day: "2017-06-14", downloads: 1236, }, { day: "2017-06-15", downloads: 926, }, { day: "2017-06-16", downloads: 1010, }, { day: "2017-06-17", downloads: 217, }, { day: "2017-06-18", downloads: 125, }, { day: "2017-06-19", downloads: 1088, }, { day: "2017-06-20", downloads: 1174, }, { day: "2017-06-21", downloads: 1328, }, { day: "2017-06-22", downloads: 1264, }, { day: "2017-06-23", downloads: 1075, }, { day: "2017-06-24", downloads: 271, }, { day: "2017-06-25", downloads: 174, }, { day: "2017-06-26", downloads: 1251, }, { day: "2017-06-27", downloads: 1298, }, { day: "2017-06-28", downloads: 1331, }, { day: "2017-06-29", downloads: 1214, }, { day: "2017-06-30", downloads: 1689, }, { day: "2017-07-01", downloads: 212, }, { day: "2017-07-02", downloads: 170, }, { day: "2017-07-03", downloads: 819, }, { day: "2017-07-04", downloads: 702, }, { day: "2017-07-05", downloads: 1315, }, { day: "2017-07-06", downloads: 1531, }, { day: "2017-07-07", downloads: 1256, }, { day: "2017-07-08", downloads: 230, }, { day: "2017-07-09", downloads: 221, }, { day: "2017-07-10", downloads: 1287, }, { day: "2017-07-11", downloads: 1202, }, { day: "2017-07-12", downloads: 1295, }, { day: "2017-07-13", downloads: 2118, }, { day: "2017-07-14", downloads: 1447, }, { day: "2017-07-15", downloads: 348, }, { day: "2017-07-16", downloads: 273, }, { day: "2017-07-17", downloads: 1294, }, { day: "2017-07-18", downloads: 1396, }, { day: "2017-07-19", downloads: 1509, }, { day: "2017-07-20", downloads: 1332, }, { day: "2017-07-21", downloads: 1275, }, { day: "2017-07-22", downloads: 216, }, { day: "2017-07-23", downloads: 202, }, { day: "2017-07-24", downloads: 2073, }, { day: "2017-07-25", downloads: 1470, }, { day: "2017-07-26", downloads: 1429, }, { day: "2017-07-27", downloads: 1474, }, { day: "2017-07-28", downloads: 1265, }, { day: "2017-07-29", downloads: 196, }, { day: "2017-07-30", downloads: 194, }, { day: "2017-07-31", downloads: 1168, }, { day: "2017-08-01", downloads: 1413, }, { day: "2017-08-02", downloads: 1578, }, { day: "2017-08-03", downloads: 1459, }, { day: "2017-08-04", downloads: 1847, }, { day: "2017-08-05", downloads: 581, }, { day: "2017-08-06", downloads: 302, }, { day: "2017-08-07", downloads: 1464, }, { day: "2017-08-08", downloads: 2344, }, { day: "2017-08-09", downloads: 1595, }, { day: "2017-08-10", downloads: 1441, }, { day: "2017-08-11", downloads: 1446, }, { day: "2017-08-12", downloads: 323, }, { day: "2017-08-13", downloads: 200, }, { day: "2017-08-14", downloads: 1461, }, { day: "2017-08-15", downloads: 1558, }, { day: "2017-08-16", downloads: 1649, }, { day: "2017-08-17", downloads: 2539, }, { day: "2017-08-18", downloads: 1570, }, { day: "2017-08-19", downloads: 318, }, { day: "2017-08-20", downloads: 959, }, { day: "2017-08-21", downloads: 1389, }, { day: "2017-08-22", downloads: 1609, }, { day: "2017-08-23", downloads: 1585, }, { day: "2017-08-24", downloads: 1752, }, { day: "2017-08-25", downloads: 1426, }, { day: "2017-08-26", downloads: 292, }, { day: "2017-08-27", downloads: 232, }, { day: "2017-08-28", downloads: 1431, }, { day: "2017-08-29", downloads: 1767, }, { day: "2017-08-30", downloads: 1554, }, { day: "2017-08-31", downloads: 1702, }, { day: "2017-09-01", downloads: 1430, }, { day: "2017-09-02", downloads: 225, }, { day: "2017-09-03", downloads: 239, }, { day: "2017-09-04", downloads: 865, }, { day: "2017-09-05", downloads: 2541, }, { day: "2017-09-06", downloads: 1884, }, { day: "2017-09-07", downloads: 1536, }, { day: "2017-09-08", downloads: 1414, }, { day: "2017-09-09", downloads: 847, }, { day: "2017-09-10", downloads: 790, }, { day: "2017-09-11", downloads: 1669, }, { day: "2017-09-12", downloads: 2323, }, { day: "2017-09-13", downloads: 2511, }, { day: "2017-09-14", downloads: 2041, }, { day: "2017-09-15", downloads: 1738, }, { day: "2017-09-16", downloads: 408, }, { day: "2017-09-17", downloads: 303, }, { day: "2017-09-18", downloads: 1606, }, { day: "2017-09-19", downloads: 1852, }, { day: "2017-09-20", downloads: 1878, }, { day: "2017-09-21", downloads: 1826, }, { day: "2017-09-22", downloads: 1718, }, { day: "2017-09-23", downloads: 453, }, { day: "2017-09-24", downloads: 292, }, { day: "2017-09-25", downloads: 1834, }, { day: "2017-09-26", downloads: 1886, }, { day: "2017-09-27", downloads: 2829, }, { day: "2017-09-28", downloads: 1844, }, { day: "2017-09-29", downloads: 1828, }, { day: "2017-09-30", downloads: 1208, }, { day: "2017-10-01", downloads: 433, }, { day: "2017-10-02", downloads: 1781, }, { day: "2017-10-03", downloads: 1822, }, { day: "2017-10-04", downloads: 2050, }, { day: "2017-10-05", downloads: 2734, }, { day: "2017-10-06", downloads: 1741, }, { day: "2017-10-07", downloads: 469, }, { day: "2017-10-08", downloads: 571, }, { day: "2017-10-09", downloads: 1524, }, { day: "2017-10-10", downloads: 1785, }, { day: "2017-10-11", downloads: 1945, }, { day: "2017-10-12", downloads: 1611, }, { day: "2017-10-13", downloads: 1644, }, { day: "2017-10-14", downloads: 326, }, { day: "2017-10-15", downloads: 344, }, { day: "2017-10-16", downloads: 1639, }, { day: "2017-10-17", downloads: 1948, }, { day: "2017-10-18", downloads: 2073, }, { day: "2017-10-19", downloads: 1658, }, { day: "2017-10-20", downloads: 2751, }, { day: "2017-10-21", downloads: 1382, }, { day: "2017-10-22", downloads: 540, }, { day: "2017-10-23", downloads: 3575, }, { day: "2017-10-24", downloads: 4549, }, { day: "2017-10-25", downloads: 6032, }, { day: "2017-10-26", downloads: 4673, }, { day: "2017-10-27", downloads: 4633, }, { day: "2017-10-28", downloads: 881, }, { day: "2017-10-29", downloads: 601, }, { day: "2017-10-30", downloads: 3815, }, { day: "2017-10-31", downloads: 6860, }, { day: "2017-11-01", downloads: 7947, }, { day: "2017-11-02", downloads: 5549, }, { day: "2017-11-03", downloads: 5317, }, { day: "2017-11-04", downloads: 1301, }, { day: "2017-11-05", downloads: 872, }, { day: "2017-11-06", downloads: 5350, }, { day: "2017-11-07", downloads: 6889, }, { day: "2017-11-08", downloads: 7629, }, { day: "2017-11-09", downloads: 6780, }, { day: "2017-11-10", downloads: 5675, }, { day: "2017-11-11", downloads: 1371, }, { day: "2017-11-12", downloads: 707, }, { day: "2017-11-13", downloads: 3836, }, { day: "2017-11-14", downloads: 4810, }, { day: "2017-11-15", downloads: 5942, }, { day: "2017-11-16", downloads: 6317, }, { day: "2017-11-17", downloads: 4852, }, { day: "2017-11-18", downloads: 1204, }, { day: "2017-11-19", downloads: 546, }, { day: "2017-11-20", downloads: 4443, }, { day: "2017-11-21", downloads: 4247, }, { day: "2017-11-22", downloads: 5021, }, { day: "2017-11-23", downloads: 1875, }, { day: "2017-11-24", downloads: 1260, }, { day: "2017-11-25", downloads: 324, }, { day: "2017-11-26", downloads: 401, }, { day: "2017-11-27", downloads: 4427, }, { day: "2017-11-28", downloads: 6964, }, { day: "2017-11-29", downloads: 5950, }, { day: "2017-11-30", downloads: 6022, }, { day: "2017-12-01", downloads: 5609, }, { day: "2017-12-02", downloads: 1403, }, { day: "2017-12-03", downloads: 278, }, { day: "2017-12-04", downloads: 3381, }, { day: "2017-12-05", downloads: 5355, }, { day: "2017-12-06", downloads: 5407, }, { day: "2017-12-07", downloads: 7059, }, { day: "2017-12-08", downloads: 5473, }, { day: "2017-12-09", downloads: 1557, }, { day: "2017-12-10", downloads: 449, }, { day: "2017-12-11", downloads: 4219, }, { day: "2017-12-12", downloads: 7777, }, { day: "2017-12-13", downloads: 6017, }, { day: "2017-12-14", downloads: 4769, }, { day: "2017-12-15", downloads: 4091, }, { day: "2017-12-16", downloads: 1142, }, { day: "2017-12-17", downloads: 390, }, { day: "2017-12-18", downloads: 5491, }, { day: "2017-12-19", downloads: 4739, }, { day: "2017-12-20", downloads: 4151, }, { day: "2017-12-21", downloads: 4487, }, { day: "2017-12-22", downloads: 3059, }, { day: "2017-12-23", downloads: 414, }, { day: "2017-12-24", downloads: 344, }, { day: "2017-12-25", downloads: 215, }, { day: "2017-12-26", downloads: 620, }, { day: "2017-12-27", downloads: 1267, }, { day: "2017-12-28", downloads: 1134, }, { day: "2017-12-29", downloads: 884, }, { day: "2017-12-30", downloads: 266, }, { day: "2017-12-31", downloads: 187, }, { day: "2018-01-01", downloads: 211, }, { day: "2018-01-02", downloads: 1914, }, { day: "2018-01-03", downloads: 2492, }, { day: "2018-01-04", downloads: 2596, }, { day: "2018-01-05", downloads: 2442, }, { day: "2018-01-06", downloads: 699, }, { day: "2018-01-07", downloads: 306, }, { day: "2018-01-08", downloads: 2415, }, { day: "2018-01-09", downloads: 3682, }, { day: "2018-01-10", downloads: 2527, }, { day: "2018-01-11", downloads: 3055, }, { day: "2018-01-12", downloads: 2940, }, { day: "2018-01-13", downloads: 727, }, { day: "2018-01-14", downloads: 362, }, { day: "2018-01-15", downloads: 2051, }, { day: "2018-01-16", downloads: 2906, }, { day: "2018-01-17", downloads: 2859, }, { day: "2018-01-18", downloads: 3230, }, { day: "2018-01-19", downloads: 2559, }, { day: "2018-01-20", downloads: 754, }, { day: "2018-01-21", downloads: 685, }, { day: "2018-01-22", downloads: 3913, }, { day: "2018-01-23", downloads: 6981, }, { day: "2018-01-24", downloads: 6739, }, { day: "2018-01-25", downloads: 6194, }, { day: "2018-01-26", downloads: 5618, }, { day: "2018-01-27", downloads: 1357, }, { day: "2018-01-28", downloads: 725, }, { day: "2018-01-29", downloads: 3855, }, { day: "2018-01-30", downloads: 6767, }, { day: "2018-01-31", downloads: 7662, }, { day: "2018-02-01", downloads: 7036, }, { day: "2018-02-02", downloads: 6651, }, { day: "2018-02-03", downloads: 2133, }, { day: "2018-02-04", downloads: 785, }, { day: "2018-02-05", downloads: 7072, }, { day: "2018-02-06", downloads: 8457, }, { day: "2018-02-07", downloads: 6671, }, { day: "2018-02-08", downloads: 7440, }, { day: "2018-02-09", downloads: 4771, }, { day: "2018-02-10", downloads: 1108, }, { day: "2018-02-11", downloads: 560, }, { day: "2018-02-12", downloads: 5410, }, { day: "2018-02-13", downloads: 6070, }, { day: "2018-02-14", downloads: 5850, }, { day: "2018-02-15", downloads: 8512, }, { day: "2018-02-16", downloads: 5238, }, { day: "2018-02-17", downloads: 1684, }, { day: "2018-02-18", downloads: 761, }, { day: "2018-02-19", downloads: 2615, }, { day: "2018-02-20", downloads: 4697, }, { day: "2018-02-21", downloads: 6387, }, { day: "2018-02-22", downloads: 7082, }, { day: "2018-02-23", downloads: 5738, }, { day: "2018-02-24", downloads: 1420, }, { day: "2018-02-25", downloads: 816, }, { day: "2018-02-26", downloads: 5244, }, { day: "2018-02-27", downloads: 6326, }, { day: "2018-02-28", downloads: 6301, }, { day: "2018-03-01", downloads: 6620, }, { day: "2018-03-02", downloads: 6455, }, { day: "2018-03-03", downloads: 3534, }, { day: "2018-03-04", downloads: 1809, }, { day: "2018-03-05", downloads: 4757, }, { day: "2018-03-06", downloads: 6803, }, { day: "2018-03-07", downloads: 7059, }, { day: "2018-03-08", downloads: 6238, }, { day: "2018-03-09", downloads: 6367, }, { day: "2018-03-10", downloads: 2295, }, { day: "2018-03-11", downloads: 1590, }, { day: "2018-03-12", downloads: 6052, }, { day: "2018-03-13", downloads: 5488, }, { day: "2018-03-14", downloads: 7570, }, { day: "2018-03-15", downloads: 5426, }, { day: "2018-03-16", downloads: 7156, }, { day: "2018-03-17", downloads: 1493, }, { day: "2018-03-18", downloads: 1635, }, { day: "2018-03-19", downloads: 5708, }, { day: "2018-03-20", downloads: 8704, }, { day: "2018-03-21", downloads: 7645, }, { day: "2018-03-22", downloads: 8335, }, { day: "2018-03-23", downloads: 5155, }, { day: "2018-03-24", downloads: 1777, }, { day: "2018-03-25", downloads: 830, }, { day: "2018-03-26", downloads: 4850, }, { day: "2018-03-27", downloads: 8277, }, { day: "2018-03-28", downloads: 9242, }, { day: "2018-03-29", downloads: 8332, }, { day: "2018-03-30", downloads: 6233, }, { day: "2018-03-31", downloads: 1448, }, { day: "2018-04-01", downloads: 621, }, { day: "2018-04-02", downloads: 6229, }, { day: "2018-04-03", downloads: 8125, }, { day: "2018-04-04", downloads: 6551, }, { day: "2018-04-05", downloads: 6030, }, { day: "2018-04-06", downloads: 6782, }, { day: "2018-04-07", downloads: 1214, }, { day: "2018-04-08", downloads: 977, }, { day: "2018-04-09", downloads: 5590, }, { day: "2018-04-10", downloads: 7797, }, { day: "2018-04-11", downloads: 8438, }, { day: "2018-04-12", downloads: 8590, }, { day: "2018-04-13", downloads: 7080, }, { day: "2018-04-14", downloads: 1357, }, { day: "2018-04-15", downloads: 1039, }, { day: "2018-04-16", downloads: 5618, }, { day: "2018-04-17", downloads: 7038, }, { day: "2018-04-18", downloads: 8400, }, { day: "2018-04-19", downloads: 8058, }, { day: "2018-04-20", downloads: 7715, }, { day: "2018-04-21", downloads: 1704, }, { day: "2018-04-22", downloads: 1605, }, { day: "2018-04-23", downloads: 5176, }, { day: "2018-04-24", downloads: 6399, }, { day: "2018-04-25", downloads: 6148, }, { day: "2018-04-26", downloads: 6690, }, { day: "2018-04-27", downloads: 6312, }, { day: "2018-04-28", downloads: 1271, }, { day: "2018-04-29", downloads: 1201, }, { day: "2018-04-30", downloads: 5597, }, { day: "2018-05-01", downloads: 4773, }, { day: "2018-05-02", downloads: 6543, }, { day: "2018-05-03", downloads: 5549, }, { day: "2018-05-04", downloads: 5652, }, { day: "2018-05-05", downloads: 1275, }, { day: "2018-05-06", downloads: 1243, }, { day: "2018-05-07", downloads: 4962, }, { day: "2018-05-08", downloads: 4973, }, { day: "2018-05-09", downloads: 4023, }, { day: "2018-05-10", downloads: 3837, }, { day: "2018-05-11", downloads: 4218, }, { day: "2018-05-12", downloads: 601, }, { day: "2018-05-13", downloads: 481, }, { day: "2018-05-14", downloads: 4296, }, { day: "2018-05-15", downloads: 4020, }, { day: "2018-05-16", downloads: 5063, }, { day: "2018-05-17", downloads: 6956, }, { day: "2018-05-18", downloads: 8227, }, { day: "2018-05-19", downloads: 1879, }, { day: "2018-05-20", downloads: 512, }, { day: "2018-05-21", downloads: 4724, }, { day: "2018-05-22", downloads: 4200, }, { day: "2018-05-23", downloads: 4330, }, { day: "2018-05-24", downloads: 4172, }, { day: "2018-05-25", downloads: 2876, }, { day: "2018-05-26", downloads: 231, }, { day: "2018-05-27", downloads: 229, }, { day: "2018-05-28", downloads: 970, }, { day: "2018-05-29", downloads: 1834, }, { day: "2018-05-30", downloads: 3401, }, { day: "2018-05-31", downloads: 3790, }, { day: "2018-06-01", downloads: 3412, }, { day: "2018-06-02", downloads: 673, }, { day: "2018-06-03", downloads: 384, }, { day: "2018-06-04", downloads: 3588, }, { day: "2018-06-05", downloads: 3834, }, { day: "2018-06-06", downloads: 5116, }, { day: "2018-06-07", downloads: 4671, }, { day: "2018-06-08", downloads: 3407, }, { day: "2018-06-09", downloads: 568, }, { day: "2018-06-10", downloads: 496, }, { day: "2018-06-11", downloads: 3534, }, { day: "2018-06-12", downloads: 4143, }, { day: "2018-06-13", downloads: 3974, }, { day: "2018-06-14", downloads: 3946, }, { day: "2018-06-15", downloads: 4943, }, { day: "2018-06-16", downloads: 579, }, { day: "2018-06-17", downloads: 474, }, { day: "2018-06-18", downloads: 5291, }, { day: "2018-06-19", downloads: 7801, }, { day: "2018-06-20", downloads: 8307, }, { day: "2018-06-21", downloads: 7982, }, { day: "2018-06-22", downloads: 7059, }, { day: "2018-06-23", downloads: 1115, }, { day: "2018-06-24", downloads: 1464, }, { day: "2018-06-25", downloads: 5828, }, { day: "2018-06-26", downloads: 5157, }, { day: "2018-06-27", downloads: 4471, }, { day: "2018-06-28", downloads: 4240, }, { day: "2018-06-29", downloads: 4926, }, { day: "2018-06-30", downloads: 1173, }, { day: "2018-07-01", downloads: 748, }, { day: "2018-07-02", downloads: 4853, }, { day: "2018-07-03", downloads: 5808, }, { day: "2018-07-04", downloads: 2928, }, { day: "2018-07-05", downloads: 4435, }, { day: "2018-07-06", downloads: 6149, }, { day: "2018-07-07", downloads: 1528, }, { day: "2018-07-08", downloads: 598, }, { day: "2018-07-09", downloads: 4376, }, { day: "2018-07-10", downloads: 6020, }, { day: "2018-07-11", downloads: 5696, }, { day: "2018-07-12", downloads: 6153, }, { day: "2018-07-13", downloads: 5077, }, { day: "2018-07-14", downloads: 927, }, { day: "2018-07-15", downloads: 448, }, { day: "2018-07-16", downloads: 4003, }, { day: "2018-07-17", downloads: 5905, }, { day: "2018-07-18", downloads: 4686, }, { day: "2018-07-19", downloads: 4443, }, { day: "2018-07-20", downloads: 3998, }, { day: "2018-07-21", downloads: 764, }, { day: "2018-07-22", downloads: 734, }, { day: "2018-07-23", downloads: 4390, }, { day: "2018-07-24", downloads: 4102, }, { day: "2018-07-25", downloads: 4234, }, { day: "2018-07-26", downloads: 4099, }, { day: "2018-07-27", downloads: 3841, }, { day: "2018-07-28", downloads: 1403, }, { day: "2018-07-29", downloads: 638, }, { day: "2018-07-30", downloads: 4199, }, { day: "2018-07-31", downloads: 4642, }, { day: "2018-08-01", downloads: 4779, }, { day: "2018-08-02", downloads: 4474, }, { day: "2018-08-03", downloads: 3846, }, { day: "2018-08-04", downloads: 1026, }, { day: "2018-08-05", downloads: 773, }, { day: "2018-08-06", downloads: 4880, }, { day: "2018-08-07", downloads: 4831, }, { day: "2018-08-08", downloads: 5363, }, { day: "2018-08-09", downloads: 4851, }, { day: "2018-08-10", downloads: 4640, }, { day: "2018-08-11", downloads: 1083, }, { day: "2018-08-12", downloads: 637, }, { day: "2018-08-13", downloads: 4592, }, { day: "2018-08-14", downloads: 4028, }, { day: "2018-08-15", downloads: 5630, }, { day: "2018-08-16", downloads: 4496, }, { day: "2018-08-17", downloads: 4107, }, { day: "2018-08-18", downloads: 856, }, { day: "2018-08-19", downloads: 646, }, { day: "2018-08-20", downloads: 3941, }, { day: "2018-08-21", downloads: 5415, }, { day: "2018-08-22", downloads: 4619, }, { day: "2018-08-23", downloads: 5014, }, { day: "2018-08-24", downloads: 4721, }, { day: "2018-08-25", downloads: 1040, }, { day: "2018-08-26", downloads: 709, }, { day: "2018-08-27", downloads: 4289, }, { day: "2018-08-28", downloads: 4826, }, { day: "2018-08-29", downloads: 4676, }, { day: "2018-08-30", downloads: 5856, }, { day: "2018-08-31", downloads: 4563, }, { day: "2018-09-01", downloads: 642, }, { day: "2018-09-02", downloads: 782, }, { day: "2018-09-03", downloads: 2812, }, { day: "2018-09-04", downloads: 4800, }, { day: "2018-09-05", downloads: 5509, }, { day: "2018-09-06", downloads: 5294, }, { day: "2018-09-07", downloads: 5088, }, { day: "2018-09-08", downloads: 958, }, { day: "2018-09-09", downloads: 763, }, { day: "2018-09-10", downloads: 5647, }, { day: "2018-09-11", downloads: 5910, }, { day: "2018-09-12", downloads: 5907, }, { day: "2018-09-13", downloads: 5246, }, { day: "2018-09-14", downloads: 4963, }, { day: "2018-09-15", downloads: 846, }, { day: "2018-09-16", downloads: 722, }, { day: "2018-09-17", downloads: 5702, }, { day: "2018-09-18", downloads: 5422, }, { day: "2018-09-19", downloads: 5933, }, { day: "2018-09-20", downloads: 6065, }, { day: "2018-09-21", downloads: 5221, }, { day: "2018-09-22", downloads: 1208, }, { day: "2018-09-23", downloads: 801, }, { day: "2018-09-24", downloads: 6038, }, { day: "2018-09-25", downloads: 5705, }, { day: "2018-09-26", downloads: 7545, }, { day: "2018-09-27", downloads: 6520, }, { day: "2018-09-28", downloads: 5598, }, { day: "2018-09-29", downloads: 820, }, { day: "2018-09-30", downloads: 641, }, { day: "2018-10-01", downloads: 5468, }, { day: "2018-10-02", downloads: 5797, }, { day: "2018-10-03", downloads: 6581, }, { day: "2018-10-04", downloads: 6281, }, { day: "2018-10-05", downloads: 5540, }, { day: "2018-10-06", downloads: 785, }, { day: "2018-10-07", downloads: 649, }, { day: "2018-10-08", downloads: 4604, }, { day: "2018-10-09", downloads: 5848, }, { day: "2018-10-10", downloads: 5671, }, { day: "2018-10-11", downloads: 5870, }, { day: "2018-10-12", downloads: 5393, }, { day: "2018-10-13", downloads: 829, }, { day: "2018-10-14", downloads: 712, }, { day: "2018-10-15", downloads: 5959, }, { day: "2018-10-16", downloads: 7536, }, { day: "2018-10-17", downloads: 5818, }, { day: "2018-10-18", downloads: 5509, }, { day: "2018-10-19", downloads: 5347, }, { day: "2018-10-20", downloads: 845, }, { day: "2018-10-21", downloads: 787, }, { day: "2018-10-22", downloads: 4256, }, { day: "2018-10-23", downloads: 6048, }, { day: "2018-10-24", downloads: 6198, }, { day: "2018-10-25", downloads: 6999, }, { day: "2018-10-26", downloads: 6236, }, { day: "2018-10-27", downloads: 1408, }, { day: "2018-10-28", downloads: 909, }, { day: "2018-10-29", downloads: 4912, }, { day: "2018-10-30", downloads: 5795, }, { day: "2018-10-31", downloads: 5548, }, { day: "2018-11-01", downloads: 5143, }, { day: "2018-11-02", downloads: 5515, }, { day: "2018-11-03", downloads: 1330, }, { day: "2018-11-04", downloads: 1217, }, { day: "2018-11-05", downloads: 5680, }, { day: "2018-11-06", downloads: 6727, }, { day: "2018-11-07", downloads: 6633, }, { day: "2018-11-08", downloads: 6633, }, { day: "2018-11-09", downloads: 5769, }, { day: "2018-11-10", downloads: 1931, }, { day: "2018-11-11", downloads: 1633, }, { day: "2018-11-12", downloads: 5538, }, { day: "2018-11-13", downloads: 6509, }, { day: "2018-11-14", downloads: 6413, }, { day: "2018-11-15", downloads: 6038, }, { day: "2018-11-16", downloads: 5552, }, { day: "2018-11-17", downloads: 1222, }, { day: "2018-11-18", downloads: 1671, }, { day: "2018-11-19", downloads: 6140, }, { day: "2018-11-20", downloads: 6349, }, { day: "2018-11-21", downloads: 5798, }, { day: "2018-11-22", downloads: 3995, }, { day: "2018-11-23", downloads: 3441, }, { day: "2018-11-24", downloads: 833, }, { day: "2018-11-25", downloads: 896, }, { day: "2018-11-26", downloads: 5919, }, { day: "2018-11-27", downloads: 6722, }, { day: "2018-11-28", downloads: 6369, }, { day: "2018-11-29", downloads: 6378, }, { day: "2018-11-30", downloads: 5657, }, { day: "2018-12-01", downloads: 1195, }, { day: "2018-12-02", downloads: 743, }, { day: "2018-12-03", downloads: 5467, }, { day: "2018-12-04", downloads: 5831, }, { day: "2018-12-05", downloads: 6696, }, { day: "2018-12-06", downloads: 6108, }, { day: "2018-12-07", downloads: 5779, }, { day: "2018-12-08", downloads: 897, }, { day: "2018-12-09", downloads: 981, }, { day: "2018-12-10", downloads: 5981, }, { day: "2018-12-11", downloads: 6610, }, { day: "2018-12-12", downloads: 7089, }, { day: "2018-12-13", downloads: 6309, }, { day: "2018-12-14", downloads: 6113, }, { day: "2018-12-15", downloads: 1036, }, { day: "2018-12-16", downloads: 976, }, { day: "2018-12-17", downloads: 6081, }, { day: "2018-12-18", downloads: 6544, }, { day: "2018-12-19", downloads: 6581, }, { day: "2018-12-20", downloads: 5759, }, { day: "2018-12-21", downloads: 4976, }, { day: "2018-12-22", downloads: 768, }, { day: "2018-12-23", downloads: 357, }, { day: "2018-12-24", downloads: 1190, }, { day: "2018-12-25", downloads: 762, }, { day: "2018-12-26", downloads: 1757, }, { day: "2018-12-27", downloads: 2710, }, { day: "2018-12-28", downloads: 2331, }, { day: "2018-12-29", downloads: 574, }, { day: "2018-12-30", downloads: 496, }, { day: "2018-12-31", downloads: 1313, }, { day: "2019-01-01", downloads: 584, }, { day: "2019-01-02", downloads: 4097, }, { day: "2019-01-03", downloads: 5156, }, { day: "2019-01-04", downloads: 5287, }, { day: "2019-01-05", downloads: 1550, }, { day: "2019-01-06", downloads: 770, }, { day: "2019-01-07", downloads: 6083, }, { day: "2019-01-08", downloads: 7316, }, { day: "2019-01-09", downloads: 7361, }, { day: "2019-01-10", downloads: 6589, }, { day: "2019-01-11", downloads: 6065, }, { day: "2019-01-12", downloads: 1234, }, { day: "2019-01-13", downloads: 777, }, { day: "2019-01-14", downloads: 6487, }, { day: "2019-01-15", downloads: 6983, }, { day: "2019-01-16", downloads: 6797, }, { day: "2019-01-17", downloads: 6923, }, { day: "2019-01-18", downloads: 5947, }, { day: "2019-01-19", downloads: 879, }, { day: "2019-01-20", downloads: 860, }, { day: "2019-01-21", downloads: 4918, }, { day: "2019-01-22", downloads: 6807, }, { day: "2019-01-23", downloads: 6987, }, { day: "2019-01-24", downloads: 6859, }, { day: "2019-01-25", downloads: 6373, }, { day: "2019-01-26", downloads: 1241, }, { day: "2019-01-27", downloads: 889, }, { day: "2019-01-28", downloads: 6736, }, { day: "2019-01-29", downloads: 8081, }, { day: "2019-01-30", downloads: 7851, }, { day: "2019-01-31", downloads: 7930, }, { day: "2019-02-01", downloads: 6773, }, { day: "2019-02-02", downloads: 1330, }, { day: "2019-02-03", downloads: 1024, }, { day: "2019-02-04", downloads: 6589, }, { day: "2019-02-05", downloads: 7599, }, { day: "2019-02-06", downloads: 7433, }, { day: "2019-02-07", downloads: 7630, }, { day: "2019-02-08", downloads: 7204, }, { day: "2019-02-09", downloads: 1285, }, { day: "2019-02-10", downloads: 1124, }, { day: "2019-02-11", downloads: 7442, }, { day: "2019-02-12", downloads: 7828, }, { day: "2019-02-13", downloads: 7487, }, { day: "2019-02-14", downloads: 7507, }, { day: "2019-02-15", downloads: 6875, }, { day: "2019-02-16", downloads: 1107, }, { day: "2019-02-17", downloads: 946, }, { day: "2019-02-18", downloads: 5972, }, { day: "2019-02-19", downloads: 7389, }, { day: "2019-02-20", downloads: 8001, }, { day: "2019-02-21", downloads: 7766, }, { day: "2019-02-22", downloads: 6721, }, { day: "2019-02-23", downloads: 1654, }, { day: "2019-02-24", downloads: 1264, }, { day: "2019-02-25", downloads: 7087, }, { day: "2019-02-26", downloads: 8629, }, { day: "2019-02-27", downloads: 8059, }, { day: "2019-02-28", downloads: 8784, }, { day: "2019-03-01", downloads: 6905, }, { day: "2019-03-02", downloads: 1194, }, { day: "2019-03-03", downloads: 1099, }, { day: "2019-03-04", downloads: 6462, }, { day: "2019-03-05", downloads: 8892, }, { day: "2019-03-06", downloads: 7016, }, { day: "2019-03-07", downloads: 6823, }, { day: "2019-03-08", downloads: 6134, }, { day: "2019-03-09", downloads: 1166, }, { day: "2019-03-10", downloads: 982, }, { day: "2019-03-11", downloads: 7113, }, { day: "2019-03-12", downloads: 9345, }, { day: "2019-03-13", downloads: 9061, }, { day: "2019-03-14", downloads: 8566, }, { day: "2019-03-15", downloads: 6873, }, { day: "2019-03-16", downloads: 971, }, { day: "2019-03-17", downloads: 1145, }, { day: "2019-03-18", downloads: 4916, }, { day: "2019-03-19", downloads: 8099, }, { day: "2019-03-20", downloads: 8327, }, { day: "2019-03-21", downloads: 7607, }, { day: "2019-03-22", downloads: 7096, }, { day: "2019-03-23", downloads: 1247, }, { day: "2019-03-24", downloads: 1086, }, { day: "2019-03-25", downloads: 6877, }, { day: "2019-03-26", downloads: 8001, }, { day: "2019-03-27", downloads: 7844, }, { day: "2019-03-28", downloads: 7327, }, { day: "2019-03-29", downloads: 6237, }, { day: "2019-03-30", downloads: 1665, }, { day: "2019-03-31", downloads: 1098, }, { day: "2019-04-01", downloads: 6794, }, { day: "2019-04-02", downloads: 8300, }, { day: "2019-04-03", downloads: 8124, }, { day: "2019-04-04", downloads: 8019, }, { day: "2019-04-05", downloads: 6419, }, { day: "2019-04-06", downloads: 1270, }, { day: "2019-04-07", downloads: 1027, }, { day: "2019-04-08", downloads: 7117, }, { day: "2019-04-09", downloads: 7751, }, { day: "2019-04-10", downloads: 8231, }, { day: "2019-04-11", downloads: 8084, }, { day: "2019-04-12", downloads: 7041, }, { day: "2019-04-13", downloads: 1296, }, { day: "2019-04-14", downloads: 1315, }, { day: "2019-04-15", downloads: 6645, }, { day: "2019-04-16", downloads: 7763, }, { day: "2019-04-17", downloads: 8020, }, { day: "2019-04-18", downloads: 6994, }, { day: "2019-04-19", downloads: 5056, }, { day: "2019-04-20", downloads: 1202, }, { day: "2019-04-21", downloads: 981, }, { day: "2019-04-22", downloads: 5481, }, { day: "2019-04-23", downloads: 7451, }, { day: "2019-04-24", downloads: 7831, }, { day: "2019-04-25", downloads: 7199, }, { day: "2019-04-26", downloads: 6931, }, { day: "2019-04-27", downloads: 1095, }, { day: "2019-04-28", downloads: 1367, }, { day: "2019-04-29", downloads: 6950, }, { day: "2019-04-30", downloads: 7168, }, { day: "2019-05-01", downloads: 5958, }, { day: "2019-05-02", downloads: 7437, }, { day: "2019-05-03", downloads: 7813, }, { day: "2019-05-04", downloads: 1880, }, { day: "2019-05-05", downloads: 977, }, { day: "2019-05-06", downloads: 6387, }, { day: "2019-05-07", downloads: 7783, }, { day: "2019-05-08", downloads: 7592, }, { day: "2019-05-09", downloads: 7870, }, { day: "2019-05-10", downloads: 6915, }, { day: "2019-05-11", downloads: 1476, }, { day: "2019-05-12", downloads: 1068, }, { day: "2019-05-13", downloads: 8013, }, { day: "2019-05-14", downloads: 8696, }, { day: "2019-05-15", downloads: 8476, }, { day: "2019-05-16", downloads: 9274, }, { day: "2019-05-17", downloads: 6872, }, { day: "2019-05-18", downloads: 1073, }, { day: "2019-05-19", downloads: 1119, }, { day: "2019-05-20", downloads: 7004, }, { day: "2019-05-21", downloads: 8720, }, { day: "2019-05-22", downloads: 8184, }, { day: "2019-05-23", downloads: 8670, }, { day: "2019-05-24", downloads: 6742, }, { day: "2019-05-25", downloads: 1306, }, { day: "2019-05-26", downloads: 1308, }, { day: "2019-05-27", downloads: 5080, }, { day: "2019-05-28", downloads: 7611, }, { day: "2019-05-29", downloads: 8854, }, { day: "2019-05-30", downloads: 7248, }, { day: "2019-05-31", downloads: 7158, }, { day: "2019-06-01", downloads: 1313, }, { day: "2019-06-02", downloads: 1132, }, { day: "2019-06-03", downloads: 8199, }, { day: "2019-06-04", downloads: 8517, }, { day: "2019-06-05", downloads: 8383, }, { day: "2019-06-06", downloads: 7889, }, { day: "2019-06-07", downloads: 7549, }, { day: "2019-06-08", downloads: 1123, }, { day: "2019-06-09", downloads: 1082, }, { day: "2019-06-10", downloads: 7658, }, { day: "2019-06-11", downloads: 8377, }, { day: "2019-06-12", downloads: 7601, }, { day: "2019-06-13", downloads: 8047, }, { day: "2019-06-14", downloads: 6791, }, { day: "2019-06-15", downloads: 1013, }, { day: "2019-06-16", downloads: 1129, }, { day: "2019-06-17", downloads: 8086, }, { day: "2019-06-18", downloads: 8814, }, { day: "2019-06-19", downloads: 8850, }, { day: "2019-06-20", downloads: 8872, }, { day: "2019-06-21", downloads: 8155, }, { day: "2019-06-22", downloads: 976, }, { day: "2019-06-23", downloads: 1103, }, { day: "2019-06-24", downloads: 9399, }, { day: "2019-06-25", downloads: 9787, }, { day: "2019-06-26", downloads: 9736, }, { day: "2019-06-27", downloads: 9253, }, { day: "2019-06-28", downloads: 8491, }, { day: "2019-06-29", downloads: 1295, }, { day: "2019-06-30", downloads: 952, }, { day: "2019-07-01", downloads: 8370, }, { day: "2019-07-02", downloads: 9632, }, { day: "2019-07-03", downloads: 9369, }, { day: "2019-07-04", downloads: 5893, }, { day: "2019-07-05", downloads: 5263, }, { day: "2019-07-06", downloads: 1099, }, { day: "2019-07-07", downloads: 993, }, { day: "2019-07-08", downloads: 7990, }, { day: "2019-07-09", downloads: 9031, }, { day: "2019-07-10", downloads: 8711, }, { day: "2019-07-11", downloads: 8822, }, { day: "2019-07-12", downloads: 7887, }, { day: "2019-07-13", downloads: 1165, }, { day: "2019-07-14", downloads: 1327, }, { day: "2019-07-15", downloads: 8576, }, { day: "2019-07-16", downloads: 10036, }, { day: "2019-07-17", downloads: 9480, }, { day: "2019-07-18", downloads: 9918, }, { day: "2019-07-19", downloads: 8071, }, { day: "2019-07-20", downloads: 1675, }, { day: "2019-07-21", downloads: 1626, }, { day: "2019-07-22", downloads: 8590, }, { day: "2019-07-23", downloads: 9905, }, { day: "2019-07-24", downloads: 9423, }, { day: "2019-07-25", downloads: 9536, }, { day: "2019-07-26", downloads: 8111, }, { day: "2019-07-27", downloads: 1166, }, { day: "2019-07-28", downloads: 1121, }, { day: "2019-07-29", downloads: 9632, }, { day: "2019-07-30", downloads: 8377, }, { day: "2019-07-31", downloads: 9505, }, { day: "2019-08-01", downloads: 8616, }, { day: "2019-08-02", downloads: 8056, }, { day: "2019-08-03", downloads: 1256, }, { day: "2019-08-04", downloads: 1062, }, { day: "2019-08-05", downloads: 9240, }, { day: "2019-08-06", downloads: 9499, }, { day: "2019-08-07", downloads: 10264, }, { day: "2019-08-08", downloads: 10308, }, { day: "2019-08-09", downloads: 8542, }, { day: "2019-08-10", downloads: 1514, }, { day: "2019-08-11", downloads: 1474, }, { day: "2019-08-12", downloads: 9759, }, { day: "2019-08-13", downloads: 10176, }, { day: "2019-08-14", downloads: 10145, }, { day: "2019-08-15", downloads: 8770, }, { day: "2019-08-16", downloads: 8955, }, { day: "2019-08-17", downloads: 1357, }, { day: "2019-08-18", downloads: 1522, }, { day: "2019-08-19", downloads: 9959, }, { day: "2019-08-20", downloads: 10711, }, { day: "2019-08-21", downloads: 10327, }, { day: "2019-08-22", downloads: 11025, }, { day: "2019-08-23", downloads: 9844, }, { day: "2019-08-24", downloads: 1473, }, { day: "2019-08-25", downloads: 1351, }, { day: "2019-08-26", downloads: 10140, }, { day: "2019-08-27", downloads: 10913, }, { day: "2019-08-28", downloads: 10845, }, { day: "2019-08-29", downloads: 11517, }, { day: "2019-08-30", downloads: 9396, }, { day: "2019-08-31", downloads: 1483, }, { day: "2019-09-01", downloads: 1190, }, { day: "2019-09-02", downloads: 5858, }, { day: "2019-09-03", downloads: 9918, }, { day: "2019-09-04", downloads: 10666, }, { day: "2019-09-05", downloads: 9697, }, { day: "2019-09-06", downloads: 8861, }, { day: "2019-09-07", downloads: 1439, }, { day: "2019-09-08", downloads: 1189, }, { day: "2019-09-09", downloads: 10527, }, { day: "2019-09-10", downloads: 14613, }, { day: "2019-09-11", downloads: 13616, }, { day: "2019-09-12", downloads: 13312, }, { day: "2019-09-13", downloads: 11974, }, { day: "2019-09-14", downloads: 2753, }, { day: "2019-09-15", downloads: 3221, }, { day: "2019-09-16", downloads: 13963, }, { day: "2019-09-17", downloads: 15955, }, { day: "2019-09-18", downloads: 14432, }, { day: "2019-09-19", downloads: 14673, }, { day: "2019-09-20", downloads: 13010, }, { day: "2019-09-21", downloads: 2722, }, { day: "2019-09-22", downloads: 2266, }, { day: "2019-09-23", downloads: 13018, }, { day: "2019-09-24", downloads: 15433, }, { day: "2019-09-25", downloads: 16953, }, { day: "2019-09-26", downloads: 19189, }, { day: "2019-09-27", downloads: 16487, }, { day: "2019-09-28", downloads: 4867, }, { day: "2019-09-29", downloads: 4306, }, { day: "2019-09-30", downloads: 15197, }, { day: "2019-10-01", downloads: 18405, }, { day: "2019-10-02", downloads: 14454, }, { day: "2019-10-03", downloads: 14511, }, { day: "2019-10-04", downloads: 12446, }, { day: "2019-10-05", downloads: 1959, }, { day: "2019-10-06", downloads: 1468, }, { day: "2019-10-07", downloads: 13891, }, { day: "2019-10-08", downloads: 15371, }, { day: "2019-10-09", downloads: 13740, }, { day: "2019-10-10", downloads: 13215, }, { day: "2019-10-11", downloads: 12080, }, { day: "2019-10-12", downloads: 1791, }, { day: "2019-10-13", downloads: 1426, }, { day: "2019-10-14", downloads: 10905, }, { day: "2019-10-15", downloads: 13979, }, { day: "2019-10-16", downloads: 15764, }, { day: "2019-10-17", downloads: 15633, }, { day: "2019-10-18", downloads: 12585, }, { day: "2019-10-19", downloads: 1722, }, { day: "2019-10-20", downloads: 1509, }, { day: "2019-10-21", downloads: 13803, }, { day: "2019-10-22", downloads: 16022, }, { day: "2019-10-23", downloads: 17777, }, { day: "2019-10-24", downloads: 18594, }, { day: "2019-10-25", downloads: 14913, }, { day: "2019-10-26", downloads: 1912, }, { day: "2019-10-27", downloads: 1724, }, { day: "2019-10-28", downloads: 15114, }, { day: "2019-10-29", downloads: 16921, }, { day: "2019-10-30", downloads: 16354, }, { day: "2019-10-31", downloads: 16435, }, { day: "2019-11-01", downloads: 13199, }, { day: "2019-11-02", downloads: 2210, }, { day: "2019-11-03", downloads: 1634, }, { day: "2019-11-04", downloads: 15319, }, { day: "2019-11-05", downloads: 19426, }, { day: "2019-11-06", downloads: 19022, }, { day: "2019-11-07", downloads: 19421, }, { day: "2019-11-08", downloads: 16716, }, { day: "2019-11-09", downloads: 3270, }, { day: "2019-11-10", downloads: 2178, }, { day: "2019-11-11", downloads: 13080, }, { day: "2019-11-12", downloads: 18042, }, { day: "2019-11-13", downloads: 19308, }, { day: "2019-11-14", downloads: 19852, }, { day: "2019-11-15", downloads: 17950, }, { day: "2019-11-16", downloads: 3074, }, { day: "2019-11-17", downloads: 2006, }, { day: "2019-11-18", downloads: 16223, }, { day: "2019-11-19", downloads: 21735, }, { day: "2019-11-20", downloads: 21210, }, { day: "2019-11-21", downloads: 23187, }, { day: "2019-11-22", downloads: 19202, }, { day: "2019-11-23", downloads: 3657, }, { day: "2019-11-24", downloads: 2011, }, { day: "2019-11-25", downloads: 18045, }, { day: "2019-11-26", downloads: 18923, }, { day: "2019-11-27", downloads: 17432, }, { day: "2019-11-28", downloads: 9946, }, { day: "2019-11-29", downloads: 8000, }, { day: "2019-11-30", downloads: 2383, }, { day: "2019-12-01", downloads: 2066, }, { day: "2019-12-02", downloads: 17135, }, { day: "2019-12-03", downloads: 21256, }, { day: "2019-12-04", downloads: 18483, }, { day: "2019-12-05", downloads: 18390, }, { day: "2019-12-06", downloads: 16855, }, { day: "2019-12-07", downloads: 3254, }, { day: "2019-12-08", downloads: 2091, }, { day: "2019-12-09", downloads: 16769, }, { day: "2019-12-10", downloads: 18079, }, { day: "2019-12-11", downloads: 17417, }, { day: "2019-12-12", downloads: 18954, }, { day: "2019-12-13", downloads: 18729, }, { day: "2019-12-14", downloads: 2848, }, { day: "2019-12-15", downloads: 1600, }, { day: "2019-12-16", downloads: 16942, }, { day: "2019-12-17", downloads: 18865, }, { day: "2019-12-18", downloads: 17146, }, { day: "2019-12-19", downloads: 17056, }, { day: "2019-12-20", downloads: 15520, }, { day: "2019-12-21", downloads: 2581, }, { day: "2019-12-22", downloads: 1511, }, { day: "2019-12-23", downloads: 7474, }, { day: "2019-12-24", downloads: 3815, }, { day: "2019-12-25", downloads: 1864, }, { day: "2019-12-26", downloads: 3976, }, { day: "2019-12-27", downloads: 4994, }, { day: "2019-12-28", downloads: 1420, }, { day: "2019-12-29", downloads: 1390, }, { day: "2019-12-30", downloads: 6286, }, { day: "2019-12-31", downloads: 4024, }, { day: "2020-01-01", downloads: 1687, }, { day: "2020-01-02", downloads: 10488, }, { day: "2020-01-03", downloads: 10794, }, { day: "2020-01-04", downloads: 2094, }, { day: "2020-01-05", downloads: 1447, }, { day: "2020-01-06", downloads: 13860, }, { day: "2020-01-07", downloads: 15719, }, { day: "2020-01-08", downloads: 15873, }, { day: "2020-01-09", downloads: 16892, }, { day: "2020-01-10", downloads: 14826, }, { day: "2020-01-11", downloads: 2941, }, { day: "2020-01-12", downloads: 1964, }, { day: "2020-01-13", downloads: 13486, }, { day: "2020-01-14", downloads: 17073, }, { day: "2020-01-15", downloads: 20667, }, { day: "2020-01-16", downloads: 18558, }, { day: "2020-01-17", downloads: 15600, }, { day: "2020-01-18", downloads: 2345, }, { day: "2020-01-19", downloads: 2211, }, { day: "2020-01-20", downloads: 12286, }, { day: "2020-01-21", downloads: 16671, }, { day: "2020-01-22", downloads: 17263, }, { day: "2020-01-23", downloads: 17806, }, { day: "2020-01-24", downloads: 17297, }, { day: "2020-01-25", downloads: 2791, }, { day: "2020-01-26", downloads: 1744, }, { day: "2020-01-27", downloads: 15599, }, { day: "2020-01-28", downloads: 19450, }, { day: "2020-01-29", downloads: 19121, }, { day: "2020-01-30", downloads: 19482, }, { day: "2020-01-31", downloads: 16553, }, { day: "2020-02-01", downloads: 2792, }, { day: "2020-02-02", downloads: 2098, }, { day: "2020-02-03", downloads: 17211, }, { day: "2020-02-04", downloads: 20172, }, { day: "2020-02-05", downloads: 21894, }, { day: "2020-02-06", downloads: 19606, }, { day: "2020-02-07", downloads: 19698, }, { day: "2020-02-08", downloads: 2949, }, { day: "2020-02-09", downloads: 1985, }, { day: "2020-02-10", downloads: 17324, }, { day: "2020-02-11", downloads: 22081, }, { day: "2020-02-12", downloads: 20836, }, { day: "2020-02-13", downloads: 22586, }, { day: "2020-02-14", downloads: 25737, }, { day: "2020-02-15", downloads: 3362, }, { day: "2020-02-16", downloads: 2633, }, { day: "2020-02-17", downloads: 14967, }, { day: "2020-02-18", downloads: 20153, }, { day: "2020-02-19", downloads: 23679, }, { day: "2020-02-20", downloads: 27203, }, { day: "2020-02-21", downloads: 20985, }, { day: "2020-02-22", downloads: 3379, }, { day: "2020-02-23", downloads: 2002, }, { day: "2020-02-24", downloads: 19851, }, { day: "2020-02-25", downloads: 22793, }, { day: "2020-02-26", downloads: 21655, }, { day: "2020-02-27", downloads: 21919, }, { day: "2020-02-28", downloads: 22414, }, { day: "2020-02-29", downloads: 3504, }, { day: "2020-03-01", downloads: 2227, }, { day: "2020-03-02", downloads: 20203, }, { day: "2020-03-03", downloads: 21809, }, { day: "2020-03-04", downloads: 22791, }, { day: "2020-03-05", downloads: 22968, }, { day: "2020-03-06", downloads: 20104, }, { day: "2020-03-07", downloads: 3430, }, { day: "2020-03-08", downloads: 2339, }, { day: "2020-03-09", downloads: 18462, }, { day: "2020-03-10", downloads: 19974, }, { day: "2020-03-11", downloads: 20907, }, { day: "2020-03-12", downloads: 20706, }, { day: "2020-03-13", downloads: 17870, }, { day: "2020-03-14", downloads: 3356, }, { day: "2020-03-15", downloads: 2876, }, { day: "2020-03-16", downloads: 18737, }, { day: "2020-03-17", downloads: 23228, }, { day: "2020-03-18", downloads: 22146, }, { day: "2020-03-19", downloads: 21481, }, { day: "2020-03-20", downloads: 20925, }, { day: "2020-03-21", downloads: 3820, }, { day: "2020-03-22", downloads: 2731, }, { day: "2020-03-23", downloads: 20862, }, { day: "2020-03-24", downloads: 22923, }, { day: "2020-03-25", downloads: 22450, }, { day: "2020-03-26", downloads: 24191, }, { day: "2020-03-27", downloads: 21351, }, { day: "2020-03-28", downloads: 4475, }, { day: "2020-03-29", downloads: 3343, }, { day: "2020-03-30", downloads: 20927, }, { day: "2020-03-31", downloads: 26116, }, { day: "2020-04-01", downloads: 24603, }, { day: "2020-04-02", downloads: 23067, }, { day: "2020-04-03", downloads: 23058, }, { day: "2020-04-04", downloads: 4039, }, { day: "2020-04-05", downloads: 3002, }, { day: "2020-04-06", downloads: 20515, }, { day: "2020-04-07", downloads: 26732, }, { day: "2020-04-08", downloads: 24984, }, { day: "2020-04-09", downloads: 25839, }, { day: "2020-04-10", downloads: 15862, }, { day: "2020-04-11", downloads: 3790, }, { day: "2020-04-12", downloads: 2399, }, { day: "2020-04-13", downloads: 16153, }, { day: "2020-04-14", downloads: 24035, }, { day: "2020-04-15", downloads: 16288, }, { day: "2020-04-16", downloads: 23402, }, { day: "2020-04-17", downloads: 25210, }, { day: "2020-04-18", downloads: 4634, }, { day: "2020-04-19", downloads: 3019, }, { day: "2020-04-20", downloads: 20497, }, { day: "2020-04-21", downloads: 22283, }, { day: "2020-04-22", downloads: 25803, }, { day: "2020-04-23", downloads: 23591, }, { day: "2020-04-24", downloads: 21888, }, { day: "2020-04-25", downloads: 3408, }, { day: "2020-04-26", downloads: 3184, }, { day: "2020-04-27", downloads: 19118, }, { day: "2020-04-28", downloads: 22785, }, { day: "2020-04-29", downloads: 24070, }, { day: "2020-04-30", downloads: 22835, }, { day: "2020-05-01", downloads: 13855, }, { day: "2020-05-02", downloads: 3852, }, { day: "2020-05-03", downloads: 2844, }, { day: "2020-05-04", downloads: 18230, }, { day: "2020-05-05", downloads: 23016, }, { day: "2020-05-06", downloads: 24201, }, { day: "2020-05-07", downloads: 28082, }, { day: "2020-05-08", downloads: 21024, }, { day: "2020-05-09", downloads: 4002, }, { day: "2020-05-10", downloads: 3100, }, { day: "2020-05-11", downloads: 18184, }, { day: "2020-05-12", downloads: 23230, }, { day: "2020-05-13", downloads: 22050, }, { day: "2020-05-14", downloads: 26274, }, { day: "2020-05-15", downloads: 22813, }, { day: "2020-05-16", downloads: 3905, }, { day: "2020-05-17", downloads: 2990, }, { day: "2020-05-18", downloads: 21793, }, { day: "2020-05-19", downloads: 24740, }, { day: "2020-05-20", downloads: 27330, }, { day: "2020-05-21", downloads: 22753, }, { day: "2020-05-22", downloads: 21867, }, { day: "2020-05-23", downloads: 3546, }, { day: "2020-05-24", downloads: 2581, }, { day: "2020-05-25", downloads: 11325, }, { day: "2020-05-26", downloads: 22510, }, { day: "2020-05-27", downloads: 25545, }, { day: "2020-05-28", downloads: 27941, }, { day: "2020-05-29", downloads: 22913, }, { day: "2020-05-30", downloads: 4275, }, { day: "2020-05-31", downloads: 2705, }, { day: "2020-06-01", downloads: 20949, }, { day: "2020-06-02", downloads: 24015, }, { day: "2020-06-03", downloads: 25237, }, { day: "2020-06-04", downloads: 25423, }, { day: "2020-06-05", downloads: 24412, }, { day: "2020-06-06", downloads: 3666, }, { day: "2020-06-07", downloads: 2705, }, { day: "2020-06-08", downloads: 22778, }, { day: "2020-06-09", downloads: 23994, }, { day: "2020-06-10", downloads: 23743, }, { day: "2020-06-11", downloads: 25748, }, { day: "2020-06-12", downloads: 23180, }, { day: "2020-06-13", downloads: 3417, }, { day: "2020-06-14", downloads: 2655, }, { day: "2020-06-15", downloads: 22394, }, { day: "2020-06-16", downloads: 27796, }, { day: "2020-06-17", downloads: 28363, }, { day: "2020-06-18", downloads: 24122, }, { day: "2020-06-19", downloads: 16152, }, { day: "2020-06-20", downloads: 2951, }, { day: "2020-06-21", downloads: 2264, }, { day: "2020-06-22", downloads: 0, }, { day: "2020-06-23", downloads: 24597, }, { day: "2020-06-24", downloads: 24228, }, { day: "2020-06-25", downloads: 22184, }, { day: "2020-06-26", downloads: 21386, }, { day: "2020-06-27", downloads: 3216, }, { day: "2020-06-28", downloads: 3008, }, { day: "2020-06-29", downloads: 19263, }, { day: "2020-06-30", downloads: 19689, }, { day: "2020-07-01", downloads: 19382, }, { day: "2020-07-02", downloads: 20049, }, { day: "2020-07-03", downloads: 11366, }, { day: "2020-07-04", downloads: 1883, }, { day: "2020-07-05", downloads: 2362, }, { day: "2020-07-06", downloads: 17119, }, { day: "2020-07-07", downloads: 20842, }, { day: "2020-07-08", downloads: 22178, }, { day: "2020-07-09", downloads: 21173, }, { day: "2020-07-10", downloads: 19394, }, { day: "2020-07-11", downloads: 3107, }, { day: "2020-07-12", downloads: 2298, }, { day: "2020-07-13", downloads: 20032, }, { day: "2020-07-14", downloads: 22931, }, { day: "2020-07-15", downloads: 23363, }, { day: "2020-07-16", downloads: 20549, }, { day: "2020-07-17", downloads: 20131, }, { day: "2020-07-18", downloads: 3905, }, { day: "2020-07-19", downloads: 2873, }, { day: "2020-07-20", downloads: 21029, }, { day: "2020-07-21", downloads: 25744, }, { day: "2020-07-22", downloads: 26425, }, { day: "2020-07-23", downloads: 26327, }, { day: "2020-07-24", downloads: 22406, }, { day: "2020-07-25", downloads: 3731, }, { day: "2020-07-26", downloads: 2640, }, { day: "2020-07-27", downloads: 22817, }, { day: "2020-07-28", downloads: 28301, }, { day: "2020-07-29", downloads: 34708, }, { day: "2020-07-30", downloads: 28440, }, { day: "2020-07-31", downloads: 24499, }, { day: "2020-08-01", downloads: 4322, }, { day: "2020-08-02", downloads: 3161, }, { day: "2020-08-03", downloads: 20310, }, { day: "2020-08-04", downloads: 28116, }, { day: "2020-08-05", downloads: 28249, }, { day: "2020-08-06", downloads: 28280, }, { day: "2020-08-07", downloads: 26644, }, { day: "2020-08-08", downloads: 3781, }, { day: "2020-08-09", downloads: 2847, }, { day: "2020-08-10", downloads: 21851, }, { day: "2020-08-11", downloads: 26203, }, { day: "2020-08-12", downloads: 29309, }, { day: "2020-08-13", downloads: 25998, }, { day: "2020-08-14", downloads: 21486, }, { day: "2020-08-15", downloads: 4267, }, { day: "2020-08-16", downloads: 3132, }, { day: "2020-08-17", downloads: 22108, }, { day: "2020-08-18", downloads: 26636, }, { day: "2020-08-19", downloads: 25378, }, { day: "2020-08-20", downloads: 27712, }, { day: "2020-08-21", downloads: 24817, }, { day: "2020-08-22", downloads: 5051, }, { day: "2020-08-23", downloads: 3844, }, { day: "2020-08-24", downloads: 23969, }, { day: "2020-08-25", downloads: 27679, }, { day: "2020-08-26", downloads: 25802, }, { day: "2020-08-27", downloads: 28646, }, { day: "2020-08-28", downloads: 23846, }, { day: "2020-08-29", downloads: 3596, }, { day: "2020-08-30", downloads: 2358, }, { day: "2020-08-31", downloads: 21461, }, { day: "2020-09-01", downloads: 25638, }, { day: "2020-09-02", downloads: 27440, }, { day: "2020-09-03", downloads: 28224, }, { day: "2020-09-04", downloads: 23206, }, { day: "2020-09-05", downloads: 3716, }, { day: "2020-09-06", downloads: 2753, }, { day: "2020-09-07", downloads: 13642, }, { day: "2020-09-08", downloads: 15119, }, { day: "2020-09-09", downloads: 30345, }, { day: "2020-09-10", downloads: 28454, }, { day: "2020-09-11", downloads: 23577, }, { day: "2020-09-12", downloads: 4874, }, { day: "2020-09-13", downloads: 4486, }, { day: "2020-09-14", downloads: 27685, }, { day: "2020-09-15", downloads: 27995, }, { day: "2020-09-16", downloads: 28597, }, { day: "2020-09-17", downloads: 27243, }, { day: "2020-09-18", downloads: 27698, }, { day: "2020-09-19", downloads: 4509, }, { day: "2020-09-20", downloads: 3852, }, { day: "2020-09-21", downloads: 26742, }, { day: "2020-09-22", downloads: 27919, }, { day: "2020-09-23", downloads: 28407, }, { day: "2020-09-24", downloads: 33461, }, { day: "2020-09-25", downloads: 25505, }, { day: "2020-09-26", downloads: 4438, }, { day: "2020-09-27", downloads: 3177, }, { day: "2020-09-28", downloads: 25917, }, { day: "2020-09-29", downloads: 30358, }, { day: "2020-09-30", downloads: 29025, }, { day: "2020-10-01", downloads: 29003, }, { day: "2020-10-02", downloads: 23925, }, { day: "2020-10-03", downloads: 3432, }, { day: "2020-10-04", downloads: 2717, }, { day: "2020-10-05", downloads: 23278, }, { day: "2020-10-06", downloads: 25427, }, { day: "2020-10-07", downloads: 28855, }, { day: "2020-10-08", downloads: 26360, }, { day: "2020-10-09", downloads: 23015, }, { day: "2020-10-10", downloads: 3767, }, { day: "2020-10-11", downloads: 3630, }, { day: "2020-10-12", downloads: 21221, }, { day: "2020-10-13", downloads: 24768, }, { day: "2020-10-14", downloads: 26983, }, { day: "2020-10-15", downloads: 27399, }, { day: "2020-10-16", downloads: 23322, }, { day: "2020-10-17", downloads: 3708, }, { day: "2020-10-18", downloads: 3721, }, { day: "2020-10-19", downloads: 23683, }, { day: "2020-10-20", downloads: 28595, }, { day: "2020-10-21", downloads: 28623, }, { day: "2020-10-22", downloads: 28578, }, { day: "2020-10-23", downloads: 24420, }, { day: "2020-10-24", downloads: 4218, }, { day: "2020-10-25", downloads: 2676, }, { day: "2020-10-26", downloads: 21008, }, { day: "2020-10-27", downloads: 27569, }, { day: "2020-10-28", downloads: 25879, }, { day: "2020-10-29", downloads: 25993, }, { day: "2020-10-30", downloads: 24205, }, { day: "2020-10-31", downloads: 3379, }, { day: "2020-11-01", downloads: 3498, }, { day: "2020-11-02", downloads: 21892, }, { day: "2020-11-03", downloads: 28203, }, { day: "2020-11-04", downloads: 28098, }, { day: "2020-11-05", downloads: 29013, }, { day: "2020-11-06", downloads: 23640, }, { day: "2020-11-07", downloads: 4566, }, { day: "2020-11-08", downloads: 3427, }, { day: "2020-11-09", downloads: 25389, }, { day: "2020-11-10", downloads: 28607, }, { day: "2020-11-11", downloads: 24849, }, { day: "2020-11-12", downloads: 30534, }, { day: "2020-11-13", downloads: 23527, }, { day: "2020-11-14", downloads: 4064, }, { day: "2020-11-15", downloads: 3698, }, { day: "2020-11-16", downloads: 21454, }, { day: "2020-11-17", downloads: 27200, }, { day: "2020-11-18", downloads: 26017, }, { day: "2020-11-19", downloads: 25590, }, { day: "2020-11-20", downloads: 24675, }, { day: "2020-11-21", downloads: 4322, }, { day: "2020-11-22", downloads: 3093, }, { day: "2020-11-23", downloads: 22160, }, { day: "2020-11-24", downloads: 25496, }, { day: "2020-11-25", downloads: 22037, }, { day: "2020-11-26", downloads: 16461, }, { day: "2020-11-27", downloads: 13860, }, { day: "2020-11-28", downloads: 2856, }, { day: "2020-11-29", downloads: 2699, }, { day: "2020-11-30", downloads: 22686, }, { day: "2020-12-01", downloads: 26124, }, { day: "2020-12-02", downloads: 29348, }, { day: "2020-12-03", downloads: 24431, }, { day: "2020-12-04", downloads: 21863, }, { day: "2020-12-05", downloads: 5563, }, { day: "2020-12-06", downloads: 3003, }, { day: "2020-12-07", downloads: 24656, }, { day: "2020-12-08", downloads: 27685, }, { day: "2020-12-09", downloads: 30244, }, { day: "2020-12-10", downloads: 29699, }, { day: "2020-12-11", downloads: 24734, }, { day: "2020-12-12", downloads: 4215, }, { day: "2020-12-13", downloads: 3116, }, { day: "2020-12-14", downloads: 25042, }, { day: "2020-12-15", downloads: 27624, }, { day: "2020-12-16", downloads: 28764, }, { day: "2020-12-17", downloads: 27682, }, { day: "2020-12-18", downloads: 24661, }, { day: "2020-12-19", downloads: 4752, }, { day: "2020-12-20", downloads: 3367, }, { day: "2020-12-21", downloads: 18149, }, { day: "2020-12-22", downloads: 18563, }, { day: "2020-12-23", downloads: 15593, }, { day: "2020-12-24", downloads: 7745, }, { day: "2020-12-25", downloads: 3165, }, { day: "2020-12-26", downloads: 1606, }, { day: "2020-12-27", downloads: 1810, }, { day: "2020-12-28", downloads: 11328, }, { day: "2020-12-29", downloads: 11404, }, { day: "2020-12-30", downloads: 11603, }, { day: "2020-12-31", downloads: 7662, }, { day: "2021-01-01", downloads: 2719, }, { day: "2021-01-02", downloads: 2222, }, { day: "2021-01-03", downloads: 2999, }, { day: "2021-01-04", downloads: 23508, }, { day: "2021-01-05", downloads: 25139, }, { day: "2021-01-06", downloads: 22741, }, { day: "2021-01-07", downloads: 24462, }, { day: "2021-01-08", downloads: 22456, }, { day: "2021-01-09", downloads: 3793, }, { day: "2021-01-10", downloads: 2861, }, { day: "2021-01-11", downloads: 24932, }, { day: "2021-01-12", downloads: 28190, }, { day: "2021-01-13", downloads: 28990, }, { day: "2021-01-14", downloads: 25711, }, { day: "2021-01-15", downloads: 23836, }, { day: "2021-01-16", downloads: 3807, }, { day: "2021-01-17", downloads: 3588, }, { day: "2021-01-18", downloads: 18286, }, { day: "2021-01-19", downloads: 24055, }, { day: "2021-01-20", downloads: 26921, }, { day: "2021-01-21", downloads: 28608, }, { day: "2021-01-22", downloads: 16587, }, { day: "2021-01-23", downloads: 4197, }, { day: "2021-01-24", downloads: 3222, }, { day: "2021-01-25", downloads: 24115, }, { day: "2021-01-26", downloads: 27400, }, { day: "2021-01-27", downloads: 30881, }, { day: "2021-01-28", downloads: 28594, }, { day: "2021-01-29", downloads: 23670, }, { day: "2021-01-30", downloads: 3779, }, { day: "2021-01-31", downloads: 5639, }, { day: "2021-02-01", downloads: 26726, }, { day: "2021-02-02", downloads: 31456, }, { day: "2021-02-03", downloads: 29737, }, { day: "2021-02-04", downloads: 30134, }, { day: "2021-02-05", downloads: 27124, }, { day: "2021-02-06", downloads: 3902, }, { day: "2021-02-07", downloads: 2660, }, { day: "2021-02-08", downloads: 25226, }, { day: "2021-02-09", downloads: 28695, }, { day: "2021-02-10", downloads: 31700, }, { day: "2021-02-11", downloads: 31367, }, { day: "2021-02-12", downloads: 30522, }, { day: "2021-02-13", downloads: 5085, }, { day: "2021-02-14", downloads: 3161, }, { day: "2021-02-15", downloads: 18898, }, { day: "2021-02-16", downloads: 26520, }, { day: "2021-02-17", downloads: 30999, }, { day: "2021-02-18", downloads: 32560, }, { day: "2021-02-19", downloads: 25035, }, { day: "2021-02-20", downloads: 5889, }, { day: "2021-02-21", downloads: 4197, }, { day: "2021-02-22", downloads: 25908, }, { day: "2021-02-23", downloads: 31709, }, { day: "2021-02-24", downloads: 31478, }, { day: "2021-02-25", downloads: 30675, }, { day: "2021-02-26", downloads: 26998, }, { day: "2021-02-27", downloads: 4194, }, { day: "2021-02-28", downloads: 3192, }, { day: "2021-03-01", downloads: 31670, }, { day: "2021-03-02", downloads: 35314, }, { day: "2021-03-03", downloads: 34541, }, { day: "2021-03-04", downloads: 35989, }, { day: "2021-03-05", downloads: 32976, }, { day: "2021-03-06", downloads: 5435, }, { day: "2021-03-07", downloads: 4178, }, { day: "2021-03-08", downloads: 29242, }, { day: "2021-03-09", downloads: 35265, }, { day: "2021-03-10", downloads: 37370, }, { day: "2021-03-11", downloads: 35769, }, { day: "2021-03-12", downloads: 32236, }, { day: "2021-03-13", downloads: 4722, }, { day: "2021-03-14", downloads: 4567, }, { day: "2021-03-15", downloads: 30791, }, { day: "2021-03-16", downloads: 41017, }, { day: "2021-03-17", downloads: 37429, }, { day: "2021-03-18", downloads: 34438, }, { day: "2021-03-19", downloads: 32432, }, { day: "2021-03-20", downloads: 3987, }, { day: "2021-03-21", downloads: 3503, }, { day: "2021-03-22", downloads: 31901, }, { day: "2021-03-23", downloads: 38687, }, { day: "2021-03-24", downloads: 40143, }, { day: "2021-03-25", downloads: 37150, }, { day: "2021-03-26", downloads: 29342, }, { day: "2021-03-27", downloads: 3493, }, { day: "2021-03-28", downloads: 3751, }, { day: "2021-03-29", downloads: 29782, }, { day: "2021-03-30", downloads: 31226, }, { day: "2021-03-31", downloads: 29908, }, { day: "2021-04-01", downloads: 32087, }, { day: "2021-04-02", downloads: 25834, }, { day: "2021-04-03", downloads: 4112, }, { day: "2021-04-04", downloads: 2930, }, { day: "2021-04-05", downloads: 19721, }, { day: "2021-04-06", downloads: 34884, }, { day: "2021-04-07", downloads: 34513, }, { day: "2021-04-08", downloads: 31378, }, { day: "2021-04-09", downloads: 27712, }, { day: "2021-04-10", downloads: 3947, }, { day: "2021-04-11", downloads: 2999, }, { day: "2021-04-12", downloads: 28900, }, { day: "2021-04-13", downloads: 29758, }, { day: "2021-04-14", downloads: 31373, }, { day: "2021-04-15", downloads: 30633, }, { day: "2021-04-16", downloads: 25806, }, { day: "2021-04-17", downloads: 3326, }, { day: "2021-04-18", downloads: 3236, }, { day: "2021-04-19", downloads: 27906, }, { day: "2021-04-20", downloads: 30473, }, { day: "2021-04-21", downloads: 27207, }, { day: "2021-04-22", downloads: 30079, }, { day: "2021-04-23", downloads: 27766, }, { day: "2021-04-24", downloads: 4245, }, { day: "2021-04-25", downloads: 4867, }, { day: "2021-04-26", downloads: 30020, }, { day: "2021-04-27", downloads: 30847, }, { day: "2021-04-28", downloads: 26309, }, { day: "2021-04-29", downloads: 28663, }, { day: "2021-04-30", downloads: 26306, }, { day: "2021-05-01", downloads: 4367, }, { day: "2021-05-02", downloads: 3848, }, { day: "2021-05-03", downloads: 24057, }, { day: "2021-05-04", downloads: 30155, }, { day: "2021-05-05", downloads: 31752, }, { day: "2021-05-06", downloads: 31915, }, { day: "2021-05-07", downloads: 26207, }, { day: "2021-05-08", downloads: 5227, }, { day: "2021-05-09", downloads: 4971, }, { day: "2021-05-10", downloads: 30286, }, { day: "2021-05-11", downloads: 33980, }, { day: "2021-05-12", downloads: 28147, }, { day: "2021-05-13", downloads: 22746, }, { day: "2021-05-14", downloads: 19249, }, { day: "2021-05-15", downloads: 3536, }, { day: "2021-05-16", downloads: 3364, }, { day: "2021-05-17", downloads: 23352, }, { day: "2021-05-18", downloads: 24909, }, { day: "2021-05-19", downloads: 26653, }, { day: "2021-05-20", downloads: 27526, }, { day: "2021-05-21", downloads: 22579, }, { day: "2021-05-22", downloads: 3402, }, { day: "2021-05-23", downloads: 3143, }, { day: "2021-05-24", downloads: 21027, }, { day: "2021-05-25", downloads: 26073, }, { day: "2021-05-26", downloads: 26605, }, { day: "2021-05-27", downloads: 25544, }, { day: "2021-05-28", downloads: 21428, }, { day: "2021-05-29", downloads: 3890, }, { day: "2021-05-30", downloads: 3087, }, { day: "2021-05-31", downloads: 15917, }, { day: "2021-06-01", downloads: 25438, }, { day: "2021-06-02", downloads: 24553, }, { day: "2021-06-03", downloads: 22469, }, { day: "2021-06-04", downloads: 21182, }, { day: "2021-06-05", downloads: 3919, }, { day: "2021-06-06", downloads: 3233, }, { day: "2021-06-07", downloads: 23575, }, { day: "2021-06-08", downloads: 24918, }, { day: "2021-06-09", downloads: 26959, }, { day: "2021-06-10", downloads: 26749, }, { day: "2021-06-11", downloads: 24817, }, { day: "2021-06-12", downloads: 4117, }, { day: "2021-06-13", downloads: 3609, }, { day: "2021-06-14", downloads: 23817, }, { day: "2021-06-15", downloads: 24946, }, { day: "2021-06-16", downloads: 25975, }, { day: "2021-06-17", downloads: 25221, }, { day: "2021-06-18", downloads: 22787, }, { day: "2021-06-19", downloads: 4227, }, { day: "2021-06-20", downloads: 4338, }, { day: "2021-06-21", downloads: 24645, }, { day: "2021-06-22", downloads: 26500, }, { day: "2021-06-23", downloads: 26154, }, { day: "2021-06-24", downloads: 26479, }, { day: "2021-06-25", downloads: 23179, }, { day: "2021-06-26", downloads: 3683, }, { day: "2021-06-27", downloads: 3358, }, { day: "2021-06-28", downloads: 25043, }, { day: "2021-06-29", downloads: 26293, }, { day: "2021-06-30", downloads: 26195, }, { day: "2021-07-01", downloads: 24491, }, { day: "2021-07-02", downloads: 20381, }, { day: "2021-07-03", downloads: 4260, }, { day: "2021-07-04", downloads: 3325, }, { day: "2021-07-05", downloads: 18108, }, { day: "2021-07-06", downloads: 25962, }, { day: "2021-07-07", downloads: 27048, }, { day: "2021-07-08", downloads: 26933, }, { day: "2021-07-09", downloads: 22873, }, { day: "2021-07-10", downloads: 4408, }, { day: "2021-07-11", downloads: 3676, }, { day: "2021-07-12", downloads: 24152, }, { day: "2021-07-13", downloads: 26186, }, { day: "2021-07-14", downloads: 25141, }, { day: "2021-07-15", downloads: 22874, }, { day: "2021-07-16", downloads: 22030, }, { day: "2021-07-17", downloads: 3650, }, { day: "2021-07-18", downloads: 3174, }, { day: "2021-07-19", downloads: 24223, }, { day: "2021-07-20", downloads: 24614, }, { day: "2021-07-21", downloads: 24809, }, { day: "2021-07-22", downloads: 24410, }, { day: "2021-07-23", downloads: 21708, }, { day: "2021-07-24", downloads: 3629, }, { day: "2021-07-25", downloads: 3550, }, { day: "2021-07-26", downloads: 23185, }, { day: "2021-07-27", downloads: 24284, }, { day: "2021-07-28", downloads: 25888, }, { day: "2021-07-29", downloads: 28905, }, { day: "2021-07-30", downloads: 26884, }, { day: "2021-07-31", downloads: 5251, }, { day: "2021-08-01", downloads: 3795, }, { day: "2021-08-02", downloads: 26195, }, { day: "2021-08-03", downloads: 26706, }, { day: "2021-08-04", downloads: 25725, }, { day: "2021-08-05", downloads: 26992, }, { day: "2021-08-06", downloads: 23452, }, { day: "2021-08-07", downloads: 4380, }, { day: "2021-08-08", downloads: 4038, }, { day: "2021-08-09", downloads: 24752, }, { day: "2021-08-10", downloads: 25231, }, { day: "2021-08-11", downloads: 26137, }, { day: "2021-08-12", downloads: 24445, }, { day: "2021-08-13", downloads: 22626, }, { day: "2021-08-14", downloads: 4343, }, { day: "2021-08-15", downloads: 3710, }, { day: "2021-08-16", downloads: 24076, }, { day: "2021-08-17", downloads: 27186, }, { day: "2021-08-18", downloads: 27041, }, { day: "2021-08-19", downloads: 26628, }, { day: "2021-08-20", downloads: 23355, }, { day: "2021-08-21", downloads: 4657, }, { day: "2021-08-22", downloads: 4398, }, { day: "2021-08-23", downloads: 25731, }, { day: "2021-08-24", downloads: 28138, }, { day: "2021-08-25", downloads: 29933, }, { day: "2021-08-26", downloads: 28361, }, { day: "2021-08-27", downloads: 24021, }, { day: "2021-08-28", downloads: 6471, }, { day: "2021-08-29", downloads: 5731, }, { day: "2021-08-30", downloads: 26783, }, { day: "2021-08-31", downloads: 30867, }, { day: "2021-09-01", downloads: 30372, }, { day: "2021-09-02", downloads: 28224, }, { day: "2021-09-03", downloads: 24120, }, { day: "2021-09-04", downloads: 5786, }, { day: "2021-09-05", downloads: 5155, }, { day: "2021-09-06", downloads: 18380, }, { day: "2021-09-07", downloads: 26666, }, { day: "2021-09-08", downloads: 29606, }, { day: "2021-09-09", downloads: 29147, }, { day: "2021-09-10", downloads: 27082, }, { day: "2021-09-11", downloads: 5947, }, { day: "2021-09-12", downloads: 4814, }, { day: "2021-09-13", downloads: 28199, }, { day: "2021-09-14", downloads: 29068, }, { day: "2021-09-15", downloads: 29344, }, { day: "2021-09-16", downloads: 28970, }, { day: "2021-09-17", downloads: 27469, }, { day: "2021-09-18", downloads: 5295, }, { day: "2021-09-19", downloads: 5045, }, { day: "2021-09-20", downloads: 30775, }, { day: "2021-09-21", downloads: 31773, }, { day: "2021-09-22", downloads: 30680, }, { day: "2021-09-23", downloads: 29019, }, { day: "2021-09-24", downloads: 25788, }, { day: "2021-09-25", downloads: 5323, }, { day: "2021-09-26", downloads: 4622, }, { day: "2021-09-27", downloads: 29998, }, { day: "2021-09-28", downloads: 32915, }, { day: "2021-09-29", downloads: 32136, }, { day: "2021-09-30", downloads: 29062, }, { day: "2021-10-01", downloads: 24783, }, { day: "2021-10-02", downloads: 5330, }, { day: "2021-10-03", downloads: 4473, }, { day: "2021-10-04", downloads: 28509, }, { day: "2021-10-05", downloads: 29521, }, { day: "2021-10-06", downloads: 29758, }, { day: "2021-10-07", downloads: 30932, }, { day: "2021-10-08", downloads: 24567, }, { day: "2021-10-09", downloads: 4374, }, { day: "2021-10-10", downloads: 4679, }, { day: "2021-10-11", downloads: 25573, }, { day: "2021-10-12", downloads: 31253, }, { day: "2021-10-13", downloads: 33742, }, { day: "2021-10-14", downloads: 30057, }, { day: "2021-10-15", downloads: 24808, }, { day: "2021-10-16", downloads: 5353, }, { day: "2021-10-17", downloads: 4364, }, { day: "2021-10-18", downloads: 29847, }, { day: "2021-10-19", downloads: 31155, }, { day: "2021-10-20", downloads: 32267, }, { day: "2021-10-21", downloads: 31259, }, { day: "2021-10-22", downloads: 29982, }, { day: "2021-10-23", downloads: 4841, }, { day: "2021-10-24", downloads: 4280, }, { day: "2021-10-25", downloads: 31353, }, { day: "2021-10-26", downloads: 32130, }, { day: "2021-10-27", downloads: 32900, }, { day: "2021-10-28", downloads: 32738, }, { day: "2021-10-29", downloads: 28951, }, { day: "2021-10-30", downloads: 5449, }, { day: "2021-10-31", downloads: 4387, }, { day: "2021-11-01", downloads: 26904, }, { day: "2021-11-02", downloads: 31336, }, { day: "2021-11-03", downloads: 33930, }, { day: "2021-11-04", downloads: 30517, }, { day: "2021-11-05", downloads: 27212, }, { day: "2021-11-06", downloads: 5034, }, { day: "2021-11-07", downloads: 4490, }, { day: "2021-11-08", downloads: 30058, }, { day: "2021-11-09", downloads: 31402, }, { day: "2021-11-10", downloads: 31601, }, { day: "2021-11-11", downloads: 29523, }, { day: "2021-11-12", downloads: 25841, }, { day: "2021-11-13", downloads: 6386, }, { day: "2021-11-14", downloads: 4883, }, { day: "2021-11-15", downloads: 28717, }, { day: "2021-11-16", downloads: 32391, }, { day: "2021-11-17", downloads: 31931, }, { day: "2021-11-18", downloads: 33460, }, { day: "2021-11-19", downloads: 27633, }, { day: "2021-11-20", downloads: 5843, }, { day: "2021-11-21", downloads: 4523, }, { day: "2021-11-22", downloads: 29115, }, { day: "2021-11-23", downloads: 29312, }, { day: "2021-11-24", downloads: 27447, }, { day: "2021-11-25", downloads: 22083, }, { day: "2021-11-26", downloads: 16675, }, { day: "2021-11-27", downloads: 4423, }, { day: "2021-11-28", downloads: 4979, }, { day: "2021-11-29", downloads: 29415, }, { day: "2021-11-30", downloads: 33020, }, { day: "2021-12-01", downloads: 31935, }, { day: "2021-12-02", downloads: 32590, }, { day: "2021-12-03", downloads: 28653, }, { day: "2021-12-04", downloads: 6306, }, { day: "2021-12-05", downloads: 6678, }, { day: "2021-12-06", downloads: 31237, }, { day: "2021-12-07", downloads: 32491, }, { day: "2021-12-08", downloads: 33283, }, { day: "2021-12-09", downloads: 33460, }, { day: "2021-12-10", downloads: 29339, }, { day: "2021-12-11", downloads: 6802, }, { day: "2021-12-12", downloads: 6911, }, { day: "2021-12-13", downloads: 32203, }, { day: "2021-12-14", downloads: 34974, }, { day: "2021-12-15", downloads: 33808, }, { day: "2021-12-16", downloads: 33713, }, { day: "2021-12-17", downloads: 28698, }, { day: "2021-12-18", downloads: 5625, }, { day: "2021-12-19", downloads: 4923, }, { day: "2021-12-20", downloads: 25686, }, { day: "2021-12-21", downloads: 26888, }, { day: "2021-12-22", downloads: 25426, }, { day: "2021-12-23", downloads: 19446, }, { day: "2021-12-24", downloads: 9413, }, { day: "2021-12-25", downloads: 3030, }, { day: "2021-12-26", downloads: 3132, }, { day: "2021-12-27", downloads: 11147, }, { day: "2021-12-28", downloads: 14420, }, { day: "2021-12-29", downloads: 14452, }, { day: "2021-12-30", downloads: 12949, }, { day: "2021-12-31", downloads: 7184, }, { day: "2022-01-01", downloads: 3118, }, { day: "2022-01-02", downloads: 3604, }, { day: "2022-01-03", downloads: 20470, }, { day: "2022-01-04", downloads: 29029, }, { day: "2022-01-05", downloads: 28776, }, { day: "2022-01-06", downloads: 27127, }, { day: "2022-01-07", downloads: 24480, }, { day: "2022-01-08", downloads: 5290, }, { day: "2022-01-09", downloads: 4188, }, { day: "2022-01-10", downloads: 28812, }, { day: "2022-01-11", downloads: 33299, }, { day: "2022-01-12", downloads: 34467, }, { day: "2022-01-13", downloads: 34583, }, { day: "2022-01-14", downloads: 27741, }, { day: "2022-01-15", downloads: 5907, }, { day: "2022-01-16", downloads: 4765, }, { day: "2022-01-17", downloads: 26521, }, { day: "2022-01-18", downloads: 32249, }, { day: "2022-01-19", downloads: 34598, }, { day: "2022-01-20", downloads: 33340, }, { day: "2022-01-21", downloads: 29744, }, { day: "2022-01-22", downloads: 6624, }, { day: "2022-01-23", downloads: 4290, }, { day: "2022-01-24", downloads: 34056, }, { day: "2022-01-25", downloads: 32797, }, { day: "2022-01-26", downloads: 33913, }, { day: "2022-01-27", downloads: 38714, }, { day: "2022-01-28", downloads: 30729, }, { day: "2022-01-29", downloads: 5324, }, { day: "2022-01-30", downloads: 4060, }, { day: "2022-01-31", downloads: 31703, }, { day: "2022-02-01", downloads: 34845, }, { day: "2022-02-02", downloads: 35008, }, { day: "2022-02-03", downloads: 34967, }, { day: "2022-02-04", downloads: 28915, }, { day: "2022-02-05", downloads: 4886, }, { day: "2022-02-06", downloads: 4832, }, { day: "2022-02-07", downloads: 32543, }, { day: "2022-02-08", downloads: 35441, }, { day: "2022-02-09", downloads: 34281, }, { day: "2022-02-10", downloads: 36041, }, { day: "2022-02-11", downloads: 28274, }, { day: "2022-02-12", downloads: 5564, }, { day: "2022-02-13", downloads: 4111, }, { day: "2022-02-14", downloads: 32527, }, { day: "2022-02-15", downloads: 35180, }, { day: "2022-02-16", downloads: 34349, }, { day: "2022-02-17", downloads: 36914, }, { day: "2022-02-18", downloads: 30285, }, { day: "2022-02-19", downloads: 7269, }, { day: "2022-02-20", downloads: 4638, }, { day: "2022-02-21", downloads: 26407, }, { day: "2022-02-22", downloads: 34958, }, { day: "2022-02-23", downloads: 33622, }, { day: "2022-02-24", downloads: 35327, }, { day: "2022-02-25", downloads: 29819, }, { day: "2022-02-26", downloads: 6291, }, { day: "2022-02-27", downloads: 4509, }, { day: "2022-02-28", downloads: 33077, }, { day: "2022-03-01", downloads: 33009, }, { day: "2022-03-02", downloads: 32440, }, { day: "2022-03-03", downloads: 32676, }, { day: "2022-03-04", downloads: 26693, }, { day: "2022-03-05", downloads: 5165, }, { day: "2022-03-06", downloads: 4877, }, { day: "2022-03-07", downloads: 31707, }, { day: "2022-03-08", downloads: 31208, }, { day: "2022-03-09", downloads: 33411, }, { day: "2022-03-10", downloads: 38113, }, { day: "2022-03-11", downloads: 28466, }, { day: "2022-03-12", downloads: 6198, }, { day: "2022-03-13", downloads: 5437, }, { day: "2022-03-14", downloads: 36306, }, { day: "2022-03-15", downloads: 38970, }, { day: "2022-03-16", downloads: 40928, }, { day: "2022-03-17", downloads: 35769, }, { day: "2022-03-18", downloads: 28626, }, { day: "2022-03-19", downloads: 8104, }, { day: "2022-03-20", downloads: 8060, }, { day: "2022-03-21", downloads: 32369, }, { day: "2022-03-22", downloads: 34206, }, { day: "2022-03-23", downloads: 36696, }, { day: "2022-03-24", downloads: 34328, }, { day: "2022-03-25", downloads: 30198, }, { day: "2022-03-26", downloads: 7089, }, { day: "2022-03-27", downloads: 5900, }, { day: "2022-03-28", downloads: 33273, }, { day: "2022-03-29", downloads: 37640, }, { day: "2022-03-30", downloads: 33853, }, { day: "2022-03-31", downloads: 32614, }, { day: "2022-04-01", downloads: 29479, }, { day: "2022-04-02", downloads: 5385, }, { day: "2022-04-03", downloads: 5451, }, { day: "2022-04-04", downloads: 34536, }, { day: "2022-04-05", downloads: 33116, }, { day: "2022-04-06", downloads: 31740, }, { day: "2022-04-07", downloads: 34734, }, { day: "2022-04-08", downloads: 27016, }, { day: "2022-04-09", downloads: 5232, }, { day: "2022-04-10", downloads: 4152, }, { day: "2022-04-11", downloads: 32506, }, { day: "2022-04-12", downloads: 34010, }, { day: "2022-04-13", downloads: 31986, }, { day: "2022-04-14", downloads: 28088, }, { day: "2022-04-15", downloads: 16898, }, { day: "2022-04-16", downloads: 4223, }, { day: "2022-04-17", downloads: 3583, }, { day: "2022-04-18", downloads: 20529, }, { day: "2022-04-19", downloads: 31380, }, { day: "2022-04-20", downloads: 32003, }, { day: "2022-04-21", downloads: 30714, }, { day: "2022-04-22", downloads: 25825, }, { day: "2022-04-23", downloads: 4818, }, { day: "2022-04-24", downloads: 4187, }, { day: "2022-04-25", downloads: 30863, }, { day: "2022-04-26", downloads: 33634, }, { day: "2022-04-27", downloads: 33711, }, { day: "2022-04-28", downloads: 32792, }, { day: "2022-04-29", downloads: 27600, }, { day: "2022-04-30", downloads: 4814, }, { day: "2022-05-01", downloads: 4321, }, { day: "2022-05-02", downloads: 26992, }, { day: "2022-05-03", downloads: 29006, }, { day: "2022-05-04", downloads: 34627, }, { day: "2022-05-05", downloads: 31922, }, { day: "2022-05-06", downloads: 26094, }, { day: "2022-05-07", downloads: 5032, }, { day: "2022-05-08", downloads: 3028, }, { day: "2022-05-09", downloads: 33664, }, { day: "2022-05-10", downloads: 36511, }, { day: "2022-05-11", downloads: 36027, }, { day: "2022-05-12", downloads: 32437, }, { day: "2022-05-13", downloads: 27629, }, { day: "2022-05-14", downloads: 4436, }, { day: "2022-05-15", downloads: 5321, }, { day: "2022-05-16", downloads: 31644, }, { day: "2022-05-17", downloads: 33423, }, { day: "2022-05-18", downloads: 33364, }, { day: "2022-05-19", downloads: 32081, }, { day: "2022-05-20", downloads: 30859, }, { day: "2022-05-21", downloads: 4566, }, { day: "2022-05-22", downloads: 4406, }, { day: "2022-05-23", downloads: 32104, }, { day: "2022-05-24", downloads: 35070, }, { day: "2022-05-25", downloads: 33185, }, { day: "2022-05-26", downloads: 27032, }, { day: "2022-05-27", downloads: 25326, }, { day: "2022-05-28", downloads: 4942, }, { day: "2022-05-29", downloads: 4297, }, { day: "2022-05-30", downloads: 23139, }, { day: "2022-05-31", downloads: 30981, }, { day: "2022-06-01", downloads: 31128, }, { day: "2022-06-02", downloads: 31662, }, { day: "2022-06-03", downloads: 27633, }, { day: "2022-06-04", downloads: 4508, }, { day: "2022-06-05", downloads: 3767, }, { day: "2022-06-06", downloads: 28382, }, { day: "2022-06-07", downloads: 33045, }, { day: "2022-06-08", downloads: 34015, }, { day: "2022-06-09", downloads: 33627, }, { day: "2022-06-10", downloads: 29459, }, { day: "2022-06-11", downloads: 4004, }, { day: "2022-06-12", downloads: 3895, }, { day: "2022-06-13", downloads: 31946, }, { day: "2022-06-14", downloads: 34077, }, { day: "2022-06-15", downloads: 33895, }, { day: "2022-06-16", downloads: 31219, }, { day: "2022-06-17", downloads: 26839, }, { day: "2022-06-18", downloads: 4861, }, { day: "2022-06-19", downloads: 4077, }, { day: "2022-06-20", downloads: 26044, }, { day: "2022-06-21", downloads: 32907, }, { day: "2022-06-22", downloads: 35392, }, { day: "2022-06-23", downloads: 33762, }, { day: "2022-06-24", downloads: 27607, }, { day: "2022-06-25", downloads: 4459, }, { day: "2022-06-26", downloads: 4282, }, { day: "2022-06-27", downloads: 31653, }, { day: "2022-06-28", downloads: 34577, }, { day: "2022-06-29", downloads: 32631, }, { day: "2022-06-30", downloads: 31553, }, { day: "2022-07-01", downloads: 25990, }, { day: "2022-07-02", downloads: 4444, }, { day: "2022-07-03", downloads: 4249, }, { day: "2022-07-04", downloads: 23629, }, { day: "2022-07-05", downloads: 31009, }, { day: "2022-07-06", downloads: 33613, }, { day: "2022-07-07", downloads: 32788, }, { day: "2022-07-08", downloads: 25598, }, { day: "2022-07-09", downloads: 4267, }, { day: "2022-07-10", downloads: 3751, }, { day: "2022-07-11", downloads: 31858, }, { day: "2022-07-12", downloads: 35614, }, { day: "2022-07-13", downloads: 38198, }, { day: "2022-07-14", downloads: 37070, }, { day: "2022-07-15", downloads: 31904, }, { day: "2022-07-16", downloads: 4863, }, { day: "2022-07-17", downloads: 4348, }, { day: "2022-07-18", downloads: 33765, }, { day: "2022-07-19", downloads: 39552, }, { day: "2022-07-20", downloads: 40217, }, { day: "2022-07-21", downloads: 41991, }, { day: "2022-07-22", downloads: 0, }, { day: "2022-07-23", downloads: 5208, }, { day: "2022-07-24", downloads: 4934, }, { day: "2022-07-25", downloads: 39569, }, { day: "2022-07-26", downloads: 37316, }, { day: "2022-07-27", downloads: 36886, }, { day: "2022-07-28", downloads: 35124, }, { day: "2022-07-29", downloads: 28415, }, { day: "2022-07-30", downloads: 4811, }, { day: "2022-07-31", downloads: 4614, }, { day: "2022-08-01", downloads: 31594, }, { day: "2022-08-02", downloads: 31977, }, { day: "2022-08-03", downloads: 33965, }, { day: "2022-08-04", downloads: 40292, }, { day: "2022-08-05", downloads: 34754, }, { day: "2022-08-06", downloads: 4822, }, { day: "2022-08-07", downloads: 4592, }, { day: "2022-08-08", downloads: 38984, }, { day: "2022-08-09", downloads: 39890, }, { day: "2022-08-10", downloads: 46371, }, { day: "2022-08-11", downloads: 40800, }, { day: "2022-08-12", downloads: 32867, }, { day: "2022-08-13", downloads: 4566, }, { day: "2022-08-14", downloads: 4479, }, { day: "2022-08-15", downloads: 28211, }, { day: "2022-08-16", downloads: 38135, }, { day: "2022-08-17", downloads: 42099, }, { day: "2022-08-18", downloads: 40740, }, { day: "2022-08-19", downloads: 31223, }, { day: "2022-08-20", downloads: 4997, }, { day: "2022-08-21", downloads: 4982, }, { day: "2022-08-22", downloads: 39473, }, { day: "2022-08-23", downloads: 40580, }, { day: "2022-08-24", downloads: 36946, }, { day: "2022-08-25", downloads: 35167, }, { day: "2022-08-26", downloads: 31099, }, { day: "2022-08-27", downloads: 4626, }, { day: "2022-08-28", downloads: 4177, }, { day: "2022-08-29", downloads: 31517, }, { day: "2022-08-30", downloads: 36843, }, { day: "2022-08-31", downloads: 37416, }, { day: "2022-09-01", downloads: 40967, }, { day: "2022-09-02", downloads: 32936, }, { day: "2022-09-03", downloads: 5451, }, { day: "2022-09-04", downloads: 4173, }, { day: "2022-09-05", downloads: 25348, }, { day: "2022-09-06", downloads: 38304, }, { day: "2022-09-07", downloads: 40670, }, { day: "2022-09-08", downloads: 37948, }, { day: "2022-09-09", downloads: 33688, }, { day: "2022-09-10", downloads: 4778, }, { day: "2022-09-11", downloads: 4974, }, { day: "2022-09-12", downloads: 35847, }, { day: "2022-09-13", downloads: 38571, }, { day: "2022-09-14", downloads: 40244, }, { day: "2022-09-15", downloads: 39516, }, { day: "2022-09-16", downloads: 32187, }, { day: "2022-09-17", downloads: 5064, }, { day: "2022-09-18", downloads: 4873, }, { day: "2022-09-19", downloads: 37674, }, { day: "2022-09-20", downloads: 40593, }, { day: "2022-09-21", downloads: 38164, }, { day: "2022-09-22", downloads: 35140, }, { day: "2022-09-23", downloads: 31200, }, { day: "2022-09-24", downloads: 4917, }, { day: "2022-09-25", downloads: 4132, }, { day: "2022-09-26", downloads: 34996, }, { day: "2022-09-27", downloads: 38293, }, { day: "2022-09-28", downloads: 37519, }, { day: "2022-09-29", downloads: 39666, }, { day: "2022-09-30", downloads: 32197, }, { day: "2022-10-01", downloads: 6067, }, { day: "2022-10-02", downloads: 4960, }, { day: "2022-10-03", downloads: 34454, }, { day: "2022-10-04", downloads: 38047, }, { day: "2022-10-05", downloads: 36417, }, { day: "2022-10-06", downloads: 38492, }, { day: "2022-10-07", downloads: 32427, }, { day: "2022-10-08", downloads: 4832, }, { day: "2022-10-09", downloads: 4778, }, { day: "2022-10-10", downloads: 32671, }, { day: "2022-10-11", downloads: 37877, }, { day: "2022-10-12", downloads: 36881, }, { day: "2022-10-13", downloads: 38875, }, { day: "2022-10-14", downloads: 34397, }, { day: "2022-10-15", downloads: 5108, }, { day: "2022-10-16", downloads: 4279, }, { day: "2022-10-17", downloads: 38700, }, { day: "2022-10-18", downloads: 42548, }, { day: "2022-10-19", downloads: 41881, }, { day: "2022-10-20", downloads: 40615, }, { day: "2022-10-21", downloads: 34599, }, { day: "2022-10-22", downloads: 5674, }, { day: "2022-10-23", downloads: 4639, }, { day: "2022-10-24", downloads: 35157, }, { day: "2022-10-25", downloads: 38776, }, { day: "2022-10-26", downloads: 38820, }, { day: "2022-10-27", downloads: 39645, }, { day: "2022-10-28", downloads: 34826, }, { day: "2022-10-29", downloads: 6998, }, { day: "2022-10-30", downloads: 5425, }, { day: "2022-10-31", downloads: 33325, }, { day: "2022-11-01", downloads: 33623, }, { day: "2022-11-02", downloads: 37272, }, { day: "2022-11-03", downloads: 40107, }, { day: "2022-11-04", downloads: 32908, }, { day: "2022-11-05", downloads: 7709, }, { day: "2022-11-06", downloads: 5614, }, { day: "2022-11-07", downloads: 39589, }, { day: "2022-11-08", downloads: 43175, }, { day: "2022-11-09", downloads: 41296, }, { day: "2022-11-10", downloads: 41358, }, { day: "2022-11-11", downloads: 27929, }, { day: "2022-11-12", downloads: 5167, }, { day: "2022-11-13", downloads: 5241, }, { day: "2022-11-14", downloads: 39861, }, { day: "2022-11-15", downloads: 44073, }, { day: "2022-11-16", downloads: 41174, }, { day: "2022-11-17", downloads: 40983, }, { day: "2022-11-18", downloads: 33878, }, { day: "2022-11-19", downloads: 5930, }, { day: "2022-11-20", downloads: 4699, }, { day: "2022-11-21", downloads: 36805, }, { day: "2022-11-22", downloads: 38327, }, { day: "2022-11-23", downloads: 37672, }, { day: "2022-11-24", downloads: 28050, }, { day: "2022-11-25", downloads: 20966, }, { day: "2022-11-26", downloads: 4732, }, { day: "2022-11-27", downloads: 5279, }, { day: "2022-11-28", downloads: 35279, }, { day: "2022-11-29", downloads: 39366, }, { day: "2022-11-30", downloads: 36538, }, { day: "2022-12-01", downloads: 38181, }, { day: "2022-12-02", downloads: 34167, }, { day: "2022-12-03", downloads: 6649, }, { day: "2022-12-04", downloads: 5214, }, { day: "2022-12-05", downloads: 37614, }, { day: "2022-12-06", downloads: 38648, }, { day: "2022-12-07", downloads: 38484, }, { day: "2022-12-08", downloads: 36338, }, { day: "2022-12-09", downloads: 30251, }, { day: "2022-12-10", downloads: 5879, }, { day: "2022-12-11", downloads: 5098, }, { day: "2022-12-12", downloads: 36820, }, { day: "2022-12-13", downloads: 39797, }, { day: "2022-12-14", downloads: 39771, }, { day: "2022-12-15", downloads: 37667, }, { day: "2022-12-16", downloads: 30347, }, { day: "2022-12-17", downloads: 5894, }, { day: "2022-12-18", downloads: 4857, }, { day: "2022-12-19", downloads: 32703, }, { day: "2022-12-20", downloads: 34754, }, { day: "2022-12-21", downloads: 33208, }, { day: "2022-12-22", downloads: 31557, }, { day: "2022-12-23", downloads: 20475, }, { day: "2022-12-24", downloads: 4216, }, { day: "2022-12-25", downloads: 3378, }, { day: "2022-12-26", downloads: 10833, }, { day: "2022-12-27", downloads: 16636, }, { day: "2022-12-28", downloads: 18144, }, { day: "2022-12-29", downloads: 17490, }, { day: "2022-12-30", downloads: 13564, }, { day: "2022-12-31", downloads: 3794, }, { day: "2023-01-01", downloads: 4139, }, { day: "2023-01-02", downloads: 15161, }, { day: "2023-01-03", downloads: 31155, }, { day: "2023-01-04", downloads: 34187, }, { day: "2023-01-05", downloads: 33164, }, { day: "2023-01-06", downloads: 26417, }, { day: "2023-01-07", downloads: 5514, }, { day: "2023-01-08", downloads: 4619, }, { day: "2023-01-09", downloads: 36398, }, { day: "2023-01-10", downloads: 37812, }, { day: "2023-01-11", downloads: 37515, }, { day: "2023-01-12", downloads: 39090, }, { day: "2023-01-13", downloads: 34165, }, { day: "2023-01-14", downloads: 5864, }, { day: "2023-01-15", downloads: 5456, }, { day: "2023-01-16", downloads: 29712, }, { day: "2023-01-17", downloads: 37651, }, { day: "2023-01-18", downloads: 37138, }, { day: "2023-01-19", downloads: 34241, }, { day: "2023-01-20", downloads: 31019, }, { day: "2023-01-21", downloads: 6312, }, { day: "2023-01-22", downloads: 5676, }, { day: "2023-01-23", downloads: 36498, }, { day: "2023-01-24", downloads: 34749, }, { day: "2023-01-25", downloads: 35075, }, { day: "2023-01-26", downloads: 33229, }, { day: "2023-01-27", downloads: 30432, }, { day: "2023-01-28", downloads: 5804, }, { day: "2023-01-29", downloads: 5592, }, { day: "2023-01-30", downloads: 33627, }, { day: "2023-01-31", downloads: 38561, }, { day: "2023-02-01", downloads: 36553, }, { day: "2023-02-02", downloads: 36416, }, { day: "2023-02-03", downloads: 32195, }, { day: "2023-02-04", downloads: 6158, }, { day: "2023-02-05", downloads: 5277, }, { day: "2023-02-06", downloads: 34466, }, { day: "2023-02-07", downloads: 38201, }, { day: "2023-02-08", downloads: 36752, }, { day: "2023-02-09", downloads: 36118, }, { day: "2023-02-10", downloads: 30709, }, { day: "2023-02-11", downloads: 6141, }, { day: "2023-02-12", downloads: 5516, }, { day: "2023-02-13", downloads: 33565, }, { day: "2023-02-14", downloads: 38580, }, { day: "2023-02-15", downloads: 37636, }, { day: "2023-02-16", downloads: 36047, }, { day: "2023-02-17", downloads: 32172, }, { day: "2023-02-18", downloads: 6804, }, { day: "2023-02-19", downloads: 5846, }, { day: "2023-02-20", downloads: 29032, }, { day: "2023-02-21", downloads: 36983, }, { day: "2023-02-22", downloads: 39959, }, { day: "2023-02-23", downloads: 37131, }, { day: "2023-02-24", downloads: 33259, }, { day: "2023-02-25", downloads: 6110, }, { day: "2023-02-26", downloads: 5706, }, { day: "2023-02-27", downloads: 35556, }, { day: "2023-02-28", downloads: 38491, }, { day: "2023-03-01", downloads: 41047, }, { day: "2023-03-02", downloads: 37515, }, { day: "2023-03-03", downloads: 31578, }, { day: "2023-03-04", downloads: 6980, }, { day: "2023-03-05", downloads: 5447, }, { day: "2023-03-06", downloads: 35573, }, { day: "2023-03-07", downloads: 38076, }, { day: "2023-03-08", downloads: 33708, }, { day: "2023-03-09", downloads: 37520, }, { day: "2023-03-10", downloads: 32108, }, { day: "2023-03-11", downloads: 5788, }, { day: "2023-03-12", downloads: 5342, }, { day: "2023-03-13", downloads: 34805, }, { day: "2023-03-14", downloads: 36822, }, { day: "2023-03-15", downloads: 39404, }, { day: "2023-03-16", downloads: 35612, }, { day: "2023-03-17", downloads: 31099, }, { day: "2023-03-18", downloads: 5150, }, { day: "2023-03-19", downloads: 5413, }, { day: "2023-03-20", downloads: 36060, }, { day: "2023-03-21", downloads: 38001, }, { day: "2023-03-22", downloads: 36587, }, { day: "2023-03-23", downloads: 35442, }, { day: "2023-03-24", downloads: 29387, }, { day: "2023-03-25", downloads: 4884, }, { day: "2023-03-26", downloads: 5453, }, { day: "2023-03-27", downloads: 33913, }, { day: "2023-03-28", downloads: 36777, }, { day: "2023-03-29", downloads: 0, }, { day: "2023-03-30", downloads: 37871, }, { day: "2023-03-31", downloads: 30736, }, { day: "2023-04-01", downloads: 6005, }, { day: "2023-04-02", downloads: 5313, }, { day: "2023-04-03", downloads: 34877, }, { day: "2023-04-04", downloads: 37588, }, { day: "2023-04-05", downloads: 36490, }, { day: "2023-04-06", downloads: 32947, }, { day: "2023-04-07", downloads: 20388, }, { day: "2023-04-08", downloads: 4398, }, { day: "2023-04-09", downloads: 3934, }, { day: "2023-04-10", downloads: 24043, }, { day: "2023-04-11", downloads: 38119, }, { day: "2023-04-12", downloads: 35943, }, { day: "2023-04-13", downloads: 37331, }, { day: "2023-04-14", downloads: 31630, }, { day: "2023-04-15", downloads: 4933, }, { day: "2023-04-16", downloads: 4726, }, { day: "2023-04-17", downloads: 37000, }, { day: "2023-04-18", downloads: 40640, }, { day: "2023-04-19", downloads: 39340, }, { day: "2023-04-20", downloads: 37948, }, { day: "2023-04-21", downloads: 31050, }, { day: "2023-04-22", downloads: 5372, }, { day: "2023-04-23", downloads: 5310, }, { day: "2023-04-24", downloads: 39292, }, { day: "2023-04-25", downloads: 38776, }, { day: "2023-04-26", downloads: 40695, }, { day: "2023-04-27", downloads: 39129, }, { day: "2023-04-28", downloads: 33466, }, { day: "2023-04-29", downloads: 5324, }, { day: "2023-04-30", downloads: 4282, }, { day: "2023-05-01", downloads: 24531, }, { day: "2023-05-02", downloads: 39738, }, { day: "2023-05-03", downloads: 39732, }, { day: "2023-05-04", downloads: 41082, }, { day: "2023-05-05", downloads: 35119, }, { day: "2023-05-06", downloads: 5741, }, { day: "2023-05-07", downloads: 5651, }, { day: "2023-05-08", downloads: 36849, }, { day: "2023-05-09", downloads: 39868, }, { day: "2023-05-10", downloads: 36512, }, { day: "2023-05-11", downloads: 38502, }, { day: "2023-05-12", downloads: 33260, }, { day: "2023-05-13", downloads: 5303, }, { day: "2023-05-14", downloads: 5373, }, { day: "2023-05-15", downloads: 37206, }, { day: "2023-05-16", downloads: 41038, }, { day: "2023-05-17", downloads: 39702, }, { day: "2023-05-18", downloads: 32397, }, { day: "2023-05-19", downloads: 30730, }, { day: "2023-05-20", downloads: 4879, }, { day: "2023-05-21", downloads: 4552, }, { day: "2023-05-22", downloads: 35783, }, { day: "2023-05-23", downloads: 40767, }, { day: "2023-05-24", downloads: 41342, }, { day: "2023-05-25", downloads: 38415, }, { day: "2023-05-26", downloads: 31889, }, { day: "2023-05-27", downloads: 4533, }, { day: "2023-05-28", downloads: 4196, }, { day: "2023-05-29", downloads: 22728, }, { day: "2023-05-30", downloads: 35568, }, { day: "2023-05-31", downloads: 39694, }, { day: "2023-06-01", downloads: 37264, }, { day: "2023-06-02", downloads: 32466, }, { day: "2023-06-03", downloads: 5311, }, { day: "2023-06-04", downloads: 5766, }, { day: "2023-06-05", downloads: 36412, }, { day: "2023-06-06", downloads: 39325, }, { day: "2023-06-07", downloads: 40969, }, { day: "2023-06-08", downloads: 38163, }, { day: "2023-06-09", downloads: 34397, }, { day: "2023-06-10", downloads: 6196, }, { day: "2023-06-11", downloads: 4743, }, { day: "2023-06-12", downloads: 37845, }, { day: "2023-06-13", downloads: 42043, }, { day: "2023-06-14", downloads: 41011, }, { day: "2023-06-15", downloads: 39248, }, { day: "2023-06-16", downloads: 34490, }, { day: "2023-06-17", downloads: 5140, }, { day: "2023-06-18", downloads: 5071, }, { day: "2023-06-19", downloads: 32493, }, { day: "2023-06-20", downloads: 38150, }, { day: "2023-06-21", downloads: 38395, }, { day: "2023-06-22", downloads: 38171, }, { day: "2023-06-23", downloads: 34663, }, { day: "2023-06-24", downloads: 5422, }, { day: "2023-06-25", downloads: 5376, }, { day: "2023-06-26", downloads: 37878, }, { day: "2023-06-27", downloads: 39680, }, { day: "2023-06-28", downloads: 37314, }, { day: "2023-06-29", downloads: 35597, }, { day: "2023-06-30", downloads: 32117, }, { day: "2023-07-01", downloads: 0, }, { day: "2023-07-02", downloads: 4654, }, { day: "2023-07-03", downloads: 31363, }, { day: "2023-07-04", downloads: 28835, }, { day: "2023-07-05", downloads: 37381, }, { day: "2023-07-06", downloads: 39977, }, { day: "2023-07-07", downloads: 35191, }, { day: "2023-07-08", downloads: 6754, }, { day: "2023-07-09", downloads: 5965, }, { day: "2023-07-10", downloads: 40388, }, { day: "2023-07-11", downloads: 43661, }, { day: "2023-07-12", downloads: 41249, }, { day: "2023-07-13", downloads: 39141, }, { day: "2023-07-14", downloads: 32866, }, { day: "2023-07-15", downloads: 5011, }, { day: "2023-07-16", downloads: 5470, }, { day: "2023-07-17", downloads: 37968, }, { day: "2023-07-18", downloads: 41998, }, { day: "2023-07-19", downloads: 40055, }, { day: "2023-07-20", downloads: 37709, }, { day: "2023-07-21", downloads: 31701, }, { day: "2023-07-22", downloads: 5554, }, { day: "2023-07-23", downloads: 5110, }, { day: "2023-07-24", downloads: 37098, }, { day: "2023-07-25", downloads: 37941, }, { day: "2023-07-26", downloads: 38245, }, { day: "2023-07-27", downloads: 35873, }, { day: "2023-07-28", downloads: 31305, }, { day: "2023-07-29", downloads: 5294, }, { day: "2023-07-30", downloads: 5083, }, { day: "2023-07-31", downloads: 36647, }, { day: "2023-08-01", downloads: 39219, }, { day: "2023-08-02", downloads: 39659, }, { day: "2023-08-03", downloads: 37983, }, { day: "2023-08-04", downloads: 31218, }, { day: "2023-08-05", downloads: 5493, }, { day: "2023-08-06", downloads: 5700, }, { day: "2023-08-07", downloads: 35960, }, { day: "2023-08-08", downloads: 40288, }, { day: "2023-08-09", downloads: 39572, }, { day: "2023-08-10", downloads: 40725, }, { day: "2023-08-11", downloads: 32408, }, { day: "2023-08-12", downloads: 5700, }, { day: "2023-08-13", downloads: 5725, }, { day: "2023-08-14", downloads: 37604, }, { day: "2023-08-15", downloads: 35762, }, { day: "2023-08-16", downloads: 42059, }, { day: "2023-08-17", downloads: 39557, }, { day: "2023-08-18", downloads: 33749, }, { day: "2023-08-19", downloads: 5545, }, { day: "2023-08-20", downloads: 5578, }, { day: "2023-08-21", downloads: 39058, }, { day: "2023-08-22", downloads: 40394, }, { day: "2023-08-23", downloads: 39224, }, { day: "2023-08-24", downloads: 39327, }, { day: "2023-08-25", downloads: 33640, }, { day: "2023-08-26", downloads: 5568, }, { day: "2023-08-27", downloads: 5452, }, { day: "2023-08-28", downloads: 36824, }, { day: "2023-08-29", downloads: 39073, }, { day: "2023-08-30", downloads: 39323, }, { day: "2023-08-31", downloads: 41019, }, { day: "2023-09-01", downloads: 33004, }, { day: "2023-09-02", downloads: 5603, }, { day: "2023-09-03", downloads: 6058, }, { day: "2023-09-04", downloads: 25503, }, { day: "2023-09-05", downloads: 39039, }, { day: "2023-09-06", downloads: 39465, }, { day: "2023-09-07", downloads: 37695, }, { day: "2023-09-08", downloads: 33268, }, { day: "2023-09-09", downloads: 5515, }, { day: "2023-09-10", downloads: 5720, }, { day: "2023-09-11", downloads: 37737, }, { day: "2023-09-12", downloads: 38926, }, { day: "2023-09-13", downloads: 0, }, { day: "2023-09-14", downloads: 0, }, { day: "2023-09-15", downloads: 33854, }, { day: "2023-09-16", downloads: 5678, }, { day: "2023-09-17", downloads: 5551, }, { day: "2023-09-18", downloads: 36439, }, { day: "2023-09-19", downloads: 39031, }, { day: "2023-09-20", downloads: 39950, }, { day: "2023-09-21", downloads: 38461, }, { day: "2023-09-22", downloads: 33511, }, { day: "2023-09-23", downloads: 5157, }, { day: "2023-09-24", downloads: 5253, }, { day: "2023-09-25", downloads: 37626, }, { day: "2023-09-26", downloads: 40204, }, { day: "2023-09-27", downloads: 40193, }, { day: "2023-09-28", downloads: 34834, }, { day: "2023-09-29", downloads: 31401, }, { day: "2023-09-30", downloads: 5459, }, { day: "2023-10-01", downloads: 6581, }, { day: "2023-10-02", downloads: 34473, }, { day: "2023-10-03", downloads: 38246, }, { day: "2023-10-04", downloads: 40662, }, { day: "2023-10-05", downloads: 38160, }, { day: "2023-10-06", downloads: 33333, }, { day: "2023-10-07", downloads: 5845, }, { day: "2023-10-08", downloads: 5461, }, { day: "2023-10-09", downloads: 30981, }, { day: "2023-10-10", downloads: 38352, }, { day: "2023-10-11", downloads: 39837, }, { day: "2023-10-12", downloads: 39334, }, { day: "2023-10-13", downloads: 32103, }, { day: "2023-10-14", downloads: 4948, }, { day: "2023-10-15", downloads: 5399, }, { day: "2023-10-16", downloads: 38939, }, { day: "2023-10-17", downloads: 41498, }, { day: "2023-10-18", downloads: 40133, }, { day: "2023-10-19", downloads: 41696, }, { day: "2023-10-20", downloads: 35038, }, { day: "2023-10-21", downloads: 5862, }, { day: "2023-10-22", downloads: 5394, }, { day: "2023-10-23", downloads: 39043, }, { day: "2023-10-24", downloads: 38789, }, { day: "2023-10-25", downloads: 43376, }, { day: "2023-10-26", downloads: 38491, }, { day: "2023-10-27", downloads: 35056, }, { day: "2023-10-28", downloads: 5020, }, { day: "2023-10-29", downloads: 6042, }, { day: "2023-10-30", downloads: 39269, }, { day: "2023-10-31", downloads: 41735, }, { day: "2023-11-01", downloads: 37687, }, { day: "2023-11-02", downloads: 13650, }, { day: "2023-11-03", downloads: 0, }, { day: "2023-11-04", downloads: 6617, }, { day: "2023-11-05", downloads: 6054, }, { day: "2023-11-06", downloads: 42700, }, { day: "2023-11-07", downloads: 44486, }, { day: "2023-11-08", downloads: 43884, }, { day: "2023-11-09", downloads: 40849, }, { day: "2023-11-10", downloads: 35183, }, { day: "2023-11-11", downloads: 5803, }, { day: "2023-11-12", downloads: 6003, }, { day: "2023-11-13", downloads: 39412, }, { day: "2023-11-14", downloads: 41653, }, { day: "2023-11-15", downloads: 41824, }, { day: "2023-11-16", downloads: 42578, }, { day: "2023-11-17", downloads: 37206, }, { day: "2023-11-18", downloads: 6360, }, { day: "2023-11-19", downloads: 6583, }, { day: "2023-11-20", downloads: 38234, }, { day: "2023-11-21", downloads: 42480, }, { day: "2023-11-22", downloads: 38974, }, { day: "2023-11-23", downloads: 30813, }, { day: "2023-11-24", downloads: 26554, }, { day: "2023-11-25", downloads: 4994, }, { day: "2023-11-26", downloads: 5757, }, { day: "2023-11-27", downloads: 40732, }, { day: "2023-11-28", downloads: 43418, }, { day: "2023-11-29", downloads: 43964, }, { day: "2023-11-30", downloads: 43138, }, { day: "2023-12-01", downloads: 38634, }, { day: "2023-12-02", downloads: 8195, }, { day: "2023-12-03", downloads: 7629, }, { day: "2023-12-04", downloads: 43429, }, { day: "2023-12-05", downloads: 45672, }, { day: "2023-12-06", downloads: 45945, }, { day: "2023-12-07", downloads: 42395, }, { day: "2023-12-08", downloads: 37113, }, { day: "2023-12-09", downloads: 8245, }, { day: "2023-12-10", downloads: 7508, }, { day: "2023-12-11", downloads: 45494, }, { day: "2023-12-12", downloads: 49548, }, { day: "2023-12-13", downloads: 46740, }, { day: "2023-12-14", downloads: 45794, }, { day: "2023-12-15", downloads: 39743, }, { day: "2023-12-16", downloads: 6932, }, { day: "2023-12-17", downloads: 6192, }, { day: "2023-12-18", downloads: 38450, }, { day: "2023-12-19", downloads: 41548, }, { day: "2023-12-20", downloads: 40992, }, { day: "2023-12-21", downloads: 37864, }, { day: "2023-12-22", downloads: 26212, }, { day: "2023-12-23", downloads: 6303, }, { day: "2023-12-24", downloads: 5024, }, { day: "2023-12-25", downloads: 9872, }, { day: "2023-12-26", downloads: 14261, }, { day: "2023-12-27", downloads: 18809, }, { day: "2023-12-28", downloads: 19870, }, { day: "2023-12-29", downloads: 15239, }, { day: "2023-12-30", downloads: 5522, }, { day: "2023-12-31", downloads: 4864, }, { day: "2024-01-01", downloads: 7638, }, { day: "2024-01-02", downloads: 33013, }, { day: "2024-01-03", downloads: 36147, }, { day: "2024-01-04", downloads: 38681, }, { day: "2024-01-05", downloads: 34532, }, { day: "2024-01-06", downloads: 6397, }, { day: "2024-01-07", downloads: 5425, }, { day: "2024-01-08", downloads: 39356, }, { day: "2024-01-09", downloads: 46544, }, { day: "2024-01-10", downloads: 45818, }, { day: "2024-01-11", downloads: 45322, }, { day: "2024-01-12", downloads: 39774, }, { day: "2024-01-13", downloads: 6236, }, { day: "2024-01-14", downloads: 5572, }, { day: "2024-01-15", downloads: 32852, }, { day: "2024-01-16", downloads: 42961, }, { day: "2024-01-17", downloads: 45820, }, { day: "2024-01-18", downloads: 43314, }, { day: "2024-01-19", downloads: 39618, }, { day: "2024-01-20", downloads: 6970, }, { day: "2024-01-21", downloads: 5690, }, { day: "2024-01-22", downloads: 42138, }, { day: "2024-01-23", downloads: 45260, }, { day: "2024-01-24", downloads: 43959, }, { day: "2024-01-25", downloads: 42333, }, { day: "2024-01-26", downloads: 36125, }, { day: "2024-01-27", downloads: 6015, }, { day: "2024-01-28", downloads: 5956, }, { day: "2024-01-29", downloads: 43201, }, { day: "2024-01-30", downloads: 45225, }, { day: "2024-01-31", downloads: 45249, }, { day: "2024-02-01", downloads: 45552, }, { day: "2024-02-02", downloads: 38488, }, { day: "2024-02-03", downloads: 6521, }, { day: "2024-02-04", downloads: 5891, }, { day: "2024-02-05", downloads: 42720, }, { day: "2024-02-06", downloads: 49491, }, { day: "2024-02-07", downloads: 49868, }, { day: "2024-02-08", downloads: 48491, }, { day: "2024-02-09", downloads: 38858, }, { day: "2024-02-10", downloads: 6406, }, { day: "2024-02-11", downloads: 6581, }, { day: "2024-02-12", downloads: 38601, }, { day: "2024-02-13", downloads: 45052, }, { day: "2024-02-14", downloads: 43539, }, { day: "2024-02-15", downloads: 43567, }, { day: "2024-02-16", downloads: 38064, }, { day: "2024-02-17", downloads: 5992, }, { day: "2024-02-18", downloads: 6255, }, { day: "2024-02-19", downloads: 35208, }, { day: "2024-02-20", downloads: 46191, }, { day: "2024-02-21", downloads: 46609, }, { day: "2024-02-22", downloads: 45749, }, { day: "2024-02-23", downloads: 40484, }, { day: "2024-02-24", downloads: 6768, }, { day: "2024-02-25", downloads: 5713, }, { day: "2024-02-26", downloads: 43896, }, { day: "2024-02-27", downloads: 47491, }, { day: "2024-02-28", downloads: 47424, }, { downloads: 49239, day: "2024-02-29", }, { downloads: 39884, day: "2024-03-01", }, { downloads: 6220, day: "2024-03-02", }, { downloads: 6061, day: "2024-03-03", }, { downloads: 46081, day: "2024-03-04", }, { downloads: 49144, day: "2024-03-05", }, { downloads: 48793, day: "2024-03-06", }, { downloads: 47389, day: "2024-03-07", }, { downloads: 39255, day: "2024-03-08", }, { downloads: 6167, day: "2024-03-09", }, { downloads: 5542, day: "2024-03-10", }, { downloads: 44753, day: "2024-03-11", }, { downloads: 48370, day: "2024-03-12", }, { downloads: 49478, day: "2024-03-13", }, { downloads: 50510, day: "2024-03-14", }, { downloads: 43521, day: "2024-03-15", }, { downloads: 7275, day: "2024-03-16", }, { downloads: 6394, day: "2024-03-17", }, { downloads: 48566, day: "2024-03-18", }, { downloads: 49237, day: "2024-03-19", }, { downloads: 49200, day: "2024-03-20", }, { downloads: 49033, day: "2024-03-21", }, { downloads: 41682, day: "2024-03-22", }, { downloads: 6968, day: "2024-03-23", }, { downloads: 6719, day: "2024-03-24", }, { downloads: 44236, day: "2024-03-25", }, { downloads: 33246, day: "2024-03-26", }, { downloads: 49165, day: "2024-03-27", }, { downloads: 45089, day: "2024-03-28", }, { downloads: 27262, day: "2024-03-29", }, { downloads: 5361, day: "2024-03-30", }, { downloads: 4404, day: "2024-03-31", }, { downloads: 34165, day: "2024-04-01", }, { downloads: 48032, day: "2024-04-02", }, { downloads: 48322, day: "2024-04-03", }, { downloads: 46982, day: "2024-04-04", }, { downloads: 38882, day: "2024-04-05", }, { downloads: 5921, day: "2024-04-06", }, { downloads: 5241, day: "2024-04-07", }, { downloads: 42581, day: "2024-04-08", }, { downloads: 46718, day: "2024-04-09", }, { downloads: 45241, day: "2024-04-10", }, { downloads: 45361, day: "2024-04-11", }, { downloads: 40250, day: "2024-04-12", }, { downloads: 5674, day: "2024-04-13", }, { downloads: 5593, day: "2024-04-14", }, { downloads: 45283, day: "2024-04-15", }, { downloads: 48065, day: "2024-04-16", }, { downloads: 46433, day: "2024-04-17", }, { downloads: 46132, day: "2024-04-18", }, { downloads: 41685, day: "2024-04-19", }, { downloads: 5661, day: "2024-04-20", }, { downloads: 5183, day: "2024-04-21", }, { downloads: 45976, day: "2024-04-22", }, { downloads: 50162, day: "2024-04-23", }, { downloads: 47458, day: "2024-04-24", }, { downloads: 46622, day: "2024-04-25", }, { downloads: 41179, day: "2024-04-26", }, { downloads: 5984, day: "2024-04-27", }, { downloads: 5298, day: "2024-04-28", }, { downloads: 48055, day: "2024-04-29", }, { downloads: 46840, day: "2024-04-30", }, { downloads: 33984, day: "2024-05-01", }, { downloads: 46678, day: "2024-05-02", }, { downloads: 39210, day: "2024-05-03", }, { downloads: 5899, day: "2024-05-04", }, { downloads: 5540, day: "2024-05-05", }, { downloads: 42105, day: "2024-05-06", }, { downloads: 45909, day: "2024-05-07", }, { downloads: 42852, day: "2024-05-08", }, { downloads: 38213, day: "2024-05-09", }, { downloads: 35724, day: "2024-05-10", }, { downloads: 5303, day: "2024-05-11", }, { downloads: 5558, day: "2024-05-12", }, { downloads: 44588, day: "2024-05-13", }, { downloads: 46534, day: "2024-05-14", }, { downloads: 47102, day: "2024-05-15", }, { downloads: 46515, day: "2024-05-16", }, { downloads: 38810, day: "2024-05-17", }, { downloads: 5013, day: "2024-05-18", }, { downloads: 5954, day: "2024-05-19", }, { downloads: 37309, day: "2024-05-20", }, { downloads: 44657, day: "2024-05-21", }, { downloads: 46921, day: "2024-05-22", }, { downloads: 47061, day: "2024-05-23", }, { downloads: 38988, day: "2024-05-24", }, { downloads: 5276, day: "2024-05-25", }, { downloads: 4754, day: "2024-05-26", }, { downloads: 30522, day: "2024-05-27", }, { downloads: 45589, day: "2024-05-28", }, { downloads: 47540, day: "2024-05-29", }, { downloads: 43133, day: "2024-05-30", }, { downloads: 38887, day: "2024-05-31", }, { downloads: 6145, day: "2024-06-01", }, { downloads: 5426, day: "2024-06-02", }, { downloads: 44660, day: "2024-06-03", }, { downloads: 47120, day: "2024-06-04", }, { downloads: 46757, day: "2024-06-05", }, { downloads: 46916, day: "2024-06-06", }, { downloads: 40849, day: "2024-06-07", }, { downloads: 5565, day: "2024-06-08", }, { downloads: 6179, day: "2024-06-09", }, { downloads: 43356, day: "2024-06-10", }, { downloads: 48334, day: "2024-06-11", }, { downloads: 47498, day: "2024-06-12", }, { downloads: 45685, day: "2024-06-13", }, { downloads: 38268, day: "2024-06-14", }, { downloads: 5606, day: "2024-06-15", }, { downloads: 5519, day: "2024-06-16", }, { downloads: 41621, day: "2024-06-17", }, { downloads: 45827, day: "2024-06-18", }, { downloads: 37623, day: "2024-06-19", }, { downloads: 43190, day: "2024-06-20", }, { downloads: 36501, day: "2024-06-21", }, { downloads: 5143, day: "2024-06-22", }, { downloads: 5799, day: "2024-06-23", }, { downloads: 43650, day: "2024-06-24", }, { downloads: 45294, day: "2024-06-25", }, { downloads: 44611, day: "2024-06-26", }, { downloads: 41220, day: "2024-06-27", }, { downloads: 34980, day: "2024-06-28", }, { downloads: 5426, day: "2024-06-29", }, { downloads: 5282, day: "2024-06-30", }, { downloads: 40917, day: "2024-07-01", }, { downloads: 43207, day: "2024-07-02", }, { downloads: 43875, day: "2024-07-03", }, { downloads: 32981, day: "2024-07-04", }, { downloads: 34079, day: "2024-07-05", }, { downloads: 4986, day: "2024-07-06", }, { downloads: 4925, day: "2024-07-07", }, { downloads: 40505, day: "2024-07-08", }, { downloads: 44622, day: "2024-07-09", }, { downloads: 45252, day: "2024-07-10", }, { downloads: 46270, day: "2024-07-11", }, { downloads: 38030, day: "2024-07-12", }, { downloads: 5732, day: "2024-07-13", }, { downloads: 5599, day: "2024-07-14", }, { downloads: 42252, day: "2024-07-15", }, { downloads: 48567, day: "2024-07-16", }, { downloads: 46149, day: "2024-07-17", }, { downloads: 45142, day: "2024-07-18", }, { downloads: 39962, day: "2024-07-19", }, { downloads: 5959, day: "2024-07-20", }, { downloads: 5693, day: "2024-07-21", }, { downloads: 44320, day: "2024-07-22", }, { downloads: 46739, day: "2024-07-23", }, { downloads: 45086, day: "2024-07-24", }, { downloads: 43962, day: "2024-07-25", }, { downloads: 38304, day: "2024-07-26", }, { downloads: 6148, day: "2024-07-27", }, { downloads: 5211, day: "2024-07-28", }, { downloads: 43683, day: "2024-07-29", }, { downloads: 46547, day: "2024-07-30", }, { downloads: 45681, day: "2024-07-31", }, { downloads: 45254, day: "2024-08-01", }, { downloads: 37873, day: "2024-08-02", }, { downloads: 5173, day: "2024-08-03", }, { downloads: 4845, day: "2024-08-04", }, { downloads: 39743, day: "2024-08-05", }, { downloads: 46228, day: "2024-08-06", }, { downloads: 45960, day: "2024-08-07", }, { downloads: 44605, day: "2024-08-08", }, { downloads: 38148, day: "2024-08-09", }, { downloads: 5604, day: "2024-08-10", }, { downloads: 5359, day: "2024-08-11", }, { downloads: 42823, day: "2024-08-12", }, { downloads: 45127, day: "2024-08-13", }, { downloads: 44558, day: "2024-08-14", }, { downloads: 35090, day: "2024-08-15", }, { downloads: 35105, day: "2024-08-16", }, { downloads: 5786, day: "2024-08-17", }, { downloads: 5216, day: "2024-08-18", }, { downloads: 41373, day: "2024-08-19", }, { downloads: 47721, day: "2024-08-20", }, { downloads: 44648, day: "2024-08-21", }, { downloads: 45633, day: "2024-08-22", }, { downloads: 38839, day: "2024-08-23", }, { downloads: 5329, day: "2024-08-24", }, { downloads: 6474, day: "2024-08-25", }, { downloads: 42413, day: "2024-08-26", }, { downloads: 47317, day: "2024-08-27", }, { downloads: 47835, day: "2024-08-28", }, { downloads: 47226, day: "2024-08-29", }, { downloads: 38174, day: "2024-08-30", }, { downloads: 5673, day: "2024-08-31", }, { downloads: 7728, day: "2024-09-01", }, { downloads: 30962, day: "2024-09-02", }, { downloads: 45510, day: "2024-09-03", }, { downloads: 46652, day: "2024-09-04", }, { downloads: 46372, day: "2024-09-05", }, { downloads: 40206, day: "2024-09-06", }, { downloads: 5808, day: "2024-09-07", }, { downloads: 5342, day: "2024-09-08", }, { downloads: 44284, day: "2024-09-09", }, { downloads: 44202, day: "2024-09-10", }, { downloads: 46610, day: "2024-09-11", }, { downloads: 47516, day: "2024-09-12", }, { downloads: 38615, day: "2024-09-13", }, { downloads: 6077, day: "2024-09-14", }, { downloads: 6061, day: "2024-09-15", }, { downloads: 44724, day: "2024-09-16", }, { downloads: 47418, day: "2024-09-17", }, { downloads: 46723, day: "2024-09-18", }, { downloads: 46057, day: "2024-09-19", }, { downloads: 40847, day: "2024-09-20", }, { downloads: 5834, day: "2024-09-21", }, { downloads: 6161, day: "2024-09-22", }, { downloads: 44940, day: "2024-09-23", }, { downloads: 47685, day: "2024-09-24", }, { downloads: 47290, day: "2024-09-25", }, { downloads: 46442, day: "2024-09-26", }, { downloads: 39081, day: "2024-09-27", }, { downloads: 5223, day: "2024-09-28", }, { downloads: 6654, day: "2024-09-29", }, { downloads: 44609, day: "2024-09-30", }, { downloads: 44796, day: "2024-10-01", }, ], }; export default data; ================================================ FILE: website/src/pages/_components/data/update-downloads.js ================================================ const fs = require("fs"); const path = require("path"); const data = d().victory; const downloads = Object.keys(data).map((day) => ({ day, downloads: data[day], })); const contents = ` const data = { data: ${JSON.stringify(downloads, null, 2)} }; export default data; `; fs.writeFileSync( path.resolve(__dirname, "..", "downloads.js"), contents, "utf8", ); // HACK: data from npm stats function d() { return { victory: { "2015-11-28": 3, "2015-11-29": 4, "2015-11-30": 5, "2015-12-01": 4, "2015-12-02": 2, "2015-12-03": 6, "2015-12-04": 37, "2015-12-05": 14, "2015-12-06": 2, "2015-12-07": 14, "2015-12-08": 4, "2015-12-09": 13, "2015-12-10": 18, "2015-12-11": 10, "2015-12-12": 6, "2015-12-13": 7, "2015-12-14": 7, "2015-12-15": 14, "2015-12-16": 11, "2015-12-17": 12, "2015-12-18": 75, "2015-12-19": 14, "2015-12-20": 13, "2015-12-21": 20, "2015-12-22": 13, "2015-12-23": 9, "2015-12-24": 4, "2015-12-25": 3, "2015-12-26": 14, "2015-12-27": 1, "2015-12-28": 11, "2015-12-29": 36, "2015-12-30": 95, "2015-12-31": 12, "2016-01-01": 12, "2016-01-02": 15, "2016-01-03": 14, "2016-01-04": 35, "2016-01-05": 19, "2016-01-06": 33, "2016-01-07": 47, "2016-01-08": 29, "2016-01-09": 33, "2016-01-10": 19, "2016-01-11": 48, "2016-01-12": 38, "2016-01-13": 38, "2016-01-14": 21, "2016-01-15": 37, "2016-01-16": 45, "2016-01-17": 33, "2016-01-18": 14, "2016-01-19": 33, "2016-01-20": 48, "2016-01-21": 38, "2016-01-22": 36, "2016-01-23": 16, "2016-01-24": 48, "2016-01-25": 43, "2016-01-26": 61, "2016-01-27": 106, "2016-01-28": 73, "2016-01-29": 43, "2016-01-30": 34, "2016-01-31": 25, "2016-02-01": 124, "2016-02-02": 78, "2016-02-03": 64, "2016-02-04": 47, "2016-02-05": 57, "2016-02-06": 23, "2016-02-07": 27, "2016-02-08": 54, "2016-02-09": 68, "2016-02-10": 94, "2016-02-11": 54, "2016-02-12": 72, "2016-02-13": 4, "2016-02-14": 9, "2016-02-15": 53, "2016-02-16": 62, "2016-02-17": 74, "2016-02-18": 103, "2016-02-19": 70, "2016-02-20": 27, "2016-02-21": 41, "2016-02-22": 93, "2016-02-23": 124, "2016-02-24": 108, "2016-02-25": 72, "2016-02-26": 95, "2016-02-27": 36, "2016-02-28": 9, "2016-02-29": 118, "2016-03-01": 98, "2016-03-02": 112, "2016-03-03": 133, "2016-03-04": 105, "2016-03-05": 27, "2016-03-06": 34, "2016-03-07": 176, "2016-03-08": 134, "2016-03-09": 117, "2016-03-10": 89, "2016-03-11": 65, "2016-03-12": 8, "2016-03-13": 10, "2016-03-14": 63, "2016-03-15": 34, "2016-03-16": 66, "2016-03-17": 146, "2016-03-18": 63, "2016-03-19": 30, "2016-03-20": 19, "2016-03-21": 56, "2016-03-22": 49, "2016-03-23": 78, "2016-03-24": 61, "2016-03-25": 21, "2016-03-26": 12, "2016-03-27": 24, "2016-03-28": 68, "2016-03-29": 92, "2016-03-30": 77, "2016-03-31": 46, "2016-04-01": 47, "2016-04-02": 15, "2016-04-03": 6, "2016-04-04": 48, "2016-04-05": 29, "2016-04-06": 56, "2016-04-07": 47, "2016-04-08": 54, "2016-04-09": 8, "2016-04-10": 28, "2016-04-11": 88, "2016-04-12": 59, "2016-04-13": 73, "2016-04-14": 141, "2016-04-15": 222, "2016-04-16": 20, "2016-04-17": 11, "2016-04-18": 126, "2016-04-19": 160, "2016-04-20": 110, "2016-04-21": 80, "2016-04-22": 54, "2016-04-23": 39, "2016-04-24": 3, "2016-04-25": 59, "2016-04-26": 94, "2016-04-27": 61, "2016-04-28": 96, "2016-04-29": 64, "2016-04-30": 40, "2016-05-01": 9, "2016-05-02": 67, "2016-05-03": 145, "2016-05-04": 155, "2016-05-05": 102, "2016-05-06": 56, "2016-05-07": 34, "2016-05-08": 10, "2016-05-09": 65, "2016-05-10": 79, "2016-05-11": 79, "2016-05-12": 82, "2016-05-13": 231, "2016-05-14": 47, "2016-05-15": 40, "2016-05-16": 116, "2016-05-17": 180, "2016-05-18": 127, "2016-05-19": 145, "2016-05-20": 150, "2016-05-21": 44, "2016-05-22": 20, "2016-05-23": 90, "2016-05-24": 109, "2016-05-25": 105, "2016-05-26": 123, "2016-05-27": 161, "2016-05-28": 33, "2016-05-29": 20, "2016-05-30": 133, "2016-05-31": 148, "2016-06-01": 117, "2016-06-02": 347, "2016-06-03": 175, "2016-06-04": 44, "2016-06-05": 34, "2016-06-06": 188, "2016-06-07": 240, "2016-06-08": 252, "2016-06-09": 199, "2016-06-10": 138, "2016-06-11": 85, "2016-06-12": 106, "2016-06-13": 222, "2016-06-14": 187, "2016-06-15": 234, "2016-06-16": 212, "2016-06-17": 314, "2016-06-18": 76, "2016-06-19": 13, "2016-06-20": 171, "2016-06-21": 213, "2016-06-22": 360, "2016-06-23": 185, "2016-06-24": 144, "2016-06-25": 22, "2016-06-26": 25, "2016-06-27": 207, "2016-06-28": 575, "2016-06-29": 362, "2016-06-30": 358, "2016-07-01": 397, "2016-07-02": 33, "2016-07-03": 31, "2016-07-04": 96, "2016-07-05": 151, "2016-07-06": 315, "2016-07-07": 270, "2016-07-08": 213, "2016-07-09": 40, "2016-07-10": 24, "2016-07-11": 224, "2016-07-12": 199, "2016-07-13": 262, "2016-07-14": 287, "2016-07-15": 163, "2016-07-16": 47, "2016-07-17": 9, "2016-07-18": 173, "2016-07-19": 177, "2016-07-20": 321, "2016-07-21": 265, "2016-07-22": 104, "2016-07-23": 21, "2016-07-24": 26, "2016-07-25": 127, "2016-07-26": 172, "2016-07-27": 239, "2016-07-28": 196, "2016-07-29": 318, "2016-07-30": 25, "2016-07-31": 16, "2016-08-01": 383, "2016-08-02": 194, "2016-08-03": 418, "2016-08-04": 438, "2016-08-05": 385, "2016-08-06": 46, "2016-08-07": 29, "2016-08-08": 225, "2016-08-09": 241, "2016-08-10": 179, "2016-08-11": 146, "2016-08-12": 193, "2016-08-13": 62, "2016-08-14": 24, "2016-08-15": 217, "2016-08-16": 327, "2016-08-17": 317, "2016-08-18": 197, "2016-08-19": 420, "2016-08-20": 47, "2016-08-21": 25, "2016-08-22": 260, "2016-08-23": 321, "2016-08-24": 223, "2016-08-25": 237, "2016-08-26": 302, "2016-08-27": 33, "2016-08-28": 57, "2016-08-29": 294, "2016-08-30": 324, "2016-08-31": 283, "2016-09-01": 203, "2016-09-02": 237, "2016-09-03": 51, "2016-09-04": 26, "2016-09-05": 154, "2016-09-06": 230, "2016-09-07": 315, "2016-09-08": 248, "2016-09-09": 394, "2016-09-10": 35, "2016-09-11": 48, "2016-09-12": 188, "2016-09-13": 303, "2016-09-14": 270, "2016-09-15": 555, "2016-09-16": 256, "2016-09-17": 43, "2016-09-18": 38, "2016-09-19": 215, "2016-09-20": 235, "2016-09-21": 298, "2016-09-22": 277, "2016-09-23": 306, "2016-09-24": 31, "2016-09-25": 32, "2016-09-26": 225, "2016-09-27": 321, "2016-09-28": 275, "2016-09-29": 411, "2016-09-30": 641, "2016-10-01": 48, "2016-10-02": 36, "2016-10-03": 252, "2016-10-04": 349, "2016-10-05": 303, "2016-10-06": 415, "2016-10-07": 293, "2016-10-08": 47, "2016-10-09": 48, "2016-10-10": 232, "2016-10-11": 263, "2016-10-12": 331, "2016-10-13": 379, "2016-10-14": 654, "2016-10-15": 89, "2016-10-16": 57, "2016-10-17": 581, "2016-10-18": 379, "2016-10-19": 340, "2016-10-20": 342, "2016-10-21": 431, "2016-10-22": 94, "2016-10-23": 100, "2016-10-24": 444, "2016-10-25": 523, "2016-10-26": 635, "2016-10-27": 487, "2016-10-28": 669, "2016-10-29": 117, "2016-10-30": 123, "2016-10-31": 643, "2016-11-01": 578, "2016-11-02": 504, "2016-11-03": 431, "2016-11-04": 389, "2016-11-05": 92, "2016-11-06": 70, "2016-11-07": 283, "2016-11-08": 408, "2016-11-09": 720, "2016-11-10": 712, "2016-11-11": 701, "2016-11-12": 190, "2016-11-13": 135, "2016-11-14": 521, "2016-11-15": 562, "2016-11-16": 491, "2016-11-17": 619, "2016-11-18": 443, "2016-11-19": 168, "2016-11-20": 88, "2016-11-21": 388, "2016-11-22": 542, "2016-11-23": 504, "2016-11-24": 275, "2016-11-25": 197, "2016-11-26": 55, "2016-11-27": 50, "2016-11-28": 493, "2016-11-29": 642, "2016-11-30": 498, "2016-12-01": 472, "2016-12-02": 556, "2016-12-03": 265, "2016-12-04": 117, "2016-12-05": 527, "2016-12-06": 667, "2016-12-07": 601, "2016-12-08": 629, "2016-12-09": 689, "2016-12-10": 139, "2016-12-11": 80, "2016-12-12": 746, "2016-12-13": 843, "2016-12-14": 1045, "2016-12-15": 681, "2016-12-16": 671, "2016-12-17": 167, "2016-12-18": 108, "2016-12-19": 593, "2016-12-20": 697, "2016-12-21": 641, "2016-12-22": 645, "2016-12-23": 341, "2016-12-24": 77, "2016-12-25": 52, "2016-12-26": 151, "2016-12-27": 287, "2016-12-28": 294, "2016-12-29": 297, "2016-12-30": 163, "2016-12-31": 39, "2017-01-01": 70, "2017-01-02": 178, "2017-01-03": 475, "2017-01-04": 1037, "2017-01-05": 744, "2017-01-06": 681, "2017-01-07": 162, "2017-01-08": 109, "2017-01-09": 583, "2017-01-10": 718, "2017-01-11": 840, "2017-01-12": 730, "2017-01-13": 653, "2017-01-14": 410, "2017-01-15": 185, "2017-01-16": 450, "2017-01-17": 588, "2017-01-18": 786, "2017-01-19": 692, "2017-01-20": 648, "2017-01-21": 159, "2017-01-22": 114, "2017-01-23": 512, "2017-01-24": 1197, "2017-01-25": 731, "2017-01-26": 770, "2017-01-27": 679, "2017-01-28": 146, "2017-01-29": 155, "2017-01-30": 592, "2017-01-31": 1095, "2017-02-01": 775, "2017-02-02": 658, "2017-02-03": 670, "2017-02-04": 634, "2017-02-05": 110, "2017-02-06": 1141, "2017-02-07": 698, "2017-02-08": 833, "2017-02-09": 830, "2017-02-10": 1215, "2017-02-11": 120, "2017-02-12": 103, "2017-02-13": 724, "2017-02-14": 738, "2017-02-15": 686, "2017-02-16": 708, "2017-02-17": 1054, "2017-02-18": 154, "2017-02-19": 108, "2017-02-20": 540, "2017-02-21": 782, "2017-02-22": 899, "2017-02-23": 928, "2017-02-24": 925, "2017-02-25": 155, "2017-02-26": 163, "2017-02-27": 1259, "2017-02-28": 725, "2017-03-01": 848, "2017-03-02": 724, "2017-03-03": 859, "2017-03-04": 213, "2017-03-05": 149, "2017-03-06": 836, "2017-03-07": 861, "2017-03-08": 1001, "2017-03-09": 1027, "2017-03-10": 969, "2017-03-11": 157, "2017-03-12": 142, "2017-03-13": 987, "2017-03-14": 937, "2017-03-15": 1350, "2017-03-16": 1318, "2017-03-17": 767, "2017-03-18": 159, "2017-03-19": 102, "2017-03-20": 991, "2017-03-21": 1489, "2017-03-22": 1040, "2017-03-23": 964, "2017-03-24": 1036, "2017-03-25": 138, "2017-03-26": 167, "2017-03-27": 1207, "2017-03-28": 1466, "2017-03-29": 1264, "2017-03-30": 1037, "2017-03-31": 1252, "2017-04-01": 127, "2017-04-02": 200, "2017-04-03": 1140, "2017-04-04": 1600, "2017-04-05": 1399, "2017-04-06": 1366, "2017-04-07": 1307, "2017-04-08": 263, "2017-04-09": 153, "2017-04-10": 1338, "2017-04-11": 1406, "2017-04-12": 1181, "2017-04-13": 1252, "2017-04-14": 843, "2017-04-15": 172, "2017-04-16": 121, "2017-04-17": 836, "2017-04-18": 1226, "2017-04-19": 1317, "2017-04-20": 1497, "2017-04-21": 1208, "2017-04-22": 162, "2017-04-23": 126, "2017-04-24": 1339, "2017-04-25": 1510, "2017-04-26": 1417, "2017-04-27": 1323, "2017-04-28": 1970, "2017-04-29": 212, "2017-04-30": 180, "2017-05-01": 667, "2017-05-02": 1698, "2017-05-03": 1126, "2017-05-04": 1274, "2017-05-05": 1142, "2017-05-06": 179, "2017-05-07": 147, "2017-05-08": 1097, "2017-05-09": 1207, "2017-05-10": 1334, "2017-05-11": 1183, "2017-05-12": 1344, "2017-05-13": 232, "2017-05-14": 208, "2017-05-15": 976, "2017-05-16": 1078, "2017-05-17": 1033, "2017-05-18": 1257, "2017-05-19": 1069, "2017-05-20": 178, "2017-05-21": 112, "2017-05-22": 1036, "2017-05-23": 1084, "2017-05-24": 1646, "2017-05-25": 899, "2017-05-26": 1028, "2017-05-27": 293, "2017-05-28": 163, "2017-05-29": 619, "2017-05-30": 1051, "2017-05-31": 1318, "2017-06-01": 1195, "2017-06-02": 862, "2017-06-03": 903, "2017-06-04": 151, "2017-06-05": 872, "2017-06-06": 1710, "2017-06-07": 1504, "2017-06-08": 1267, "2017-06-09": 1169, "2017-06-10": 375, "2017-06-11": 190, "2017-06-12": 1035, "2017-06-13": 1254, "2017-06-14": 1236, "2017-06-15": 926, "2017-06-16": 1010, "2017-06-17": 217, "2017-06-18": 125, "2017-06-19": 1088, "2017-06-20": 1174, "2017-06-21": 1328, "2017-06-22": 1264, "2017-06-23": 1075, "2017-06-24": 271, "2017-06-25": 174, "2017-06-26": 1251, "2017-06-27": 1298, "2017-06-28": 1331, "2017-06-29": 1214, "2017-06-30": 1689, "2017-07-01": 212, "2017-07-02": 170, "2017-07-03": 819, "2017-07-04": 702, "2017-07-05": 1315, "2017-07-06": 1531, "2017-07-07": 1256, "2017-07-08": 230, "2017-07-09": 221, "2017-07-10": 1287, "2017-07-11": 1202, "2017-07-12": 1295, "2017-07-13": 2118, "2017-07-14": 1447, "2017-07-15": 348, "2017-07-16": 273, "2017-07-17": 1294, "2017-07-18": 1396, "2017-07-19": 1509, "2017-07-20": 1332, "2017-07-21": 1275, "2017-07-22": 216, "2017-07-23": 202, "2017-07-24": 2073, "2017-07-25": 1470, "2017-07-26": 1429, "2017-07-27": 1474, "2017-07-28": 1265, "2017-07-29": 196, "2017-07-30": 194, "2017-07-31": 1168, "2017-08-01": 1413, "2017-08-02": 1578, "2017-08-03": 1459, "2017-08-04": 1847, "2017-08-05": 581, "2017-08-06": 302, "2017-08-07": 1464, "2017-08-08": 2344, "2017-08-09": 1595, "2017-08-10": 1441, "2017-08-11": 1446, "2017-08-12": 323, "2017-08-13": 200, "2017-08-14": 1461, "2017-08-15": 1558, "2017-08-16": 1649, "2017-08-17": 2539, "2017-08-18": 1570, "2017-08-19": 318, "2017-08-20": 959, "2017-08-21": 1389, "2017-08-22": 1609, "2017-08-23": 1585, "2017-08-24": 1752, "2017-08-25": 1426, "2017-08-26": 292, "2017-08-27": 232, "2017-08-28": 1431, "2017-08-29": 1767, "2017-08-30": 1554, "2017-08-31": 1702, "2017-09-01": 1430, "2017-09-02": 225, "2017-09-03": 239, "2017-09-04": 865, "2017-09-05": 2541, "2017-09-06": 1884, "2017-09-07": 1536, "2017-09-08": 1414, "2017-09-09": 847, "2017-09-10": 790, "2017-09-11": 1669, "2017-09-12": 2323, "2017-09-13": 2511, "2017-09-14": 2041, "2017-09-15": 1738, "2017-09-16": 408, "2017-09-17": 303, "2017-09-18": 1606, "2017-09-19": 1852, "2017-09-20": 1878, "2017-09-21": 1826, "2017-09-22": 1718, "2017-09-23": 453, "2017-09-24": 292, "2017-09-25": 1834, "2017-09-26": 1886, "2017-09-27": 2829, "2017-09-28": 1844, "2017-09-29": 1828, "2017-09-30": 1208, "2017-10-01": 433, "2017-10-02": 1781, "2017-10-03": 1822, "2017-10-04": 2050, "2017-10-05": 2734, "2017-10-06": 1741, "2017-10-07": 469, "2017-10-08": 571, "2017-10-09": 1524, "2017-10-10": 1785, "2017-10-11": 1945, "2017-10-12": 1611, "2017-10-13": 1644, "2017-10-14": 326, "2017-10-15": 344, "2017-10-16": 1639, "2017-10-17": 1948, "2017-10-18": 2073, "2017-10-19": 1658, "2017-10-20": 2751, "2017-10-21": 1382, "2017-10-22": 540, "2017-10-23": 3575, "2017-10-24": 4549, "2017-10-25": 6032, "2017-10-26": 4673, "2017-10-27": 4633, "2017-10-28": 881, "2017-10-29": 601, "2017-10-30": 3815, "2017-10-31": 6860, "2017-11-01": 7947, "2017-11-02": 5549, "2017-11-03": 5317, "2017-11-04": 1301, "2017-11-05": 872, "2017-11-06": 5350, "2017-11-07": 6889, "2017-11-08": 7629, "2017-11-09": 6780, "2017-11-10": 5675, "2017-11-11": 1371, "2017-11-12": 707, "2017-11-13": 3836, "2017-11-14": 4810, "2017-11-15": 5942, "2017-11-16": 6317, "2017-11-17": 4852, "2017-11-18": 1204, "2017-11-19": 546, "2017-11-20": 4443, "2017-11-21": 4247, "2017-11-22": 5021, "2017-11-23": 1875, "2017-11-24": 1260, "2017-11-25": 324, "2017-11-26": 401, "2017-11-27": 4427, "2017-11-28": 6964, "2017-11-29": 5950, "2017-11-30": 6022, "2017-12-01": 5609, "2017-12-02": 1403, "2017-12-03": 278, "2017-12-04": 3381, "2017-12-05": 5355, "2017-12-06": 5407, "2017-12-07": 7059, "2017-12-08": 5473, "2017-12-09": 1557, "2017-12-10": 449, "2017-12-11": 4219, "2017-12-12": 7777, "2017-12-13": 6017, "2017-12-14": 4769, "2017-12-15": 4091, "2017-12-16": 1142, "2017-12-17": 390, "2017-12-18": 5491, "2017-12-19": 4739, "2017-12-20": 4151, "2017-12-21": 4487, "2017-12-22": 3059, "2017-12-23": 414, "2017-12-24": 344, "2017-12-25": 215, "2017-12-26": 620, "2017-12-27": 1267, "2017-12-28": 1134, "2017-12-29": 884, "2017-12-30": 266, "2017-12-31": 187, "2018-01-01": 211, "2018-01-02": 1914, "2018-01-03": 2492, "2018-01-04": 2596, "2018-01-05": 2442, "2018-01-06": 699, "2018-01-07": 306, "2018-01-08": 2415, "2018-01-09": 3682, "2018-01-10": 2527, "2018-01-11": 3055, "2018-01-12": 2940, "2018-01-13": 727, "2018-01-14": 362, "2018-01-15": 2051, "2018-01-16": 2906, "2018-01-17": 2859, "2018-01-18": 3230, "2018-01-19": 2559, "2018-01-20": 754, "2018-01-21": 685, "2018-01-22": 3913, "2018-01-23": 6981, "2018-01-24": 6739, "2018-01-25": 6194, "2018-01-26": 5618, "2018-01-27": 1357, "2018-01-28": 725, "2018-01-29": 3855, "2018-01-30": 6767, "2018-01-31": 7662, "2018-02-01": 7036, "2018-02-02": 6651, "2018-02-03": 2133, "2018-02-04": 785, "2018-02-05": 7072, "2018-02-06": 8457, "2018-02-07": 6671, "2018-02-08": 7440, "2018-02-09": 4771, "2018-02-10": 1108, "2018-02-11": 560, "2018-02-12": 5410, "2018-02-13": 6070, "2018-02-14": 5850, "2018-02-15": 8512, "2018-02-16": 5238, "2018-02-17": 1684, "2018-02-18": 761, "2018-02-19": 2615, "2018-02-20": 4697, "2018-02-21": 6387, "2018-02-22": 7082, "2018-02-23": 5738, "2018-02-24": 1420, "2018-02-25": 816, "2018-02-26": 5244, "2018-02-27": 6326, "2018-02-28": 6301, "2018-03-01": 6620, "2018-03-02": 6455, "2018-03-03": 3534, "2018-03-04": 1809, "2018-03-05": 4757, "2018-03-06": 6803, "2018-03-07": 7059, "2018-03-08": 6238, "2018-03-09": 6367, "2018-03-10": 2295, "2018-03-11": 1590, "2018-03-12": 6052, "2018-03-13": 5488, "2018-03-14": 7570, "2018-03-15": 5426, "2018-03-16": 7156, "2018-03-17": 1493, "2018-03-18": 1635, "2018-03-19": 5708, "2018-03-20": 8704, "2018-03-21": 7645, "2018-03-22": 8335, "2018-03-23": 5155, "2018-03-24": 1777, "2018-03-25": 830, "2018-03-26": 4850, "2018-03-27": 8277, "2018-03-28": 9242, "2018-03-29": 8332, "2018-03-30": 6233, "2018-03-31": 1448, "2018-04-01": 621, "2018-04-02": 6229, "2018-04-03": 8125, "2018-04-04": 6551, "2018-04-05": 6030, "2018-04-06": 6782, "2018-04-07": 1214, "2018-04-08": 977, "2018-04-09": 5590, "2018-04-10": 7797, "2018-04-11": 8438, "2018-04-12": 8590, "2018-04-13": 7080, "2018-04-14": 1357, "2018-04-15": 1039, "2018-04-16": 5618, "2018-04-17": 7038, "2018-04-18": 8400, "2018-04-19": 8058, "2018-04-20": 7715, "2018-04-21": 1704, "2018-04-22": 1605, "2018-04-23": 5176, "2018-04-24": 6399, "2018-04-25": 6148, "2018-04-26": 6690, "2018-04-27": 6312, "2018-04-28": 1271, "2018-04-29": 1201, "2018-04-30": 5597, "2018-05-01": 4773, "2018-05-02": 6543, "2018-05-03": 5549, "2018-05-04": 5652, "2018-05-05": 1275, "2018-05-06": 1243, "2018-05-07": 4962, "2018-05-08": 4973, "2018-05-09": 4023, "2018-05-10": 3837, "2018-05-11": 4218, "2018-05-12": 601, "2018-05-13": 481, "2018-05-14": 4296, "2018-05-15": 4020, "2018-05-16": 5063, "2018-05-17": 6956, "2018-05-18": 8227, "2018-05-19": 1879, "2018-05-20": 512, "2018-05-21": 4724, "2018-05-22": 4200, "2018-05-23": 4330, "2018-05-24": 4172, "2018-05-25": 2876, "2018-05-26": 231, "2018-05-27": 229, "2018-05-28": 970, "2018-05-29": 1834, "2018-05-30": 3401, "2018-05-31": 3790, "2018-06-01": 3412, "2018-06-02": 673, "2018-06-03": 384, "2018-06-04": 3588, "2018-06-05": 3834, "2018-06-06": 5116, "2018-06-07": 4671, "2018-06-08": 3407, "2018-06-09": 568, "2018-06-10": 496, "2018-06-11": 3534, "2018-06-12": 4143, "2018-06-13": 3974, "2018-06-14": 3946, "2018-06-15": 4943, "2018-06-16": 579, "2018-06-17": 474, "2018-06-18": 5291, "2018-06-19": 7801, "2018-06-20": 8307, "2018-06-21": 7982, "2018-06-22": 7059, "2018-06-23": 1115, "2018-06-24": 1464, "2018-06-25": 5828, "2018-06-26": 5157, "2018-06-27": 4471, "2018-06-28": 4240, "2018-06-29": 4926, "2018-06-30": 1173, "2018-07-01": 748, "2018-07-02": 4853, "2018-07-03": 5808, "2018-07-04": 2928, "2018-07-05": 4435, "2018-07-06": 6149, "2018-07-07": 1528, "2018-07-08": 598, "2018-07-09": 4376, "2018-07-10": 6020, "2018-07-11": 5696, "2018-07-12": 6153, "2018-07-13": 5077, "2018-07-14": 927, "2018-07-15": 448, "2018-07-16": 4003, "2018-07-17": 5905, "2018-07-18": 4686, "2018-07-19": 4443, "2018-07-20": 3998, "2018-07-21": 764, "2018-07-22": 734, "2018-07-23": 4390, "2018-07-24": 4102, "2018-07-25": 4234, "2018-07-26": 4099, "2018-07-27": 3841, "2018-07-28": 1403, "2018-07-29": 638, "2018-07-30": 4199, "2018-07-31": 4642, "2018-08-01": 4779, "2018-08-02": 4474, "2018-08-03": 3846, "2018-08-04": 1026, "2018-08-05": 773, "2018-08-06": 4880, "2018-08-07": 4831, "2018-08-08": 5363, "2018-08-09": 4851, "2018-08-10": 4640, "2018-08-11": 1083, "2018-08-12": 637, "2018-08-13": 4592, "2018-08-14": 4028, "2018-08-15": 5630, "2018-08-16": 4496, "2018-08-17": 4107, "2018-08-18": 856, "2018-08-19": 646, "2018-08-20": 3941, "2018-08-21": 5415, "2018-08-22": 4619, "2018-08-23": 5014, "2018-08-24": 4721, "2018-08-25": 1040, "2018-08-26": 709, "2018-08-27": 4289, "2018-08-28": 4826, "2018-08-29": 4676, "2018-08-30": 5856, "2018-08-31": 4563, "2018-09-01": 642, "2018-09-02": 782, "2018-09-03": 2812, "2018-09-04": 4800, "2018-09-05": 5509, "2018-09-06": 5294, "2018-09-07": 5088, "2018-09-08": 958, "2018-09-09": 763, "2018-09-10": 5647, "2018-09-11": 5910, "2018-09-12": 5907, "2018-09-13": 5246, "2018-09-14": 4963, "2018-09-15": 846, "2018-09-16": 722, "2018-09-17": 5702, "2018-09-18": 5422, "2018-09-19": 5933, "2018-09-20": 6065, "2018-09-21": 5221, "2018-09-22": 1208, "2018-09-23": 801, "2018-09-24": 6038, "2018-09-25": 5705, "2018-09-26": 7545, "2018-09-27": 6520, "2018-09-28": 5598, "2018-09-29": 820, "2018-09-30": 641, "2018-10-01": 5468, "2018-10-02": 5797, "2018-10-03": 6581, "2018-10-04": 6281, "2018-10-05": 5540, "2018-10-06": 785, "2018-10-07": 649, "2018-10-08": 4604, "2018-10-09": 5848, "2018-10-10": 5671, "2018-10-11": 5870, "2018-10-12": 5393, "2018-10-13": 829, "2018-10-14": 712, "2018-10-15": 5959, "2018-10-16": 7536, "2018-10-17": 5818, "2018-10-18": 5509, "2018-10-19": 5347, "2018-10-20": 845, "2018-10-21": 787, "2018-10-22": 4256, "2018-10-23": 6048, "2018-10-24": 6198, "2018-10-25": 6999, "2018-10-26": 6236, "2018-10-27": 1408, "2018-10-28": 909, "2018-10-29": 4912, "2018-10-30": 5795, "2018-10-31": 5548, "2018-11-01": 5143, "2018-11-02": 5515, "2018-11-03": 1330, "2018-11-04": 1217, "2018-11-05": 5680, "2018-11-06": 6727, "2018-11-07": 6633, "2018-11-08": 6633, "2018-11-09": 5769, "2018-11-10": 1931, "2018-11-11": 1633, "2018-11-12": 5538, "2018-11-13": 6509, "2018-11-14": 6413, "2018-11-15": 6038, "2018-11-16": 5552, "2018-11-17": 1222, "2018-11-18": 1671, "2018-11-19": 6140, "2018-11-20": 6349, "2018-11-21": 5798, "2018-11-22": 3995, "2018-11-23": 3441, "2018-11-24": 833, "2018-11-25": 896, "2018-11-26": 5919, "2018-11-27": 6722, "2018-11-28": 6369, "2018-11-29": 6378, "2018-11-30": 5657, "2018-12-01": 1195, "2018-12-02": 743, "2018-12-03": 5467, "2018-12-04": 5831, "2018-12-05": 6696, "2018-12-06": 6108, "2018-12-07": 5779, "2018-12-08": 897, "2018-12-09": 981, "2018-12-10": 5981, "2018-12-11": 6610, "2018-12-12": 7089, "2018-12-13": 6309, "2018-12-14": 6113, "2018-12-15": 1036, "2018-12-16": 976, "2018-12-17": 6081, "2018-12-18": 6544, "2018-12-19": 6581, "2018-12-20": 5759, "2018-12-21": 4976, "2018-12-22": 768, "2018-12-23": 357, "2018-12-24": 1190, "2018-12-25": 762, "2018-12-26": 1757, "2018-12-27": 2710, "2018-12-28": 2331, "2018-12-29": 574, "2018-12-30": 496, "2018-12-31": 1313, "2019-01-01": 584, "2019-01-02": 4097, "2019-01-03": 5156, "2019-01-04": 5287, "2019-01-05": 1550, "2019-01-06": 770, "2019-01-07": 6083, "2019-01-08": 7316, "2019-01-09": 7361, "2019-01-10": 6589, "2019-01-11": 6065, "2019-01-12": 1234, "2019-01-13": 777, "2019-01-14": 6487, "2019-01-15": 6983, "2019-01-16": 6797, "2019-01-17": 6923, "2019-01-18": 5947, "2019-01-19": 879, "2019-01-20": 860, "2019-01-21": 4918, "2019-01-22": 6807, "2019-01-23": 6987, "2019-01-24": 6859, "2019-01-25": 6373, "2019-01-26": 1241, "2019-01-27": 889, "2019-01-28": 6736, "2019-01-29": 8081, "2019-01-30": 7851, "2019-01-31": 7930, "2019-02-01": 6773, "2019-02-02": 1330, "2019-02-03": 1024, "2019-02-04": 6589, "2019-02-05": 7599, "2019-02-06": 7433, "2019-02-07": 7630, "2019-02-08": 7204, "2019-02-09": 1285, "2019-02-10": 1124, "2019-02-11": 7442, "2019-02-12": 7828, "2019-02-13": 7487, "2019-02-14": 7507, "2019-02-15": 6875, "2019-02-16": 1107, "2019-02-17": 946, "2019-02-18": 5972, "2019-02-19": 7389, "2019-02-20": 8001, "2019-02-21": 7766, "2019-02-22": 6721, "2019-02-23": 1654, "2019-02-24": 1264, "2019-02-25": 7087, "2019-02-26": 8629, "2019-02-27": 8059, "2019-02-28": 8784, "2019-03-01": 6905, "2019-03-02": 1194, "2019-03-03": 1099, "2019-03-04": 6462, "2019-03-05": 8892, "2019-03-06": 7016, "2019-03-07": 6823, "2019-03-08": 6134, "2019-03-09": 1166, "2019-03-10": 982, "2019-03-11": 7113, "2019-03-12": 9345, "2019-03-13": 9061, "2019-03-14": 8566, "2019-03-15": 6873, "2019-03-16": 971, "2019-03-17": 1145, "2019-03-18": 4916, "2019-03-19": 8099, "2019-03-20": 8327, "2019-03-21": 7607, "2019-03-22": 7096, "2019-03-23": 1247, "2019-03-24": 1086, "2019-03-25": 6877, "2019-03-26": 8001, "2019-03-27": 7844, "2019-03-28": 7327, "2019-03-29": 6237, "2019-03-30": 1665, "2019-03-31": 1098, "2019-04-01": 6794, "2019-04-02": 8300, "2019-04-03": 8124, "2019-04-04": 8019, "2019-04-05": 6419, "2019-04-06": 1270, "2019-04-07": 1027, "2019-04-08": 7117, "2019-04-09": 7751, "2019-04-10": 8231, "2019-04-11": 8084, "2019-04-12": 7041, "2019-04-13": 1296, "2019-04-14": 1315, "2019-04-15": 6645, "2019-04-16": 7763, "2019-04-17": 8020, "2019-04-18": 6994, "2019-04-19": 5056, "2019-04-20": 1202, "2019-04-21": 981, "2019-04-22": 5481, "2019-04-23": 7451, "2019-04-24": 7831, "2019-04-25": 7199, "2019-04-26": 6931, "2019-04-27": 1095, "2019-04-28": 1367, "2019-04-29": 6950, "2019-04-30": 7168, "2019-05-01": 5958, "2019-05-02": 7437, "2019-05-03": 7813, "2019-05-04": 1880, "2019-05-05": 977, "2019-05-06": 6387, "2019-05-07": 7783, "2019-05-08": 7592, "2019-05-09": 7870, "2019-05-10": 6915, "2019-05-11": 1476, "2019-05-12": 1068, "2019-05-13": 8013, "2019-05-14": 8696, "2019-05-15": 8476, "2019-05-16": 9274, "2019-05-17": 6872, "2019-05-18": 1073, "2019-05-19": 1119, "2019-05-20": 7004, "2019-05-21": 8720, "2019-05-22": 8184, "2019-05-23": 8670, "2019-05-24": 6742, "2019-05-25": 1306, "2019-05-26": 1308, "2019-05-27": 5080, "2019-05-28": 7611, "2019-05-29": 8854, "2019-05-30": 7248, "2019-05-31": 7158, "2019-06-01": 1313, "2019-06-02": 1132, "2019-06-03": 8199, "2019-06-04": 8517, "2019-06-05": 8383, "2019-06-06": 7889, "2019-06-07": 7549, "2019-06-08": 1123, "2019-06-09": 1082, "2019-06-10": 7658, "2019-06-11": 8377, "2019-06-12": 7601, "2019-06-13": 8047, "2019-06-14": 6791, "2019-06-15": 1013, "2019-06-16": 1129, "2019-06-17": 8086, "2019-06-18": 8814, "2019-06-19": 8850, "2019-06-20": 8872, "2019-06-21": 8155, "2019-06-22": 976, "2019-06-23": 1103, "2019-06-24": 9399, "2019-06-25": 9787, "2019-06-26": 9736, "2019-06-27": 9253, "2019-06-28": 8491, "2019-06-29": 1295, "2019-06-30": 952, "2019-07-01": 8370, "2019-07-02": 9632, "2019-07-03": 9369, "2019-07-04": 5893, "2019-07-05": 5263, "2019-07-06": 1099, "2019-07-07": 993, "2019-07-08": 7990, "2019-07-09": 9031, "2019-07-10": 8711, "2019-07-11": 8822, "2019-07-12": 7887, "2019-07-13": 1165, "2019-07-14": 1327, "2019-07-15": 8576, "2019-07-16": 10036, "2019-07-17": 9480, "2019-07-18": 9918, "2019-07-19": 8071, "2019-07-20": 1675, "2019-07-21": 1626, "2019-07-22": 8590, "2019-07-23": 9905, "2019-07-24": 9423, "2019-07-25": 9536, "2019-07-26": 8111, "2019-07-27": 1166, "2019-07-28": 1121, "2019-07-29": 9632, "2019-07-30": 8377, "2019-07-31": 9505, "2019-08-01": 8616, "2019-08-02": 8056, "2019-08-03": 1256, "2019-08-04": 1062, "2019-08-05": 9240, "2019-08-06": 9499, "2019-08-07": 10264, "2019-08-08": 10308, "2019-08-09": 8542, "2019-08-10": 1514, "2019-08-11": 1474, "2019-08-12": 9759, "2019-08-13": 10176, "2019-08-14": 10145, "2019-08-15": 8770, "2019-08-16": 8955, "2019-08-17": 1357, "2019-08-18": 1522, "2019-08-19": 9959, "2019-08-20": 10711, "2019-08-21": 10327, "2019-08-22": 11025, "2019-08-23": 9844, "2019-08-24": 1473, "2019-08-25": 1351, "2019-08-26": 10140, "2019-08-27": 10913, "2019-08-28": 10845, "2019-08-29": 11517, "2019-08-30": 9396, "2019-08-31": 1483, "2019-09-01": 1190, "2019-09-02": 5858, "2019-09-03": 9918, "2019-09-04": 10666, "2019-09-05": 9697, "2019-09-06": 8861, "2019-09-07": 1439, "2019-09-08": 1189, "2019-09-09": 10527, "2019-09-10": 14613, "2019-09-11": 13616, "2019-09-12": 13312, "2019-09-13": 11974, "2019-09-14": 2753, "2019-09-15": 3221, "2019-09-16": 13963, "2019-09-17": 15955, "2019-09-18": 14432, "2019-09-19": 14673, "2019-09-20": 13010, "2019-09-21": 2722, "2019-09-22": 2266, "2019-09-23": 13018, "2019-09-24": 15433, "2019-09-25": 16953, "2019-09-26": 19189, "2019-09-27": 16487, "2019-09-28": 4867, "2019-09-29": 4306, "2019-09-30": 15197, "2019-10-01": 18405, "2019-10-02": 14454, "2019-10-03": 14511, "2019-10-04": 12446, "2019-10-05": 1959, "2019-10-06": 1468, "2019-10-07": 13891, "2019-10-08": 15371, "2019-10-09": 13740, "2019-10-10": 13215, "2019-10-11": 12080, "2019-10-12": 1791, "2019-10-13": 1426, "2019-10-14": 10905, "2019-10-15": 13979, "2019-10-16": 15764, "2019-10-17": 15633, "2019-10-18": 12585, "2019-10-19": 1722, "2019-10-20": 1509, "2019-10-21": 13803, "2019-10-22": 16022, "2019-10-23": 17777, "2019-10-24": 18594, "2019-10-25": 14913, "2019-10-26": 1912, "2019-10-27": 1724, "2019-10-28": 15114, "2019-10-29": 16921, "2019-10-30": 16354, "2019-10-31": 16435, "2019-11-01": 13199, "2019-11-02": 2210, "2019-11-03": 1634, "2019-11-04": 15319, "2019-11-05": 19426, "2019-11-06": 19022, "2019-11-07": 19421, "2019-11-08": 16716, "2019-11-09": 3270, "2019-11-10": 2178, "2019-11-11": 13080, "2019-11-12": 18042, "2019-11-13": 19308, "2019-11-14": 19852, "2019-11-15": 17950, "2019-11-16": 3074, "2019-11-17": 2006, "2019-11-18": 16223, "2019-11-19": 21735, "2019-11-20": 21210, "2019-11-21": 23187, "2019-11-22": 19202, "2019-11-23": 3657, "2019-11-24": 2011, "2019-11-25": 18045, "2019-11-26": 18923, "2019-11-27": 17432, "2019-11-28": 9946, "2019-11-29": 8000, "2019-11-30": 2383, "2019-12-01": 2066, "2019-12-02": 17135, "2019-12-03": 21256, "2019-12-04": 18483, "2019-12-05": 18390, "2019-12-06": 16855, "2019-12-07": 3254, "2019-12-08": 2091, "2019-12-09": 16769, "2019-12-10": 18079, "2019-12-11": 17417, "2019-12-12": 18954, "2019-12-13": 18729, "2019-12-14": 2848, "2019-12-15": 1600, "2019-12-16": 16942, "2019-12-17": 18865, "2019-12-18": 17146, "2019-12-19": 17056, "2019-12-20": 15520, "2019-12-21": 2581, "2019-12-22": 1511, "2019-12-23": 7474, "2019-12-24": 3815, "2019-12-25": 1864, "2019-12-26": 3976, "2019-12-27": 4994, "2019-12-28": 1420, "2019-12-29": 1390, "2019-12-30": 6286, "2019-12-31": 4024, "2020-01-01": 1687, "2020-01-02": 10488, "2020-01-03": 10794, "2020-01-04": 2094, "2020-01-05": 1447, "2020-01-06": 13860, "2020-01-07": 15719, "2020-01-08": 15873, "2020-01-09": 16892, "2020-01-10": 14826, "2020-01-11": 2941, "2020-01-12": 1964, "2020-01-13": 13486, "2020-01-14": 17073, "2020-01-15": 20667, "2020-01-16": 18558, "2020-01-17": 15600, "2020-01-18": 2345, "2020-01-19": 2211, "2020-01-20": 12286, "2020-01-21": 16671, "2020-01-22": 17263, "2020-01-23": 17806, "2020-01-24": 17297, "2020-01-25": 2791, "2020-01-26": 1744, "2020-01-27": 15599, "2020-01-28": 19450, "2020-01-29": 19121, "2020-01-30": 19482, "2020-01-31": 16553, "2020-02-01": 2792, "2020-02-02": 2098, "2020-02-03": 17211, "2020-02-04": 20172, "2020-02-05": 21894, "2020-02-06": 19606, "2020-02-07": 19698, "2020-02-08": 2949, "2020-02-09": 1985, "2020-02-10": 17324, "2020-02-11": 22081, "2020-02-12": 20836, "2020-02-13": 22586, "2020-02-14": 25737, "2020-02-15": 3362, "2020-02-16": 2633, "2020-02-17": 14967, "2020-02-18": 20153, "2020-02-19": 23679, "2020-02-20": 27203, "2020-02-21": 20985, "2020-02-22": 3379, "2020-02-23": 2002, "2020-02-24": 19851, "2020-02-25": 22793, "2020-02-26": 21655, "2020-02-27": 21919, "2020-02-28": 22414, "2020-02-29": 3504, "2020-03-01": 2227, "2020-03-02": 20203, "2020-03-03": 21809, "2020-03-04": 22791, "2020-03-05": 22968, "2020-03-06": 20104, "2020-03-07": 3430, "2020-03-08": 2339, "2020-03-09": 18462, "2020-03-10": 19974, "2020-03-11": 20907, "2020-03-12": 20706, "2020-03-13": 17870, "2020-03-14": 3356, "2020-03-15": 2876, "2020-03-16": 18737, "2020-03-17": 23228, "2020-03-18": 22146, "2020-03-19": 21481, "2020-03-20": 20925, "2020-03-21": 3820, "2020-03-22": 2731, "2020-03-23": 20862, "2020-03-24": 22923, "2020-03-25": 22450, "2020-03-26": 24191, "2020-03-27": 21351, "2020-03-28": 4475, "2020-03-29": 3343, "2020-03-30": 20927, "2020-03-31": 26116, "2020-04-01": 24603, "2020-04-02": 23067, "2020-04-03": 23058, "2020-04-04": 4039, "2020-04-05": 3002, "2020-04-06": 20515, "2020-04-07": 26732, "2020-04-08": 24984, "2020-04-09": 25839, "2020-04-10": 15862, "2020-04-11": 3790, "2020-04-12": 2399, "2020-04-13": 16153, "2020-04-14": 24035, "2020-04-15": 16288, "2020-04-16": 23402, "2020-04-17": 25210, "2020-04-18": 4634, "2020-04-19": 3019, "2020-04-20": 20497, "2020-04-21": 22283, "2020-04-22": 25803, "2020-04-23": 23591, "2020-04-24": 21888, "2020-04-25": 3408, "2020-04-26": 3184, "2020-04-27": 19118, "2020-04-28": 22785, "2020-04-29": 24070, "2020-04-30": 22835, "2020-05-01": 13855, "2020-05-02": 3852, "2020-05-03": 2844, "2020-05-04": 18230, "2020-05-05": 23016, "2020-05-06": 24201, "2020-05-07": 28082, "2020-05-08": 21024, "2020-05-09": 4002, "2020-05-10": 3100, "2020-05-11": 18184, "2020-05-12": 23230, "2020-05-13": 22050, "2020-05-14": 26274, "2020-05-15": 22813, "2020-05-16": 3905, "2020-05-17": 2990, "2020-05-18": 21793, "2020-05-19": 24740, "2020-05-20": 27330, "2020-05-21": 22753, "2020-05-22": 21867, "2020-05-23": 3546, "2020-05-24": 2581, "2020-05-25": 11325, "2020-05-26": 22510, "2020-05-27": 25545, "2020-05-28": 27941, "2020-05-29": 22913, "2020-05-30": 4275, "2020-05-31": 2705, "2020-06-01": 20949, "2020-06-02": 24015, "2020-06-03": 25237, "2020-06-04": 25423, "2020-06-05": 24412, "2020-06-06": 3666, "2020-06-07": 2705, "2020-06-08": 22778, "2020-06-09": 23994, "2020-06-10": 23743, "2020-06-11": 25748, "2020-06-12": 23180, "2020-06-13": 3417, "2020-06-14": 2655, "2020-06-15": 22394, "2020-06-16": 27796, "2020-06-17": 28363, "2020-06-18": 24122, "2020-06-19": 16152, "2020-06-20": 2951, "2020-06-21": 2264, "2020-06-22": 0, "2020-06-23": 24597, "2020-06-24": 24228, "2020-06-25": 22184, "2020-06-26": 21386, "2020-06-27": 3216, "2020-06-28": 3008, "2020-06-29": 19263, "2020-06-30": 19689, "2020-07-01": 19382, "2020-07-02": 20049, "2020-07-03": 11366, "2020-07-04": 1883, "2020-07-05": 2362, "2020-07-06": 17119, "2020-07-07": 20842, "2020-07-08": 22178, "2020-07-09": 21173, "2020-07-10": 19394, "2020-07-11": 3107, "2020-07-12": 2298, "2020-07-13": 20032, "2020-07-14": 22931, "2020-07-15": 23363, "2020-07-16": 20549, "2020-07-17": 20131, "2020-07-18": 3905, "2020-07-19": 2873, "2020-07-20": 21029, "2020-07-21": 25744, "2020-07-22": 26425, "2020-07-23": 26327, "2020-07-24": 22406, "2020-07-25": 3731, "2020-07-26": 2640, "2020-07-27": 22817, "2020-07-28": 28301, "2020-07-29": 34708, "2020-07-30": 28440, "2020-07-31": 24499, "2020-08-01": 4322, "2020-08-02": 3161, "2020-08-03": 20310, "2020-08-04": 28116, "2020-08-05": 28249, "2020-08-06": 28280, "2020-08-07": 26644, "2020-08-08": 3781, "2020-08-09": 2847, "2020-08-10": 21851, "2020-08-11": 26203, "2020-08-12": 29309, "2020-08-13": 25998, "2020-08-14": 21486, "2020-08-15": 4267, "2020-08-16": 3132, "2020-08-17": 22108, "2020-08-18": 26636, "2020-08-19": 25378, "2020-08-20": 27712, "2020-08-21": 24817, "2020-08-22": 5051, "2020-08-23": 3844, "2020-08-24": 23969, "2020-08-25": 27679, "2020-08-26": 25802, "2020-08-27": 28646, "2020-08-28": 23846, "2020-08-29": 3596, "2020-08-30": 2358, "2020-08-31": 21461, "2020-09-01": 25638, "2020-09-02": 27440, "2020-09-03": 28224, "2020-09-04": 23206, "2020-09-05": 3716, "2020-09-06": 2753, "2020-09-07": 13642, "2020-09-08": 15119, "2020-09-09": 30345, "2020-09-10": 28454, "2020-09-11": 23577, "2020-09-12": 4874, "2020-09-13": 4486, "2020-09-14": 27685, "2020-09-15": 27995, "2020-09-16": 28597, "2020-09-17": 27243, "2020-09-18": 27698, "2020-09-19": 4509, "2020-09-20": 3852, "2020-09-21": 26742, "2020-09-22": 27919, "2020-09-23": 28407, "2020-09-24": 33461, "2020-09-25": 25505, "2020-09-26": 4438, "2020-09-27": 3177, "2020-09-28": 25917, "2020-09-29": 30358, "2020-09-30": 29025, "2020-10-01": 29003, "2020-10-02": 23925, "2020-10-03": 3432, "2020-10-04": 2717, "2020-10-05": 23278, "2020-10-06": 25427, "2020-10-07": 28855, "2020-10-08": 26360, "2020-10-09": 23015, "2020-10-10": 3767, "2020-10-11": 3630, "2020-10-12": 21221, "2020-10-13": 24768, "2020-10-14": 26983, "2020-10-15": 27399, "2020-10-16": 23322, "2020-10-17": 3708, "2020-10-18": 3721, "2020-10-19": 23683, "2020-10-20": 28595, "2020-10-21": 28623, "2020-10-22": 28578, "2020-10-23": 24420, "2020-10-24": 4218, "2020-10-25": 2676, "2020-10-26": 21008, "2020-10-27": 27569, "2020-10-28": 25879, "2020-10-29": 25993, "2020-10-30": 24205, "2020-10-31": 3379, "2020-11-01": 3498, "2020-11-02": 21892, "2020-11-03": 28203, "2020-11-04": 28098, "2020-11-05": 29013, "2020-11-06": 23640, "2020-11-07": 4566, "2020-11-08": 3427, "2020-11-09": 25389, "2020-11-10": 28607, "2020-11-11": 24849, "2020-11-12": 30534, "2020-11-13": 23527, "2020-11-14": 4064, "2020-11-15": 3698, "2020-11-16": 21454, "2020-11-17": 27200, "2020-11-18": 26017, "2020-11-19": 25590, "2020-11-20": 24675, "2020-11-21": 4322, "2020-11-22": 3093, "2020-11-23": 22160, "2020-11-24": 25496, "2020-11-25": 22037, "2020-11-26": 16461, "2020-11-27": 13860, "2020-11-28": 2856, "2020-11-29": 2699, "2020-11-30": 22686, "2020-12-01": 26124, "2020-12-02": 29348, "2020-12-03": 24431, "2020-12-04": 21863, "2020-12-05": 5563, "2020-12-06": 3003, "2020-12-07": 24656, "2020-12-08": 27685, "2020-12-09": 30244, "2020-12-10": 29699, "2020-12-11": 24734, "2020-12-12": 4215, "2020-12-13": 3116, "2020-12-14": 25042, "2020-12-15": 27624, "2020-12-16": 28764, "2020-12-17": 27682, "2020-12-18": 24661, "2020-12-19": 4752, "2020-12-20": 3367, "2020-12-21": 18149, "2020-12-22": 18563, "2020-12-23": 15593, "2020-12-24": 7745, "2020-12-25": 3165, "2020-12-26": 1606, "2020-12-27": 1810, "2020-12-28": 11328, "2020-12-29": 11404, "2020-12-30": 11603, "2020-12-31": 7662, "2021-01-01": 2719, "2021-01-02": 2222, "2021-01-03": 2999, "2021-01-04": 23508, "2021-01-05": 25139, "2021-01-06": 22741, "2021-01-07": 24462, "2021-01-08": 22456, "2021-01-09": 3793, "2021-01-10": 2861, "2021-01-11": 24932, "2021-01-12": 28190, "2021-01-13": 28990, "2021-01-14": 25711, "2021-01-15": 23836, "2021-01-16": 3807, "2021-01-17": 3588, "2021-01-18": 18286, "2021-01-19": 24055, "2021-01-20": 26921, "2021-01-21": 28608, "2021-01-22": 16587, "2021-01-23": 4197, "2021-01-24": 3222, "2021-01-25": 24115, "2021-01-26": 27400, "2021-01-27": 30881, "2021-01-28": 28594, "2021-01-29": 23670, "2021-01-30": 3779, "2021-01-31": 5639, "2021-02-01": 26726, "2021-02-02": 31456, "2021-02-03": 29737, "2021-02-04": 30134, "2021-02-05": 27124, "2021-02-06": 3902, "2021-02-07": 2660, "2021-02-08": 25226, "2021-02-09": 28695, "2021-02-10": 31700, "2021-02-11": 31367, "2021-02-12": 30522, "2021-02-13": 5085, "2021-02-14": 3161, "2021-02-15": 18898, "2021-02-16": 26520, "2021-02-17": 30999, "2021-02-18": 32560, "2021-02-19": 25035, "2021-02-20": 5889, "2021-02-21": 4197, "2021-02-22": 25908, "2021-02-23": 31709, "2021-02-24": 31478, "2021-02-25": 30675, "2021-02-26": 26998, "2021-02-27": 4194, "2021-02-28": 3192, "2021-03-01": 31670, "2021-03-02": 35314, "2021-03-03": 34541, "2021-03-04": 35989, "2021-03-05": 32976, "2021-03-06": 5435, "2021-03-07": 4178, "2021-03-08": 29242, "2021-03-09": 35265, "2021-03-10": 37370, "2021-03-11": 35769, "2021-03-12": 32236, "2021-03-13": 4722, "2021-03-14": 4567, "2021-03-15": 30791, "2021-03-16": 41017, "2021-03-17": 37429, "2021-03-18": 34438, "2021-03-19": 32432, "2021-03-20": 3987, "2021-03-21": 3503, "2021-03-22": 31901, "2021-03-23": 38687, "2021-03-24": 40143, "2021-03-25": 37150, "2021-03-26": 29342, "2021-03-27": 3493, "2021-03-28": 3751, "2021-03-29": 29782, "2021-03-30": 31226, "2021-03-31": 29908, "2021-04-01": 32087, "2021-04-02": 25834, "2021-04-03": 4112, "2021-04-04": 2930, "2021-04-05": 19721, "2021-04-06": 34884, "2021-04-07": 34513, "2021-04-08": 31378, "2021-04-09": 27712, "2021-04-10": 3947, "2021-04-11": 2999, "2021-04-12": 28900, "2021-04-13": 29758, "2021-04-14": 31373, "2021-04-15": 30633, "2021-04-16": 25806, "2021-04-17": 3326, "2021-04-18": 3236, "2021-04-19": 27906, "2021-04-20": 30473, "2021-04-21": 27207, "2021-04-22": 30079, "2021-04-23": 27766, "2021-04-24": 4245, "2021-04-25": 4867, "2021-04-26": 30020, "2021-04-27": 30847, "2021-04-28": 26309, "2021-04-29": 28663, "2021-04-30": 26306, "2021-05-01": 4367, "2021-05-02": 3848, "2021-05-03": 24057, "2021-05-04": 30155, "2021-05-05": 31752, "2021-05-06": 31915, "2021-05-07": 26207, "2021-05-08": 5227, "2021-05-09": 4971, "2021-05-10": 30286, "2021-05-11": 33980, "2021-05-12": 28147, "2021-05-13": 22746, "2021-05-14": 19249, "2021-05-15": 3536, "2021-05-16": 3364, "2021-05-17": 23352, "2021-05-18": 24909, "2021-05-19": 26653, "2021-05-20": 27526, "2021-05-21": 22579, "2021-05-22": 3402, "2021-05-23": 3143, "2021-05-24": 21027, "2021-05-25": 26073, "2021-05-26": 26605, "2021-05-27": 25544, "2021-05-28": 21428, "2021-05-29": 3890, "2021-05-30": 3087, "2021-05-31": 15917, "2021-06-01": 25438, "2021-06-02": 24553, "2021-06-03": 22469, "2021-06-04": 21182, "2021-06-05": 3919, "2021-06-06": 3233, "2021-06-07": 23575, "2021-06-08": 24918, "2021-06-09": 26959, "2021-06-10": 26749, "2021-06-11": 24817, "2021-06-12": 4117, "2021-06-13": 3609, "2021-06-14": 23817, "2021-06-15": 24946, "2021-06-16": 25975, "2021-06-17": 25221, "2021-06-18": 22787, "2021-06-19": 4227, "2021-06-20": 4338, "2021-06-21": 24645, "2021-06-22": 26500, "2021-06-23": 26154, "2021-06-24": 26479, "2021-06-25": 23179, "2021-06-26": 3683, "2021-06-27": 3358, "2021-06-28": 25043, "2021-06-29": 26293, "2021-06-30": 26195, "2021-07-01": 24491, "2021-07-02": 20381, "2021-07-03": 4260, "2021-07-04": 3325, "2021-07-05": 18108, "2021-07-06": 25962, "2021-07-07": 27048, "2021-07-08": 26933, "2021-07-09": 22873, "2021-07-10": 4408, "2021-07-11": 3676, "2021-07-12": 24152, "2021-07-13": 26186, "2021-07-14": 25141, "2021-07-15": 22874, "2021-07-16": 22030, "2021-07-17": 3650, "2021-07-18": 3174, "2021-07-19": 24223, "2021-07-20": 24614, "2021-07-21": 24809, "2021-07-22": 24410, "2021-07-23": 21708, "2021-07-24": 3629, "2021-07-25": 3550, "2021-07-26": 23185, "2021-07-27": 24284, "2021-07-28": 25888, "2021-07-29": 28905, "2021-07-30": 26884, "2021-07-31": 5251, "2021-08-01": 3795, "2021-08-02": 26195, "2021-08-03": 26706, "2021-08-04": 25725, "2021-08-05": 26992, "2021-08-06": 23452, "2021-08-07": 4380, "2021-08-08": 4038, "2021-08-09": 24752, "2021-08-10": 25231, "2021-08-11": 26137, "2021-08-12": 24445, "2021-08-13": 22626, "2021-08-14": 4343, "2021-08-15": 3710, "2021-08-16": 24076, "2021-08-17": 27186, "2021-08-18": 27041, "2021-08-19": 26628, "2021-08-20": 23355, "2021-08-21": 4657, "2021-08-22": 4398, "2021-08-23": 25731, "2021-08-24": 28138, "2021-08-25": 29933, "2021-08-26": 28361, "2021-08-27": 24021, "2021-08-28": 6471, "2021-08-29": 5731, "2021-08-30": 26783, "2021-08-31": 30867, "2021-09-01": 30372, "2021-09-02": 28224, "2021-09-03": 24120, "2021-09-04": 5786, "2021-09-05": 5155, "2021-09-06": 18380, "2021-09-07": 26666, "2021-09-08": 29606, "2021-09-09": 29147, "2021-09-10": 27082, "2021-09-11": 5947, "2021-09-12": 4814, "2021-09-13": 28199, "2021-09-14": 29068, "2021-09-15": 29344, "2021-09-16": 28970, "2021-09-17": 27469, "2021-09-18": 5295, "2021-09-19": 5045, "2021-09-20": 30775, "2021-09-21": 31773, "2021-09-22": 30680, "2021-09-23": 29019, "2021-09-24": 25788, "2021-09-25": 5323, "2021-09-26": 4622, "2021-09-27": 29998, "2021-09-28": 32915, "2021-09-29": 32136, "2021-09-30": 29062, "2021-10-01": 24783, "2021-10-02": 5330, "2021-10-03": 4473, "2021-10-04": 28509, "2021-10-05": 29521, "2021-10-06": 29758, "2021-10-07": 30932, "2021-10-08": 24567, "2021-10-09": 4374, "2021-10-10": 4679, "2021-10-11": 25573, "2021-10-12": 31253, "2021-10-13": 33742, "2021-10-14": 30057, "2021-10-15": 24808, "2021-10-16": 5353, "2021-10-17": 4364, "2021-10-18": 29847, "2021-10-19": 31155, "2021-10-20": 32267, "2021-10-21": 31259, "2021-10-22": 29982, "2021-10-23": 4841, "2021-10-24": 4280, "2021-10-25": 31353, "2021-10-26": 32130, "2021-10-27": 32900, "2021-10-28": 32738, "2021-10-29": 28951, "2021-10-30": 5449, "2021-10-31": 4387, "2021-11-01": 26904, "2021-11-02": 31336, "2021-11-03": 33930, "2021-11-04": 30517, "2021-11-05": 27212, "2021-11-06": 5034, "2021-11-07": 4490, "2021-11-08": 30058, "2021-11-09": 31402, "2021-11-10": 31601, "2021-11-11": 29523, "2021-11-12": 25841, "2021-11-13": 6386, "2021-11-14": 4883, "2021-11-15": 28717, "2021-11-16": 32391, "2021-11-17": 31931, "2021-11-18": 33460, "2021-11-19": 27633, "2021-11-20": 5843, "2021-11-21": 4523, "2021-11-22": 29115, "2021-11-23": 29312, "2021-11-24": 27447, "2021-11-25": 22083, "2021-11-26": 16675, "2021-11-27": 4423, "2021-11-28": 4979, "2021-11-29": 29415, "2021-11-30": 33020, "2021-12-01": 31935, "2021-12-02": 32590, "2021-12-03": 28653, "2021-12-04": 6306, "2021-12-05": 6678, "2021-12-06": 31237, "2021-12-07": 32491, "2021-12-08": 33283, "2021-12-09": 33460, "2021-12-10": 29339, "2021-12-11": 6802, "2021-12-12": 6911, "2021-12-13": 32203, "2021-12-14": 34974, "2021-12-15": 33808, "2021-12-16": 33713, "2021-12-17": 28698, "2021-12-18": 5625, "2021-12-19": 4923, "2021-12-20": 25686, "2021-12-21": 26888, "2021-12-22": 25426, "2021-12-23": 19446, "2021-12-24": 9413, "2021-12-25": 3030, "2021-12-26": 3132, "2021-12-27": 11147, "2021-12-28": 14420, "2021-12-29": 14452, "2021-12-30": 12949, "2021-12-31": 7184, "2022-01-01": 3118, "2022-01-02": 3604, "2022-01-03": 20470, "2022-01-04": 29029, "2022-01-05": 28776, "2022-01-06": 27127, "2022-01-07": 24480, "2022-01-08": 5290, "2022-01-09": 4188, "2022-01-10": 28812, "2022-01-11": 33299, "2022-01-12": 34467, "2022-01-13": 34583, "2022-01-14": 27741, "2022-01-15": 5907, "2022-01-16": 4765, "2022-01-17": 26521, "2022-01-18": 32249, "2022-01-19": 34598, "2022-01-20": 33340, "2022-01-21": 29744, "2022-01-22": 6624, "2022-01-23": 4290, "2022-01-24": 34056, "2022-01-25": 32797, "2022-01-26": 33913, "2022-01-27": 38714, "2022-01-28": 30729, "2022-01-29": 5324, "2022-01-30": 4060, "2022-01-31": 31703, "2022-02-01": 34845, "2022-02-02": 35008, "2022-02-03": 34967, "2022-02-04": 28915, "2022-02-05": 4886, "2022-02-06": 4832, "2022-02-07": 32543, "2022-02-08": 35441, "2022-02-09": 34281, "2022-02-10": 36041, "2022-02-11": 28274, "2022-02-12": 5564, "2022-02-13": 4111, "2022-02-14": 32527, "2022-02-15": 35180, "2022-02-16": 34349, "2022-02-17": 36914, "2022-02-18": 30285, "2022-02-19": 7269, "2022-02-20": 4638, "2022-02-21": 26407, "2022-02-22": 34958, "2022-02-23": 33622, "2022-02-24": 35327, "2022-02-25": 29819, "2022-02-26": 6291, "2022-02-27": 4509, "2022-02-28": 33077, "2022-03-01": 33009, "2022-03-02": 32440, "2022-03-03": 32676, "2022-03-04": 26693, "2022-03-05": 5165, "2022-03-06": 4877, "2022-03-07": 31707, "2022-03-08": 31208, "2022-03-09": 33411, "2022-03-10": 38113, "2022-03-11": 28466, "2022-03-12": 6198, "2022-03-13": 5437, "2022-03-14": 36306, "2022-03-15": 38970, "2022-03-16": 40928, "2022-03-17": 35769, "2022-03-18": 28626, "2022-03-19": 8104, "2022-03-20": 8060, "2022-03-21": 32369, "2022-03-22": 34206, "2022-03-23": 36696, "2022-03-24": 34328, "2022-03-25": 30198, "2022-03-26": 7089, "2022-03-27": 5900, "2022-03-28": 33273, "2022-03-29": 37640, "2022-03-30": 33853, "2022-03-31": 32614, "2022-04-01": 29479, "2022-04-02": 5385, "2022-04-03": 5451, "2022-04-04": 34536, "2022-04-05": 33116, "2022-04-06": 31740, "2022-04-07": 34734, "2022-04-08": 27016, "2022-04-09": 5232, "2022-04-10": 4152, "2022-04-11": 32506, "2022-04-12": 34010, "2022-04-13": 31986, "2022-04-14": 28088, "2022-04-15": 16898, "2022-04-16": 4223, "2022-04-17": 3583, "2022-04-18": 20529, "2022-04-19": 31380, "2022-04-20": 32003, "2022-04-21": 30714, "2022-04-22": 25825, "2022-04-23": 4818, "2022-04-24": 4187, "2022-04-25": 30863, "2022-04-26": 33634, "2022-04-27": 33711, "2022-04-28": 32792, "2022-04-29": 27600, "2022-04-30": 4814, "2022-05-01": 4321, "2022-05-02": 26992, "2022-05-03": 29006, "2022-05-04": 34627, "2022-05-05": 31922, "2022-05-06": 26094, "2022-05-07": 5032, "2022-05-08": 3028, "2022-05-09": 33664, "2022-05-10": 36511, "2022-05-11": 36027, "2022-05-12": 32437, "2022-05-13": 27629, "2022-05-14": 4436, "2022-05-15": 5321, "2022-05-16": 31644, "2022-05-17": 33423, "2022-05-18": 33364, "2022-05-19": 32081, "2022-05-20": 30859, "2022-05-21": 4566, "2022-05-22": 4406, "2022-05-23": 32104, "2022-05-24": 35070, "2022-05-25": 33185, "2022-05-26": 27032, "2022-05-27": 25326, "2022-05-28": 4942, "2022-05-29": 4297, "2022-05-30": 23139, "2022-05-31": 30981, "2022-06-01": 31128, "2022-06-02": 31662, "2022-06-03": 27633, "2022-06-04": 4508, "2022-06-05": 3767, "2022-06-06": 28382, "2022-06-07": 33045, "2022-06-08": 34015, "2022-06-09": 33627, "2022-06-10": 29459, "2022-06-11": 4004, "2022-06-12": 3895, "2022-06-13": 31946, "2022-06-14": 34077, "2022-06-15": 33895, "2022-06-16": 31219, "2022-06-17": 26839, "2022-06-18": 4861, "2022-06-19": 4077, "2022-06-20": 26044, "2022-06-21": 32907, "2022-06-22": 35392, "2022-06-23": 33762, "2022-06-24": 27607, "2022-06-25": 4459, "2022-06-26": 4282, "2022-06-27": 31653, "2022-06-28": 34577, "2022-06-29": 32631, "2022-06-30": 31553, "2022-07-01": 25990, "2022-07-02": 4444, "2022-07-03": 4249, "2022-07-04": 23629, "2022-07-05": 31009, "2022-07-06": 33613, "2022-07-07": 32788, "2022-07-08": 25598, "2022-07-09": 4267, "2022-07-10": 3751, "2022-07-11": 31858, "2022-07-12": 35614, "2022-07-13": 38198, "2022-07-14": 37070, "2022-07-15": 31904, "2022-07-16": 4863, "2022-07-17": 4348, "2022-07-18": 33765, "2022-07-19": 39552, "2022-07-20": 40217, "2022-07-21": 41991, "2022-07-22": 0, "2022-07-23": 5208, "2022-07-24": 4934, "2022-07-25": 39569, "2022-07-26": 37316, "2022-07-27": 36886, "2022-07-28": 35124, "2022-07-29": 28415, "2022-07-30": 4811, "2022-07-31": 4614, "2022-08-01": 31594, "2022-08-02": 31977, "2022-08-03": 33965, "2022-08-04": 40292, "2022-08-05": 34754, "2022-08-06": 4822, "2022-08-07": 4592, "2022-08-08": 38984, "2022-08-09": 39890, "2022-08-10": 46371, "2022-08-11": 40800, "2022-08-12": 32867, "2022-08-13": 4566, "2022-08-14": 4479, "2022-08-15": 28211, "2022-08-16": 38135, "2022-08-17": 42099, "2022-08-18": 40740, "2022-08-19": 31223, "2022-08-20": 4997, "2022-08-21": 4982, "2022-08-22": 39473, "2022-08-23": 40580, "2022-08-24": 36946, "2022-08-25": 35167, "2022-08-26": 31099, "2022-08-27": 4626, "2022-08-28": 4177, "2022-08-29": 31517, "2022-08-30": 36843, "2022-08-31": 37416, "2022-09-01": 40967, "2022-09-02": 32936, "2022-09-03": 5451, "2022-09-04": 4173, "2022-09-05": 25348, "2022-09-06": 38304, "2022-09-07": 40670, "2022-09-08": 37948, "2022-09-09": 33688, "2022-09-10": 4778, "2022-09-11": 4974, "2022-09-12": 35847, "2022-09-13": 38571, "2022-09-14": 40244, "2022-09-15": 39516, "2022-09-16": 32187, "2022-09-17": 5064, "2022-09-18": 4873, "2022-09-19": 37674, "2022-09-20": 40593, "2022-09-21": 38164, "2022-09-22": 35140, "2022-09-23": 31200, "2022-09-24": 4917, "2022-09-25": 4132, "2022-09-26": 34996, "2022-09-27": 38293, "2022-09-28": 37519, "2022-09-29": 39666, "2022-09-30": 32197, "2022-10-01": 6067, "2022-10-02": 4960, "2022-10-03": 34454, "2022-10-04": 38047, "2022-10-05": 36417, "2022-10-06": 38492, "2022-10-07": 32427, "2022-10-08": 4832, "2022-10-09": 4778, "2022-10-10": 32671, "2022-10-11": 37877, "2022-10-12": 36881, "2022-10-13": 38875, "2022-10-14": 34397, "2022-10-15": 5108, "2022-10-16": 4279, "2022-10-17": 38700, "2022-10-18": 42548, "2022-10-19": 41881, "2022-10-20": 40615, "2022-10-21": 34599, "2022-10-22": 5674, "2022-10-23": 4639, "2022-10-24": 35157, "2022-10-25": 38776, "2022-10-26": 38820, "2022-10-27": 39645, "2022-10-28": 34826, "2022-10-29": 6998, "2022-10-30": 5425, "2022-10-31": 33325, "2022-11-01": 33623, "2022-11-02": 37272, "2022-11-03": 40107, "2022-11-04": 32908, "2022-11-05": 7709, "2022-11-06": 5614, "2022-11-07": 39589, "2022-11-08": 43175, "2022-11-09": 41296, "2022-11-10": 41358, "2022-11-11": 27929, "2022-11-12": 5167, "2022-11-13": 5241, "2022-11-14": 39861, "2022-11-15": 44073, "2022-11-16": 41174, "2022-11-17": 40983, "2022-11-18": 33878, "2022-11-19": 5930, "2022-11-20": 4699, "2022-11-21": 36805, "2022-11-22": 38327, "2022-11-23": 37672, "2022-11-24": 28050, "2022-11-25": 20966, "2022-11-26": 4732, "2022-11-27": 5279, "2022-11-28": 35279, "2022-11-29": 39366, "2022-11-30": 36538, "2022-12-01": 38181, "2022-12-02": 34167, "2022-12-03": 6649, "2022-12-04": 5214, "2022-12-05": 37614, "2022-12-06": 38648, "2022-12-07": 38484, "2022-12-08": 36338, "2022-12-09": 30251, "2022-12-10": 5879, "2022-12-11": 5098, "2022-12-12": 36820, "2022-12-13": 39797, "2022-12-14": 39771, "2022-12-15": 37667, "2022-12-16": 30347, "2022-12-17": 5894, "2022-12-18": 4857, "2022-12-19": 32703, "2022-12-20": 34754, "2022-12-21": 33208, "2022-12-22": 31557, "2022-12-23": 20475, "2022-12-24": 4216, "2022-12-25": 3378, "2022-12-26": 10833, "2022-12-27": 16636, "2022-12-28": 18144, "2022-12-29": 17490, "2022-12-30": 13564, "2022-12-31": 3794, "2023-01-01": 4139, "2023-01-02": 15161, "2023-01-03": 31155, "2023-01-04": 34187, "2023-01-05": 33164, "2023-01-06": 26417, "2023-01-07": 5514, "2023-01-08": 4619, "2023-01-09": 36398, "2023-01-10": 37812, "2023-01-11": 37515, "2023-01-12": 39090, "2023-01-13": 34165, "2023-01-14": 5864, "2023-01-15": 5456, "2023-01-16": 29712, "2023-01-17": 37651, "2023-01-18": 37138, "2023-01-19": 34241, "2023-01-20": 31019, "2023-01-21": 6312, "2023-01-22": 5676, "2023-01-23": 36498, "2023-01-24": 34749, "2023-01-25": 35075, "2023-01-26": 33229, "2023-01-27": 30432, "2023-01-28": 5804, "2023-01-29": 5592, "2023-01-30": 33627, "2023-01-31": 38561, "2023-02-01": 36553, "2023-02-02": 36416, "2023-02-03": 32195, "2023-02-04": 6158, "2023-02-05": 5277, "2023-02-06": 34466, "2023-02-07": 38201, "2023-02-08": 36752, "2023-02-09": 36118, "2023-02-10": 30709, "2023-02-11": 6141, "2023-02-12": 5516, "2023-02-13": 33565, "2023-02-14": 38580, "2023-02-15": 37636, "2023-02-16": 36047, "2023-02-17": 32172, "2023-02-18": 6804, "2023-02-19": 5846, "2023-02-20": 29032, "2023-02-21": 36983, "2023-02-22": 39959, "2023-02-23": 37131, "2023-02-24": 33259, "2023-02-25": 6110, "2023-02-26": 5706, "2023-02-27": 35556, "2023-02-28": 38491, "2023-03-01": 41047, "2023-03-02": 37515, "2023-03-03": 31578, "2023-03-04": 6980, "2023-03-05": 5447, "2023-03-06": 35573, "2023-03-07": 38076, "2023-03-08": 33708, "2023-03-09": 37520, "2023-03-10": 32108, "2023-03-11": 5788, "2023-03-12": 5342, "2023-03-13": 34805, "2023-03-14": 36822, "2023-03-15": 39404, "2023-03-16": 35612, "2023-03-17": 31099, "2023-03-18": 5150, "2023-03-19": 5413, "2023-03-20": 36060, "2023-03-21": 38001, "2023-03-22": 36587, "2023-03-23": 35442, "2023-03-24": 29387, "2023-03-25": 4884, "2023-03-26": 5453, "2023-03-27": 33913, "2023-03-28": 36777, "2023-03-29": 0, "2023-03-30": 37871, "2023-03-31": 30736, "2023-04-01": 6005, "2023-04-02": 5313, "2023-04-03": 34877, "2023-04-04": 37588, "2023-04-05": 36490, "2023-04-06": 32947, "2023-04-07": 20388, "2023-04-08": 4398, "2023-04-09": 3934, "2023-04-10": 24043, "2023-04-11": 38119, "2023-04-12": 35943, "2023-04-13": 37331, "2023-04-14": 31630, "2023-04-15": 4933, "2023-04-16": 4726, "2023-04-17": 37000, "2023-04-18": 40640, "2023-04-19": 39340, "2023-04-20": 37948, "2023-04-21": 31050, "2023-04-22": 5372, "2023-04-23": 5310, "2023-04-24": 39292, "2023-04-25": 38776, "2023-04-26": 40695, "2023-04-27": 39129, "2023-04-28": 33466, "2023-04-29": 5324, "2023-04-30": 4282, "2023-05-01": 24531, "2023-05-02": 39738, "2023-05-03": 39732, "2023-05-04": 41082, "2023-05-05": 35119, "2023-05-06": 5741, "2023-05-07": 5651, "2023-05-08": 36849, "2023-05-09": 39868, "2023-05-10": 36512, "2023-05-11": 38502, "2023-05-12": 33260, "2023-05-13": 5303, "2023-05-14": 5373, "2023-05-15": 37206, "2023-05-16": 41038, "2023-05-17": 39702, "2023-05-18": 32397, "2023-05-19": 30730, "2023-05-20": 4879, "2023-05-21": 4552, "2023-05-22": 35783, "2023-05-23": 40767, "2023-05-24": 41342, "2023-05-25": 38415, "2023-05-26": 31889, "2023-05-27": 4533, "2023-05-28": 4196, "2023-05-29": 22728, "2023-05-30": 35568, "2023-05-31": 39694, "2023-06-01": 37264, "2023-06-02": 32466, "2023-06-03": 5311, "2023-06-04": 5766, "2023-06-05": 36412, "2023-06-06": 39325, "2023-06-07": 40969, "2023-06-08": 38163, "2023-06-09": 34397, "2023-06-10": 6196, "2023-06-11": 4743, "2023-06-12": 37845, "2023-06-13": 42043, "2023-06-14": 41011, "2023-06-15": 39248, "2023-06-16": 34490, "2023-06-17": 5140, "2023-06-18": 5071, "2023-06-19": 32493, "2023-06-20": 38150, "2023-06-21": 38395, "2023-06-22": 38171, "2023-06-23": 34663, "2023-06-24": 5422, "2023-06-25": 5376, "2023-06-26": 37878, "2023-06-27": 39680, "2023-06-28": 37314, "2023-06-29": 35597, "2023-06-30": 32117, "2023-07-01": 0, "2023-07-02": 4654, "2023-07-03": 31363, "2023-07-04": 28835, "2023-07-05": 37381, "2023-07-06": 39977, "2023-07-07": 35191, "2023-07-08": 6754, "2023-07-09": 5965, "2023-07-10": 40388, "2023-07-11": 43661, "2023-07-12": 41249, "2023-07-13": 39141, "2023-07-14": 32866, "2023-07-15": 5011, "2023-07-16": 5470, "2023-07-17": 37968, "2023-07-18": 41998, "2023-07-19": 40055, "2023-07-20": 37709, "2023-07-21": 31701, "2023-07-22": 5554, "2023-07-23": 5110, "2023-07-24": 37098, "2023-07-25": 37941, "2023-07-26": 38245, "2023-07-27": 35873, "2023-07-28": 31305, "2023-07-29": 5294, "2023-07-30": 5083, "2023-07-31": 36647, "2023-08-01": 39219, "2023-08-02": 39659, "2023-08-03": 37983, "2023-08-04": 31218, "2023-08-05": 5493, "2023-08-06": 5700, "2023-08-07": 35960, "2023-08-08": 40288, "2023-08-09": 39572, "2023-08-10": 40725, "2023-08-11": 32408, "2023-08-12": 5700, "2023-08-13": 5725, "2023-08-14": 37604, "2023-08-15": 35762, "2023-08-16": 42059, "2023-08-17": 39557, "2023-08-18": 33749, "2023-08-19": 5545, "2023-08-20": 5578, "2023-08-21": 39058, "2023-08-22": 40394, "2023-08-23": 39224, "2023-08-24": 39327, "2023-08-25": 33640, "2023-08-26": 5568, "2023-08-27": 5452, "2023-08-28": 36824, "2023-08-29": 39073, "2023-08-30": 39323, "2023-08-31": 41019, "2023-09-01": 33004, "2023-09-02": 5603, "2023-09-03": 6058, "2023-09-04": 25503, "2023-09-05": 39039, "2023-09-06": 39465, "2023-09-07": 37695, "2023-09-08": 33268, "2023-09-09": 5515, "2023-09-10": 5720, "2023-09-11": 37737, "2023-09-12": 38926, "2023-09-13": 0, "2023-09-14": 0, "2023-09-15": 33854, "2023-09-16": 5678, "2023-09-17": 5551, "2023-09-18": 36439, "2023-09-19": 39031, "2023-09-20": 39950, "2023-09-21": 38461, "2023-09-22": 33511, "2023-09-23": 5157, "2023-09-24": 5253, "2023-09-25": 37626, "2023-09-26": 40204, "2023-09-27": 40193, "2023-09-28": 34834, "2023-09-29": 31401, "2023-09-30": 5459, "2023-10-01": 6581, "2023-10-02": 34473, "2023-10-03": 38246, "2023-10-04": 40662, "2023-10-05": 38160, "2023-10-06": 33333, "2023-10-07": 5845, "2023-10-08": 5461, "2023-10-09": 30981, "2023-10-10": 38352, "2023-10-11": 39837, "2023-10-12": 39334, "2023-10-13": 32103, "2023-10-14": 4948, "2023-10-15": 5399, "2023-10-16": 38939, "2023-10-17": 41498, "2023-10-18": 40133, "2023-10-19": 41696, "2023-10-20": 35038, "2023-10-21": 5862, "2023-10-22": 5394, "2023-10-23": 39043, "2023-10-24": 38789, "2023-10-25": 43376, "2023-10-26": 38491, "2023-10-27": 35056, "2023-10-28": 5020, "2023-10-29": 6042, "2023-10-30": 39269, "2023-10-31": 41735, "2023-11-01": 37687, "2023-11-02": 13650, "2023-11-03": 0, "2023-11-04": 6617, "2023-11-05": 6054, "2023-11-06": 42700, "2023-11-07": 44486, "2023-11-08": 43884, "2023-11-09": 40849, "2023-11-10": 35183, "2023-11-11": 5803, "2023-11-12": 6003, "2023-11-13": 39412, "2023-11-14": 41653, "2023-11-15": 41824, "2023-11-16": 42578, "2023-11-17": 37206, "2023-11-18": 6360, "2023-11-19": 6583, "2023-11-20": 38234, "2023-11-21": 42480, "2023-11-22": 38974, "2023-11-23": 30813, "2023-11-24": 26554, "2023-11-25": 4994, "2023-11-26": 5757, "2023-11-27": 40732, "2023-11-28": 43418, "2023-11-29": 43964, "2023-11-30": 43138, "2023-12-01": 38634, "2023-12-02": 8195, "2023-12-03": 7629, "2023-12-04": 43429, "2023-12-05": 45672, "2023-12-06": 45945, "2023-12-07": 42395, "2023-12-08": 37113, "2023-12-09": 8245, "2023-12-10": 7508, "2023-12-11": 45494, "2023-12-12": 49548, "2023-12-13": 46740, "2023-12-14": 45794, "2023-12-15": 39743, "2023-12-16": 6932, "2023-12-17": 6192, "2023-12-18": 38450, "2023-12-19": 41548, "2023-12-20": 40992, "2023-12-21": 37864, "2023-12-22": 26212, "2023-12-23": 6303, "2023-12-24": 5024, "2023-12-25": 9872, "2023-12-26": 14261, "2023-12-27": 18809, "2023-12-28": 19870, "2023-12-29": 15239, "2023-12-30": 5522, "2023-12-31": 4864, "2024-01-01": 7638, "2024-01-02": 33013, "2024-01-03": 36147, "2024-01-04": 38681, "2024-01-05": 34532, "2024-01-06": 6397, "2024-01-07": 5425, "2024-01-08": 39356, "2024-01-09": 46544, "2024-01-10": 45818, "2024-01-11": 45322, "2024-01-12": 39774, "2024-01-13": 6236, "2024-01-14": 5572, "2024-01-15": 32852, "2024-01-16": 42961, "2024-01-17": 45820, "2024-01-18": 43314, "2024-01-19": 39618, "2024-01-20": 6970, "2024-01-21": 5690, "2024-01-22": 42138, "2024-01-23": 45260, "2024-01-24": 43959, "2024-01-25": 42333, "2024-01-26": 36125, "2024-01-27": 6015, "2024-01-28": 5956, "2024-01-29": 43201, "2024-01-30": 45225, "2024-01-31": 45249, "2024-02-01": 45552, "2024-02-02": 38488, "2024-02-03": 6521, "2024-02-04": 5891, "2024-02-05": 42720, "2024-02-06": 49491, "2024-02-07": 49868, "2024-02-08": 48491, "2024-02-09": 38858, "2024-02-10": 6406, "2024-02-11": 6581, "2024-02-12": 38601, "2024-02-13": 45052, "2024-02-14": 43539, "2024-02-15": 43567, "2024-02-16": 38064, "2024-02-17": 5992, "2024-02-18": 6255, "2024-02-19": 35208, "2024-02-20": 46191, "2024-02-21": 46609, "2024-02-22": 45749, "2024-02-23": 40484, "2024-02-24": 6768, "2024-02-25": 5713, "2024-02-26": 43896, "2024-02-27": 47491, "2024-02-28": 47424, "2024-02-29": 49239, }, }; } ================================================ FILE: website/src/pages/_components/data/update-versions.js ================================================ const fs = require("fs"); const path = require("path"); const { execSync } = require("child_process"); const sortByVersion = (a, b) => { let i = 0; const aVersionParts = a.version.split("."); const bVersionParts = b.version.split("."); // Compare each part of the version number while (i < aVersionParts.length || i < bVersionParts.length) { const aPart = Number(aVersionParts[i] || "0"); const bPart = Number(bVersionParts[i] || "0"); if (aPart < bPart) return 1; if (aPart > bPart) return -1; i++; } return 0; }; const output = execSync("npm view victory time --json"); const data = JSON.parse(output.toString()); delete data.modified; delete data.created; const downloads = Object.keys(data) .map((version) => ({ version, // eslint-disable-next-line no-magic-numbers date: data[version].substr(0, 10), })) .filter((v) => !(v.version.includes("next") || v.version.includes("alpha"))) .sort(sortByVersion); const contents = ` const data = { data: ${JSON.stringify(downloads, null, 2)} }; export default data; `; fs.writeFileSync(path.resolve(__dirname, "versions.js"), contents, "utf8"); ================================================ FILE: website/src/pages/_components/data/versions.js ================================================ const data = { data: [ { version: "37.1.1", date: "2024-09-05", }, { version: "37.1.0", date: "2024-08-29", }, { version: "37.0.2", date: "2024-04-04", }, { version: "37.0.1", date: "2024-03-25", }, { version: "37.0.0", date: "2024-03-18", }, { version: "36.9.2", date: "2024-03-13", }, { version: "36.9.1", date: "2024-02-06", }, { version: "36.9.0", date: "2024-02-05", }, { version: "36.8.6", date: "2024-02-01", }, { version: "36.8.5", date: "2024-01-31", }, { version: "36.8.4", date: "2024-01-29", }, { version: "36.8.3", date: "2024-01-29", }, { version: "36.8.2", date: "2024-01-16", }, { version: "36.8.1", date: "2024-01-09", }, { version: "36.7.0", date: "2023-11-28", }, { version: "36.6.12", date: "2023-11-02", }, { version: "36.6.11", date: "2023-06-13", }, { version: "36.6.10", date: "2023-05-01", }, { version: "36.6.8", date: "2022-09-26", }, { version: "36.6.7", date: "2022-09-14", }, { version: "36.6.6", date: "2022-08-31", }, { version: "36.6.5", date: "2022-08-23", }, { version: "36.6.4", date: "2022-08-19", }, { version: "36.6.3", date: "2022-08-19", }, { version: "36.6.2", date: "2022-08-17", }, { version: "36.6.1", date: "2022-08-16", }, { version: "36.6.0", date: "2022-08-04", }, { version: "36.5.3", date: "2022-06-27", }, { version: "36.5.2", date: "2022-06-23", }, { version: "36.5.1", date: "2022-06-23", }, { version: "36.5.0", date: "2022-06-07", }, { version: "36.4.1", date: "2022-05-25", }, { version: "36.4.0", date: "2022-05-10", }, { version: "36.3.2", date: "2022-04-14", }, { version: "36.3.1", date: "2022-03-14", }, { version: "36.3.0", date: "2022-02-14", }, { version: "36.2.2", date: "2022-02-14", }, { version: "36.2.1", date: "2022-01-28", }, { version: "36.2.0", date: "2021-11-03", }, { version: "36.1.0", date: "2021-10-26", }, { version: "36.0.1", date: "2021-09-18", }, { version: "36.0.0", date: "2021-09-07", }, { version: "35.11.4", date: "2021-09-02", }, { version: "35.11.3", date: "2021-08-31", }, { version: "35.11.2", date: "2021-08-30", }, { version: "35.11.0", date: "2021-08-23", }, { version: "35.10.1", date: "2021-08-13", }, { version: "35.10.0", date: "2021-08-04", }, { version: "35.9.3", date: "2021-07-23", }, { version: "35.9.2", date: "2021-07-22", }, { version: "35.9.1", date: "2021-07-14", }, { version: "35.9.0", date: "2021-06-24", }, { version: "35.8.6", date: "2021-06-11", }, { version: "35.8.5", date: "2021-06-09", }, { version: "35.8.4", date: "2021-06-01", }, { version: "35.8.3", date: "2021-05-31", }, { version: "35.8.2", date: "2021-05-26", }, { version: "35.8.1", date: "2021-05-24", }, { version: "35.8.0", date: "2021-05-19", }, { version: "35.7.2", date: "2021-05-18", }, { version: "35.7.1", date: "2021-05-14", }, { version: "35.7.0", date: "2021-05-12", }, { version: "35.6.4", date: "2021-05-12", }, { version: "35.6.3", date: "2021-05-10", }, { version: "35.6.2", date: "2021-05-07", }, { version: "35.6.1", date: "2021-05-05", }, { version: "35.6.0", date: "2021-05-04", }, { version: "35.5.1", date: "2021-04-12", }, { version: "35.5.0", date: "2021-04-08", }, { version: "35.4.13", date: "2021-04-02", }, { version: "35.4.12", date: "2021-03-19", }, { version: "35.4.11", date: "2021-03-04", }, { version: "35.4.10", date: "2021-03-02", }, { version: "35.4.9", date: "2021-02-13", }, { version: "35.4.8", date: "2021-02-01", }, { version: "35.4.7", date: "2021-01-27", }, { version: "35.4.6", date: "2021-01-05", }, { version: "35.4.5", date: "2021-01-05", }, { version: "35.4.4", date: "2020-12-25", }, { version: "35.4.3", date: "2020-12-07", }, { version: "35.4.2", date: "2020-12-01", }, { version: "35.4.1", date: "2020-12-01", }, { version: "35.4.0", date: "2020-11-25", }, { version: "35.3.5", date: "2020-11-09", }, { version: "35.3.4", date: "2020-11-07", }, { version: "35.3.3", date: "2020-11-02", }, { version: "35.3.2", date: "2020-10-29", }, { version: "35.3.1", date: "2020-10-20", }, { version: "35.3.0", date: "2020-10-14", }, { version: "35.2.0", date: "2020-10-08", }, { version: "35.1.1", date: "2020-09-28", }, { version: "35.1.0", date: "2020-09-25", }, { version: "35.0.9", date: "2020-09-08", }, { version: "35.0.8", date: "2020-07-30", }, { version: "35.0.7", date: "2020-07-29", }, { version: "35.0.6", date: "2020-07-28", }, { version: "35.0.5", date: "2020-07-17", }, { version: "35.0.4", date: "2020-07-16", }, { version: "35.0.3", date: "2020-07-08", }, { version: "35.0.2", date: "2020-07-01", }, { version: "35.0.1", date: "2020-06-28", }, { version: "35.0.0", date: "2020-06-27", }, { version: "34.3.12", date: "2020-06-22", }, { version: "34.3.11", date: "2020-06-09", }, { version: "34.3.10", date: "2020-06-07", }, { version: "34.3.9", date: "2020-05-30", }, { version: "34.3.8", date: "2020-05-26", }, { version: "34.3.7", date: "2020-05-22", }, { version: "34.3.6", date: "2020-05-18", }, { version: "34.3.5", date: "2020-05-15", }, { version: "34.3.4", date: "2020-05-14", }, { version: "34.3.3", date: "2020-05-14", }, { version: "34.3.2", date: "2020-05-13", }, { version: "34.3.1", date: "2020-05-13", }, { version: "34.3.0", date: "2020-05-12", }, { version: "34.2.2", date: "2020-05-11", }, { version: "34.2.1", date: "2020-05-09", }, { version: "34.2.0", date: "2020-05-06", }, { version: "34.1.3", date: "2020-03-09", }, { version: "34.1.2", date: "2020-03-03", }, { version: "34.1.1", date: "2020-02-06", }, { version: "34.1.0", date: "2020-02-04", }, { version: "34.0.1", date: "2020-01-23", }, { version: "34.0.0", date: "2019-12-21", }, { version: "33.1.7", date: "2019-12-06", }, { version: "33.1.6", date: "2019-12-01", }, { version: "33.1.5", date: "2019-11-27", }, { version: "33.1.4", date: "2019-11-26", }, { version: "33.1.3", date: "2019-11-08", }, { version: "33.1.2", date: "2019-11-01", }, { version: "33.1.1", date: "2019-10-08", }, { version: "33.1.0", date: "2019-09-25", }, { version: "33.0.6", date: "2019-09-20", }, { version: "33.0.5", date: "2019-08-29", }, { version: "33.0.4", date: "2019-08-28", }, { version: "33.0.3", date: "2019-08-26", }, { version: "33.0.2", date: "2019-08-26", }, { version: "33.0.1", date: "2019-08-23", }, { version: "33.0.0", date: "2019-08-22", }, { version: "32.3.7", date: "2019-08-20", }, { version: "32.3.6", date: "2019-08-08", }, { version: "32.3.5", date: "2019-08-07", }, { version: "32.3.4", date: "2019-08-05", }, { version: "32.3.3", date: "2019-07-03", }, { version: "32.3.2", date: "2019-07-02", }, { version: "32.3.1", date: "2019-06-28", }, { version: "32.3.0", date: "2019-06-20", }, { version: "32.2.3", date: "2019-05-14", }, { version: "32.2.2", date: "2019-05-04", }, { version: "32.2.1", date: "2019-05-03", }, { version: "32.2.0", date: "2019-04-10", }, { version: "32.1.0", date: "2019-03-18", }, { version: "32.0.2", date: "2019-03-12", }, { version: "32.0.1", date: "2019-03-12", }, { version: "32.0.0", date: "2019-02-28", }, { version: "31.3.0", date: "2019-02-23", }, { version: "31.2.0", date: "2019-01-28", }, { version: "31.1.0", date: "2019-01-08", }, { version: "31.0.2", date: "2018-12-12", }, { version: "31.0.1", date: "2018-11-18", }, { version: "31.0.0", date: "2018-11-11", }, { version: "30.6.1", date: "2018-11-10", }, { version: "30.6.0", date: "2018-10-27", }, { version: "30.5.1", date: "2018-10-19", }, { version: "30.5.0", date: "2018-10-03", }, { version: "30.4.1", date: "2018-09-26", }, { version: "30.4.0", date: "2018-09-24", }, { version: "30.3.1", date: "2018-08-30", }, { version: "30.3.0", date: "2018-08-24", }, { version: "30.2.0", date: "2018-08-06", }, { version: "30.1.0", date: "2018-07-28", }, { version: "30.0.0", date: "2018-07-17", }, { version: "0.27.2", date: "2018-06-24", }, { version: "0.27.1", date: "2018-06-22", }, { version: "0.27.0", date: "2018-06-06", }, { version: "0.26.1", date: "2018-05-17", }, { version: "0.26.0", date: "2018-04-22", }, { version: "0.25.7", date: "2018-03-27", }, { version: "0.25.6", date: "2018-02-15", }, { version: "0.25.5", date: "2018-02-13", }, { version: "0.25.4", date: "2018-02-08", }, { version: "0.25.3", date: "2018-02-06", }, { version: "0.25.1", date: "2018-02-05", }, { version: "0.25.0", date: "2018-02-05", }, { version: "0.24.5", date: "2018-01-09", }, { version: "0.24.3", date: "2017-12-18", }, { version: "0.24.2", date: "2017-11-15", }, { version: "0.24.1", date: "2017-11-08", }, { version: "0.24.0", date: "2017-10-20", }, { version: "0.23.1", date: "2017-10-05", }, { version: "0.23.0", date: "2017-09-30", }, { version: "0.22.2", date: "2017-09-12", }, { version: "0.22.1", date: "2017-09-10", }, { version: "0.22.0", date: "2017-09-09", }, { version: "0.21.5", date: "2017-08-17", }, { version: "0.21.4", date: "2017-08-08", }, { version: "0.21.3", date: "2017-07-24", }, { version: "0.21.2", date: "2017-07-13", }, { version: "0.21.1", date: "2017-06-30", }, { version: "0.21.0", date: "2017-06-06", }, { version: "0.20.0", date: "2017-05-24", }, { version: "0.19.1", date: "2017-05-12", }, { version: "0.19.0", date: "2017-05-02", }, { version: "0.18.4", date: "2017-04-04", }, { version: "0.18.3", date: "2017-03-21", }, { version: "0.18.2", date: "2017-03-16", }, { version: "0.18.1", date: "2017-03-15", }, { version: "0.18.0", date: "2017-02-27", }, { version: "0.17.0", date: "2017-02-06", }, { version: "0.16.1", date: "2017-02-04", }, { version: "0.16.0", date: "2017-01-31", }, { version: "0.15.0", date: "2017-01-04", }, { version: "0.14.2", date: "2016-12-14", }, { version: "0.14.1", date: "2016-12-13", }, { version: "0.14.0", date: "2016-12-03", }, { version: "0.13.7", date: "2016-11-11", }, { version: "0.13.6", date: "2016-11-10", }, { version: "0.13.5", date: "2016-11-09", }, { version: "0.13.4", date: "2016-11-09", }, { version: "0.13.3", date: "2016-10-31", }, { version: "0.13.2", date: "2016-10-28", }, { version: "0.13.1", date: "2016-10-26", }, { version: "0.13.0", date: "2016-10-14", }, { version: "0.12.1", date: "2016-09-15", }, { version: "0.12.0", date: "2016-09-09", }, { version: "0.11.0", date: "2016-08-19", }, { version: "0.10.4", date: "2016-08-05", }, { version: "0.10.3", date: "2016-08-04", }, { version: "0.10.2", date: "2016-08-03", }, { version: "0.10.1", date: "2016-08-01", }, { version: "0.10.0", date: "2016-07-29", }, { version: "0.9.0", date: "2016-06-17", }, { version: "0.8.0", date: "2016-06-02", }, { version: "0.7.0", date: "2016-05-13", }, { version: "0.6.1", date: "2016-04-19", }, { version: "0.6.0", date: "2016-04-15", }, { version: "0.5.1", date: "2016-03-17", }, { version: "0.5.0", date: "2016-03-17", }, { version: "0.4.2", date: "2016-03-07", }, { version: "0.4.1", date: "2016-03-03", }, { version: "0.4.0", date: "2016-02-01", }, { version: "0.3.0", date: "2016-01-27", }, { version: "0.2.0", date: "2016-01-16", }, { version: "0.1.3", date: "2015-12-30", }, { version: "0.1.2", date: "2015-12-30", }, { version: "0.1.1", date: "2015-12-29", }, { version: "0.1.0", date: "2015-12-18", }, { version: "0.0.4", date: "2015-12-18", }, { version: "0.0.3", date: "2015-12-04", }, { version: "0.0.2", date: "2015-11-13", }, { version: "0.0.1", date: "2015-07-02", }, ], }; export default data; ================================================ FILE: website/src/pages/_components/landing-banner.tsx ================================================ import React from "react"; import LearnMoreLink from "../../components/LearnMoreLink"; export const LandingBanner = () => (

Like this project? You'll love working with us.

Contact us to learn more{" "} about our full range of services and offerings.

); ================================================ FILE: website/src/pages/_components/landing-demo.tsx ================================================ /* eslint-disable no-magic-numbers */ import React, { useState, useEffect } from "react"; import axios from "axios"; import last from "lodash/last"; import { format, startOfWeek, parse, subDays } from "date-fns"; import { VictoryLine, VictoryChart, VictoryLabel, VictoryAxis, VictoryScatter, VictoryVoronoiContainer, Point, } from "victory"; import downloads from "./data/downloads"; import versions from "./data/versions"; import { theme } from "./theme"; const font = (color = theme.color.brown) => ({ fill: color, fontSize: 20, fontFamily: "Helvetica", }); const numberWithCommas = (x) => x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); const groupDownloadsByWeek = (dates) => { const downloadsGroupedByPeriod = {}; const today = new Date(); dates.forEach((date) => { const start = format( startOfWeek(parse(date.day, "yyyy-MM-dd", today)), "yyyy-MM-dd", ); downloadsGroupedByPeriod[start] = downloadsGroupedByPeriod[start] ? downloadsGroupedByPeriod[start] + date.downloads : date.downloads; }); const weeklyDownloads = Object.entries(downloadsGroupedByPeriod).map( ([key, value]) => ({ date: key, downloads: value, }), ); // remove the last element in the array, as it may not be a full week weeklyDownloads.pop(); return weeklyDownloads; }; const minorVersions = versions.data.filter((v) => v.version.endsWith("0")); const latestVersion = versions.data[0].version; const voronoiBlacklist = minorVersions.map((v) => `ignore-${v.version}`); const LinkLabel = (props) => { const { x, index, version } = props; if (Number(index) || !version.label) { return null; } const versionDate = `${version.version}-${version.date}`; const hash = versionDate.replace(/[^\w-]+/g, ""); const linkStyle = font(theme.color.red); return ( {version.label} ); }; // eslint-disable-next-line react/no-multi-comp const VoronoiLabel = (props) => { const { datum, x, y, data } = props; if (last(data).downloads === datum.downloads) { return null; } const labelStyles = { fill: theme.color.white, fontSize: 20, fontFamily: "Helvetica", textAnchor: "middle", fontWeight: "bold", }; return ( ); }; const lastDate = last(downloads.data)!.day; const recentDate = format(subDays(new Date(), 2), "yyyy-MM-dd"); const oldDownloads = groupDownloadsByWeek(downloads.data); async function fetchData(url) { try { const result = await axios(url); const freshData = result.data; const allDownloads = downloads.data.concat(freshData.downloads); return groupDownloadsByWeek(allDownloads); } catch { return oldDownloads; } } // eslint-disable-next-line react/no-multi-comp export const LandingDemo = () => { const [downloadsPerWeek, setData] = useState(oldDownloads); const url = `https://api.npmjs.org/downloads/range/${lastDate}:${recentDate}/victory`; useEffect(() => { void fetchData(url).then(setData); }, [url]); return (
numberWithCommas(datum.downloads)} voronoiBlacklist={voronoiBlacklist} labelComponent={} /> } > ""} style={{ axis: { stroke: theme.color.white, strokeWidth: 3 }, }} scale={{ x: "time" }} /> {minorVersions.map((v) => ( new Date(v.date)} style={{ data: { stroke: theme.color.red, strokeWidth: v.label ? 3 : 1, }, }} labels={() => v.label} labelComponent={} groupComponent={} samples={2} /> ))} } y="downloads" x={(d) => new Date(d.date)} style={{ data: { stroke: theme.color.white, strokeWidth: 4 }, }} /> new Date(d.date)} size={6} style={{ data: { fill: theme.color.white }, labels: { verticalAnchor: "start" }, }} labelComponent={ } labels={({ datum }) => `${numberWithCommas(datum.downloads)}\nDOWNLOADS / WEEK` } />
); }; ================================================ FILE: website/src/pages/_components/landing-divider.tsx ================================================ import React from "react"; export const LandingDivider = () => (
); ================================================ FILE: website/src/pages/_components/landing-featured-projects.tsx ================================================ import React, { FunctionComponent, SVGProps, CSSProperties } from "react"; import { SpectacleBadge, FigLogBadge, EnvyBadge, UrqlBadge, } from "formidable-oss-badges"; import { LinkButton } from "../../components/link-button"; type BadgeProps = SVGProps & { className?: string; isHoverable?: boolean; style?: CSSProperties; simple?: boolean; }; type Projects = { name: string; Component: FunctionComponent; link: string; description: string; title?: string; }; const projects: Projects[] = [ { name: "spectacle", Component: SpectacleBadge, link: "https://commerce.nearform.com/open-source/spectacle", description: "A React.js based library for creating sleek presentations using JSX syntax with the ability to live demo your code!", }, { name: "figlog", Component: FigLogBadge, link: "https://github.com/FormidableLabs/FigLog", description: "FigLog is the easiest and most efficient way to document team decisions and the evolution of your changes in Figma.", title: "FigLog", }, { name: "envy", Component: EnvyBadge, link: "https://github.com/FormidableLabs/envy", description: "Envy will trace the network calls from every application in your stack and allow you to view them in a central place.", }, { name: "urql", Component: UrqlBadge, link: "https://commerce.nearform.com/open-source/urql/", description: "The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow.", }, ]; export const LandingFeaturedProjects = () => (

More Open Source from Nearform Commerce

{projects.map(({ name, Component, link, description, title }) => ( {title || name} {description} ))}
View All Projects
); ================================================ FILE: website/src/pages/_components/landing-features.tsx ================================================ import React from "react"; import { LinkButton } from "../../components/link-button"; import robustFeature from "./assets/feature-robust.png"; import flexibleFeature from "./assets/feature-flexible.png"; import nativeFeature from "./assets/feature-native.png"; const list = [ { imgSrc: robustFeature, alt: "Robust", title: "Robust", body: "Area charts. Scatter plots. Voronoi polygons. Easy-to-use components for complex charting.", }, { imgSrc: flexibleFeature, alt: "Flexible", title: "Flexible", body: "Fully contained, reusable data visualization elements are responsible for their own styles and behaviors.", }, { imgSrc: nativeFeature, alt: "Native", title: "Native", body: "Extend the Victory experience on Android and iOS platforms with an identical API.", }, ]; export const LandingFeatures = () => (

Features

    {list.map(({ alt, body, imgSrc, title }) => (
  • {alt} {title} {body}
  • ))}
Documentation
); ================================================ FILE: website/src/pages/_components/landing-hero.tsx ================================================ import React from "react"; import { VictoryBadge } from "formidable-oss-badges"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; export const LandingHero = () => { const { siteConfig } = useDocusaurusContext(); return (

{siteConfig.title}

{siteConfig.tagline}

); }; ================================================ FILE: website/src/pages/_components/landing-showcase.tsx ================================================ import React from "react"; import AirbnbLogo from "../../../static/logos/logo-airbnb.svg"; import ViacomLogo from "../../../static/logos/logo-viacom.svg"; import FiveThirtyEightLogo from "../../../static/logos/logo-fivethirtyeight.svg"; import UsaFactsLogo from "../../../static/logos/logo-usafacts.svg"; import RedfinLogo from "../../../static/logos/logo-redfin.svg"; import TuneLogo from "../../../static/logos/logo-tune.svg"; import ZillowLogo from "../../../static/logos/logo-zillow.svg"; import BenaroyaLogo from "../../../static/logos/logo-benaroya.svg"; const logoClass = "max-h-[100px] max-w-[200px] self-center justify-self-center"; export function LandingShowcase() { return (

A Few of Our Fans

); } ================================================ FILE: website/src/pages/_components/theme.ts ================================================ export const theme = { color: { paleRed: "#ffad9f", red: "#ff684f", darkRed: "#ad1b11", brown: "#bc5240", deepBrown: "#4a1b13", otherBrown: "#793d33", accentBrown: "#531f17", darkBrown: "#4c2e29", white: "#ffffff", nearWhite: "#f2f2f2", lightGray: "#f0f0f0", gray: "#999999", darkGray: "#DDD", darkestGray: "#4d4d4d", nearBlack: "#242121", black: "#1f1f1f", }, }; ================================================ FILE: website/src/pages/index.tsx ================================================ import React from "react"; import Layout from "@theme/Layout"; import { LandingHero } from "./_components/landing-hero"; import { LandingFeaturedProjects } from "./_components/landing-featured-projects"; import { LandingFeatures } from "./_components/landing-features"; import { LandingDemo } from "./_components/landing-demo"; import { LandingShowcase } from "./_components/landing-showcase"; import CalloutBanner from "../components/CalloutBanner"; // eslint-disable-next-line no-undef export default function Home(): JSX.Element { return ( ); } ================================================ FILE: website/src/pages/themes/_components/accordion.tsx ================================================ import React from "react"; import clsx from "clsx"; import { FaChevronDown } from "react-icons/fa"; type AccordionProps = { id: string; title: string; children: React.ReactNode; defaultOpen?: boolean; className?: string; }; const Accordion = ({ id, title, children, defaultOpen = false, className, }: AccordionProps) => { const [isOpen, setIsOpen] = React.useState(defaultOpen); const toggleAccordion = () => { setIsOpen(!isOpen); }; return (

{children}
); }; export default Accordion; ================================================ FILE: website/src/pages/themes/_components/alert.tsx ================================================ import clsx from "clsx"; import React, { useEffect, useState } from "react"; import { IoClose } from "react-icons/io5"; import { FaCircleCheck, FaCircleInfo, FaCircleXmark, FaTriangleExclamation, } from "react-icons/fa6"; export enum AlertType { SUCCESS = "success", ERROR = "error", INFO = "info", WARNING = "warning", } export type AlertProps = { id: string; type: AlertType; title: string; message?: string; duration?: number; }; type AlertComponentProps = Omit & { onClose: () => void; }; const alertStyles: Record< AlertComponentProps["type"], { iconStyle: string; typeStyle: string; buttonStyle: string; Icon: React.ComponentType>; } > = { success: { iconStyle: "text-green-400", typeStyle: "bg-green-50 text-green-800", buttonStyle: "hover:bg-green-100", Icon: FaCircleCheck, }, error: { iconStyle: "text-red-400", typeStyle: "bg-red-50 text-red-800", buttonStyle: "hover:bg-red-100", Icon: FaCircleXmark, }, info: { iconStyle: "text-blue-400", typeStyle: "bg-blue-50 text-blue-800", buttonStyle: "hover:bg-blue-100", Icon: FaCircleInfo, }, warning: { iconStyle: "text-yellow-400", typeStyle: "bg-yellow-50 text-yellow-800", buttonStyle: "hover:bg-yellow-100", Icon: FaTriangleExclamation, }, }; const DEFAULT_DURATION = 5000; const Alert = ({ type, title, message, onClose, duration = DEFAULT_DURATION, }: AlertComponentProps) => { const [isVisible, setIsVisible] = useState(false); useEffect(() => { setIsVisible(true); if (duration) { const timer = setTimeout(() => setIsVisible(false), duration); return () => clearTimeout(timer); } }, [onClose, duration]); const handleClose = () => { setIsVisible(false); }; const { Icon, typeStyle, iconStyle, buttonStyle } = alertStyles[type]; return (
!isVisible && onClose()} >

{title}

{message &&

{message}

}
); }; export default Alert; ================================================ FILE: website/src/pages/themes/_components/base-theme-panel.tsx ================================================ import React from "react"; import Select from "./select"; import { CUSTOM_THEME, themes, useTheme } from "../_providers/themeProvider"; import { usePreviewOptions } from "../_providers/previewOptionsProvider"; import PanelHeader from "./panel-header"; import Card from "./card"; import { codeItem } from "./sidenav"; import { TiArrowRight } from "react-icons/ti"; import { useSideNavContext } from "../_providers/sideNavProvider"; const themeOptions = themes.map((theme) => ({ label: theme.name, value: theme.name, })); const BaseThemePanel = () => { const { baseTheme, onBaseThemeSelect } = useTheme(); const { resetPreviewOptions } = usePreviewOptions(); const { setActiveSideNavItem } = useSideNavContext(); const handleThemeSelect = (themeName?: string) => { onBaseThemeSelect(themeName); resetPreviewOptions(); }; const isCustomTheme = baseTheme?.name === CUSTOM_THEME.name; return ( <> {controls.map((control, i) => { return ( ); })} ); }; export default ChartPanel; ================================================ FILE: website/src/pages/themes/_components/checkbox.tsx ================================================ import clsx from "clsx"; import React, { useId } from "react"; type CheckboxProps = { label?: string; isChecked?: boolean; onChange?: (isChecked: boolean) => void; className?: string; }; const Checkbox = ({ label = "Checkbox", isChecked = false, onChange, className, }: CheckboxProps) => { const handleChange = (event: React.ChangeEvent) => { if (onChange) { onChange(event.target.checked); } }; const id = useId(); return ( ); }; export default Checkbox; ================================================ FILE: website/src/pages/themes/_components/code-block.tsx ================================================ import clsx from "clsx"; import { Highlight } from "prism-react-renderer"; import React from "react"; import { FiCheck, FiCopy } from "react-icons/fi"; type CodeBlock = { title?: string; code: string; language: string; }; type CodeBlockProps = { className?: string; } & (CodeBlock | { blocks: CodeBlock[] }); const CodeBlock = (props: CodeBlockProps) => { const { className: classes } = props; const [copyStatus, setCopyStatus] = React.useState(null); const [activeBlockIndex, setActiveBlockIndex] = React.useState(0); const code = "blocks" in props ? props.blocks[activeBlockIndex].code : props.code; const language = "blocks" in props ? props.blocks[activeBlockIndex].language : props.language; const handleCopyThemeConfig = () => { navigator.clipboard .writeText(code) // eslint-disable-next-line promise/always-return .then(() => { setCopyStatus("Copied successfully."); }) .catch(() => { setCopyStatus("Failed to copy."); }); }; const handleBlockChange = (index: number) => { setActiveBlockIndex(index); setCopyStatus(null); }; return (
{"blocks" in props && (
{props.blocks.map(({ title }, index) => ( ))}
{copyStatus && ( {copyStatus} )}
)}
{({ className, style, tokens, getLineProps, getTokenProps }) => (
              {tokens.map((line, i) => (
                
{line.map((token, key) => ( ))}
))}
)}
); }; export default CodeBlock; ================================================ FILE: website/src/pages/themes/_components/code-panel.tsx ================================================ import React, { useEffect, useState } from "react"; import Editor from "@monaco-editor/react"; import { CUSTOM_THEME, useTheme } from "../_providers/themeProvider"; import { stringifyWithoutQuotes } from "../_utils"; import { Button } from "@site/src/components/button"; import { useAlert } from "../_providers/alertProvider"; import { AlertType } from "./alert"; const EDITOR_OPTIONS = { minimap: { enabled: false }, fontSize: 12, }; const CodePanel = () => { const { onBaseThemeSelect, customThemeConfig } = useTheme(); const { addAlert } = useAlert(); const [customTheme, setCustomTheme] = useState(() => stringifyWithoutQuotes(customThemeConfig), ); useEffect(() => { setCustomTheme(stringifyWithoutQuotes(customThemeConfig)); }, [customThemeConfig]); const handleCustomThemeChange = (value: string | undefined) => { setCustomTheme(value || ""); }; const applyCustomTheme = () => { try { const parsedTheme = new Function(`return (${customTheme.trim()});`)(); if (typeof parsedTheme !== "object" || Array.isArray(parsedTheme)) { addAlert({ type: AlertType.ERROR, title: "Invalid theme structure.", message: "Must be an object.", }); return; } addAlert({ type: AlertType.SUCCESS, title: "Changes applied successfully.", }); onBaseThemeSelect(CUSTOM_THEME.name, parsedTheme); } catch { addAlert({ type: AlertType.ERROR, title: "Invalid JavaScript object.", message: "Please check your theme configuration.", }); } }; const handleEditorMount = (_, monaco) => { monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ validate: false, enableSchemaRequest: false, schemas: [], }); }; return (

Theme Code

You can import your code by pasting your custom theme here or edit the existing theme configuration.

`candlestick.style.labels.padding` {" "} and{" "} `errorbar.borderWidth` {" "} are required for proper theme functionality.

); }; export default CodePanel; ================================================ FILE: website/src/pages/themes/_components/color-palette-selector.tsx ================================================ import React from "react"; import { VictoryThemeDefinition } from "victory"; import clsx from "clsx"; import { usePreviewOptions } from "../_providers/previewOptionsProvider"; import ColorPickerList from "./color-picker-list"; type ColorPaletteSelectorProps = { label: string; value: string; palette?: VictoryThemeDefinition["palette"]; className?: string; onColorsChange: (newColors: string[]) => void; }; const ColorPaletteSelector = ({ label, value, palette, className, onColorsChange, }: ColorPaletteSelectorProps) => { const { colorScale, updateColorScale } = usePreviewOptions(); const handleRadioChange = () => { updateColorScale(value); }; const handleColorsChange = (newColors) => { onColorsChange(newColors); updateColorScale(value); }; const isSelected = colorScale === value; return ( ); }; export default ColorPaletteSelector; ================================================ FILE: website/src/pages/themes/_components/color-picker-list.tsx ================================================ import React from "react"; import ColorPicker, { PLACEHOLDER_COLOR } from "./color-picker"; import clsx from "clsx"; import { TiPlus } from "react-icons/ti"; type ColorPickerListProps = { label?: string; colors?: string[]; onColorsChange: (newColors: string[]) => void; className?: string; }; const ColorPickerList = ({ label, colors = [], onColorsChange, className, }: ColorPickerListProps) => { const handleColorChange = (newColor, i) => { const updatedColors = [...colors]; updatedColors[i] = newColor; onColorsChange(updatedColors); }; const handleRemoveColor = (i) => { const updatedColors = [...colors]; updatedColors.splice(i, 1); onColorsChange(updatedColors); }; const handleAddColor = () => { const updatedColors = [...colors, PLACEHOLDER_COLOR]; onColorsChange(updatedColors); }; return (
{label && ( {label} )}
{colors.map((color, i) => ( handleColorChange(newColor, i)} onColorRemove={() => handleRemoveColor(i)} /> ))}
); }; export default ColorPickerList; ================================================ FILE: website/src/pages/themes/_components/color-picker.tsx ================================================ import React, { useId } from "react"; import { IoMdClose } from "react-icons/io"; import clsx from "clsx"; import Select from "./select"; type ColorPickerProps = { label?: string; color: string; onColorChange: (color?: string) => void; onColorRemove?: () => void; showSelectOptions?: boolean; className?: string; }; export const PLACEHOLDER_COLOR = "#000000"; const DEFAULT_COLOR = undefined; enum ColorPickerOptions { NONE = "none", CUSTOM = "custom", } const ColorPicker = ({ label, color, onColorChange, onColorRemove, showSelectOptions = false, className, }: ColorPickerProps) => { const [isPickerOpen, setIsPickerOpen] = React.useState(false); const [colorOption, setColorOption] = React.useState( () => { if (color === ColorPickerOptions.NONE || color === "transparent") { return ColorPickerOptions.NONE; } if (color === DEFAULT_COLOR) { return DEFAULT_COLOR; } return ColorPickerOptions.CUSTOM; }, ); const handleColorOptionChange = (value?: string) => { setColorOption(value); if (value === ColorPickerOptions.NONE) { onColorChange(ColorPickerOptions.NONE); } else if (value === ColorPickerOptions.CUSTOM) { onColorChange(PLACEHOLDER_COLOR); } else { onColorChange(DEFAULT_COLOR); } }; const handleChange = (event: React.ChangeEvent) => { if (onColorChange) { onColorChange(event.target.value); } }; const handleRemoveColor = () => { if (onColorRemove) onColorRemove(); }; const id = useId(); return (
{label && ( )}
{showSelectOptions && (
setIsPickerOpen(true)} onBlur={() => setIsPickerOpen(false)} />
)}
); }; export default ColorPicker; ================================================ FILE: website/src/pages/themes/_components/color-scale-override-selector.tsx ================================================ import React, { useCallback } from "react"; import clsx from "clsx"; import Toggle from "./toggle"; import { defaultColorScale, usePreviewOptions, } from "../_providers/previewOptionsProvider"; import ColorPickerList from "./color-picker-list"; type ColorScaleOverrideSelectorProps = { id: string; label?: string; colors?: string | string[]; onColorsChange: (colors: string[] | undefined) => void; hideDefaultToggle?: boolean; className?: string; }; const ColorScaleOverrideSelector = ({ id, label = "Color Scale", colors, onColorsChange, hideDefaultToggle = false, className, }: ColorScaleOverrideSelectorProps) => { const { colorScale, updateColorScale } = usePreviewOptions(); const [showCustomColors, setShowCustomColors] = React.useState( () => !!colors && Array.isArray(colors), ); const [prevColors, setPrevColors] = React.useState( Array.isArray(colors) ? colors : [], ); const setColorScaleToDefault = useCallback(() => { if (colorScale !== defaultColorScale) { updateColorScale(defaultColorScale); } }, [colorScale, updateColorScale]); const onCheckboxChange = (isChecked) => { setShowCustomColors(isChecked); onColorsChange(!isChecked ? undefined : prevColors); setColorScaleToDefault(); }; const handleColorsChange = (newColors) => { onColorsChange(newColors); setPrevColors(newColors); setColorScaleToDefault(); }; return (
{!hideDefaultToggle && ( )} {showCustomColors && typeof colors !== "string" && ( )}
); }; export default ColorScaleOverrideSelector; ================================================ FILE: website/src/pages/themes/_components/control.tsx ================================================ import React, { useId } from "react"; import Select from "./select"; import Slider from "./slider"; import ColorPicker from "./color-picker"; import ColorPaletteSelector from "./color-palette-selector"; import { getConfigValue } from "../_utils"; import { useTheme } from "../_providers/themeProvider"; import Accordion from "./accordion"; import ColorScaleOverrideSelector from "./color-scale-override-selector"; import PreviewColorScaleSelect from "./theme-preview/preview-color-scale-select"; export type ColorChangeArgs = { newColor?: string; index: number; colorScale: string; }; type ControlProps = { type: string; control: any; className?: string; }; const Control = ({ type, control, className }: ControlProps) => { const { customThemeConfig, updateCustomThemeConfig } = useTheme(); const handleChange = (newValue) => { updateCustomThemeConfig(control.path, newValue); }; const configValue = getConfigValue(customThemeConfig, control.path); const id = useId(); switch (type) { case "accordion": return ( {control.controls?.map((nestedControl, i) => ( ))} ); case "colorPalette": return ( ); case "section": return (

{control.label}

{control.description && (

{control.description}

)}
{control.controls?.map((nestedControl, i) => ( ))}
); case "slider": return ( ); case "select": return ( handleCheckboxChange(value)} className="mr-2" /> ))}
  • Save the Exported Theme File

    Save your custom generate theme to a file in your project. Use{" "} theme.js or theme.ts depending on whether your project uses JavaScript or TypeScript.

  • Import the Theme

    To use your custom theme in your application, import the file where you saved the theme configuration. The import path should match the file's location in your project directory.

  • Apply the Theme to Victory Components

    Once the theme is imported, you can apply it to Victory components by passing it as the theme prop.

    {/* Add your Victory components here */} `} language="jsx" />
  • ); }; export default ExportPanel; ================================================ FILE: website/src/pages/themes/_components/main.tsx ================================================ import React from "react"; import { useSideNavContext } from "../_providers/sideNavProvider"; import { ThemePreview } from "./theme-preview"; import BaseThemePanel from "./base-theme-panel"; import ChartPanel from "./chart-panel"; import OptionsPanel from "./options-panel"; import ExportPanel from "./export-panel"; import CodePanel from "./code-panel"; const Main = () => { const { activeSideNavItem } = useSideNavContext(); const isExportPanel = activeSideNavItem.panelType === "export"; if (isExportPanel) return ; if (activeSideNavItem.panelType === "code") return ; return ( <> ); }; export default Main; ================================================ FILE: website/src/pages/themes/_components/options-panel.tsx ================================================ import React from "react"; import Control from "./control"; import PanelHeader from "./panel-header"; import { OptionsPanelConfig } from "../_config"; type GlobalPanelProps = { config: OptionsPanelConfig; }; const OptionsPanel = ({ config: { title, description, controls }, }: GlobalPanelProps) => { return ( <> {controls.map((control, i) => { return ( ); })} ); }; export default OptionsPanel; ================================================ FILE: website/src/pages/themes/_components/panel-header.tsx ================================================ import React from "react"; type PanelHeaderProps = { title?: string; description?: string; }; const PanelHeader = ({ title, description }: PanelHeaderProps) => { return (
    {!!title && (

    {title}

    )} {!!description && (

    {description}

    )}
    ); }; export default PanelHeader; ================================================ FILE: website/src/pages/themes/_components/select.tsx ================================================ import React from "react"; import clsx from "clsx"; export type SelectOption = { label: string; value?: string; }; type SelectProps = { id: string; label?: string; options: SelectOption[]; value?: string; onChange: (value: string) => void; includeDefault?: boolean; className?: string; size?: "sm" | "md"; }; const Select = ({ id, label, options, value = "", onChange, includeDefault, className, size = "md", }: SelectProps) => { const handleChange = (event: React.ChangeEvent) => { if (onChange) { onChange(event.target.value); } }; const labelSizeClasses = size === "sm" ? "font-medium" : "font-semibold"; const selectSizeClasses = size === "sm" ? "text-sm px-2 py-1.5" : "text-base p-2"; return (
    {label && ( )}
    ); }; export default Select; ================================================ FILE: website/src/pages/themes/_components/sideNavButton.tsx ================================================ import clsx from "clsx"; import React from "react"; import { NavItem } from "./sidenav"; type SideNavButtonProps = { item: NavItem; Icon: React.ElementType; isActive: boolean; isDisabled?: boolean; onClick: () => void; }; const SideNavButton = ({ item, Icon, isActive, isDisabled = false, onClick, }: SideNavButtonProps) => { return ( ); }; export default SideNavButton; ================================================ FILE: website/src/pages/themes/_components/sidenav.tsx ================================================ import clsx from "clsx"; import React from "react"; import { chartOptionsConfig, ChartPanelConfig, globalOptionsConfig, OptionsPanelConfig, paletteOptionsConfig, } from "../_config"; import axisOptionsConfig from "../_config/axis"; import { useTheme } from "../_providers/themeProvider"; import { defaultColorScale, usePreviewOptions, } from "../_providers/previewOptionsProvider"; import { AllExamples, ExampleConfig, GroupExamples, PieExamples, StackExamples, } from "./examples"; import SideNavButton from "./sideNavButton"; import { useSideNavContext } from "../_providers/sideNavProvider"; import { AxisOptionsIcon, ChartOptionsIcon, ExportIcon } from "../_icons"; import { FiGlobe } from "react-icons/fi"; import { LuDatabase } from "react-icons/lu"; import { HiOutlineColorSwatch, HiCode } from "react-icons/hi"; export type NavItem = { title: string; Icon: React.ElementType; content: ExampleConfig[]; } & ( | { panelType: "theme" | "export" | "code"; } | { panelType: "default"; config: OptionsPanelConfig; } | { panelType: "chart"; config: ChartPanelConfig; } ); export const baseThemeItem: NavItem = { title: "Base Theme", Icon: LuDatabase, panelType: "theme", content: AllExamples, }; export const colorPaletteItem: NavItem = { title: "Color Palette", Icon: HiOutlineColorSwatch, config: paletteOptionsConfig, panelType: "default", content: [...StackExamples, ...GroupExamples, ...PieExamples], }; export const globalOptionsItem: NavItem = { title: "Global Options", Icon: FiGlobe, config: globalOptionsConfig, panelType: "default", content: AllExamples, }; export const axisOptionsItem: NavItem = { title: "Axis Options", Icon: AxisOptionsIcon, config: axisOptionsConfig, panelType: "chart", content: [], }; export const chartOptionsItem: NavItem = { title: "Chart Options", Icon: ChartOptionsIcon, config: chartOptionsConfig, panelType: "chart", content: [], }; export const NAV_ITEMS: NavItem[] = [ baseThemeItem, colorPaletteItem, globalOptionsItem, axisOptionsItem, chartOptionsItem, ]; export const codeItem: NavItem = { title: "Theme Code", Icon: HiCode, panelType: "code", content: [], }; export const exportItem: NavItem = { title: "Export Theme", Icon: ExportIcon, panelType: "export", content: [], }; const SideNav = () => { const { baseTheme } = useTheme(); const { activeSideNavItem, setActiveSideNavItem } = useSideNavContext(); const { setExampleContent, updateColorScale, setActiveChartType } = usePreviewOptions(); const isBaseThemeSelected = !!baseTheme; const handleItemSelect = (item: NavItem) => { setActiveChartType(null); setActiveSideNavItem(item); updateColorScale(defaultColorScale); setExampleContent(item.content); }; const isExportItemActive = activeSideNavItem.title === exportItem.title; const isCodeItemActive = activeSideNavItem.title === codeItem.title; return ( ); }; export default SideNav; ================================================ FILE: website/src/pages/themes/_components/slider.tsx ================================================ import React, { useState } from "react"; import clsx from "clsx"; import Toggle from "./toggle"; type SliderProps = { label: string; id: string; value?: number; defaultValue?: number; unit?: string; onChange?: (value?: number) => void; min?: number; max?: number; step?: number; className?: string; }; const DEFAULT_MIN = 1; const DEFAULT_MAX = 100; const Slider = ({ label, id, value, defaultValue, unit, onChange, min = DEFAULT_MIN, max = DEFAULT_MAX, step = 1, className, }: SliderProps) => { const [prevValue, setPrevValue] = useState(() => { return value ?? defaultValue ?? min; }); const handleSliderChange = (event: React.ChangeEvent) => { const newValue = event.target.valueAsNumber; if (onChange) { onChange(newValue); setPrevValue(newValue); } }; const handleNumberChange = (event: React.ChangeEvent) => { const newValue = parseFloat(event.target.value) || min; if (onChange) { onChange(newValue); } }; const handleToggle = (isChecked: boolean) => { if (!onChange) return; const newValue = isChecked ? undefined : prevValue; onChange(newValue); }; const isUsingDefault = value === undefined; return (
    {unit && {unit}}
    ); }; export default Slider; ================================================ FILE: website/src/pages/themes/_components/theme-preview/header.tsx ================================================ import React, { useState } from "react"; import { Button } from "@site/src/components/button"; import { useClickOutside } from "@site/src/hooks/useClickOutside"; import { Options } from "./options"; export function Header() { const [isSettingsMenuOpen, setIsSettingsMenuOpen] = useState(false); const ref = useClickOutside(() => { setIsSettingsMenuOpen((prev) => !prev); }); const handlePreviewClick = () => { setIsSettingsMenuOpen((prev) => !prev); }; return (

    Chart Previews

    ); } ================================================ FILE: website/src/pages/themes/_components/theme-preview/index.tsx ================================================ import React from "react"; import { ColorScalePropType } from "victory"; import { useTheme } from "../../_providers/themeProvider"; import { usePreviewOptions } from "../../_providers/previewOptionsProvider"; import { Preview } from "./preview"; import { Header } from "./header"; export function ThemePreview() { const { customThemeConfig } = useTheme(); const { colorScale, exampleContent, showTooltips } = usePreviewOptions(); if (!customThemeConfig) return null; return (
    {exampleContent.map((content, i) => ( 1} /> ))}
    ); } ================================================ FILE: website/src/pages/themes/_components/theme-preview/options.tsx ================================================ import React, { forwardRef } from "react"; import { usePreviewOptions } from "../../_providers/previewOptionsProvider"; import Toggle from "../toggle"; import PreviewColorScaleSelect from "./preview-color-scale-select"; type Props = { isOpen: boolean; }; export const Options = forwardRef(({ isOpen }, ref) => { const { showTooltips, setShowTooltips } = usePreviewOptions(); return (
    {isOpen && (

    Preview Settings

    )}
    ); }); ================================================ FILE: website/src/pages/themes/_components/theme-preview/preview-color-scale-select.tsx ================================================ import React, { useId } from "react"; import Select from "../select"; import { usePreviewOptions } from "../../_providers/previewOptionsProvider"; import { colorScaleOptions } from "../../_const"; import clsx from "clsx"; type PreviewColorScaleSelectProps = { label?: string; size?: "sm" | "md"; className?: string; }; const PreviewColorScaleSelect = ({ label = "Preview color scale", size = "md", className, }: PreviewColorScaleSelectProps) => { const { colorScale, updateColorScale } = usePreviewOptions(); const id = useId(); return (