@chipmobilesdk/rn-chart 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +129 -0
- package/README.md +45 -3
- package/lib/commonjs/a11y/summary.js +190 -0
- package/lib/commonjs/a11y/summary.js.map +1 -0
- package/lib/commonjs/contract/datasource.js +64 -0
- package/lib/commonjs/contract/datasource.js.map +1 -0
- package/lib/commonjs/contract/distribution.js +45 -0
- package/lib/commonjs/contract/distribution.js.map +1 -0
- package/lib/commonjs/contract/errors.js +88 -0
- package/lib/commonjs/contract/errors.js.map +1 -0
- package/lib/commonjs/contract/format.js +133 -0
- package/lib/commonjs/contract/format.js.map +1 -0
- package/lib/commonjs/contract/interaction.js +24 -0
- package/lib/commonjs/contract/interaction.js.map +1 -0
- package/lib/commonjs/contract/marker.js +55 -0
- package/lib/commonjs/contract/marker.js.map +1 -0
- package/lib/commonjs/contract/props.js +6 -0
- package/lib/commonjs/contract/props.js.map +1 -0
- package/lib/commonjs/contract/state.js +68 -0
- package/lib/commonjs/contract/state.js.map +1 -0
- package/lib/commonjs/contract/strings.js +54 -0
- package/lib/commonjs/contract/strings.js.map +1 -0
- package/lib/commonjs/contract/theme.js +116 -0
- package/lib/commonjs/contract/theme.js.map +1 -0
- package/lib/commonjs/contract/value.js +112 -0
- package/lib/commonjs/contract/value.js.map +1 -0
- package/lib/commonjs/geometry/bar.js +257 -0
- package/lib/commonjs/geometry/bar.js.map +1 -0
- package/lib/commonjs/geometry/distribution.js +197 -0
- package/lib/commonjs/geometry/distribution.js.map +1 -0
- package/lib/commonjs/geometry/heatmap.js +186 -0
- package/lib/commonjs/geometry/heatmap.js.map +1 -0
- package/lib/commonjs/geometry/hitTest.js +117 -0
- package/lib/commonjs/geometry/hitTest.js.map +1 -0
- package/lib/commonjs/geometry/layout.js +134 -0
- package/lib/commonjs/geometry/layout.js.map +1 -0
- package/lib/commonjs/geometry/markers.js +59 -0
- package/lib/commonjs/geometry/markers.js.map +1 -0
- package/lib/commonjs/geometry/path.js +166 -0
- package/lib/commonjs/geometry/path.js.map +1 -0
- package/lib/commonjs/geometry/progress.js +203 -0
- package/lib/commonjs/geometry/progress.js.map +1 -0
- package/lib/commonjs/geometry/scale.js +127 -0
- package/lib/commonjs/geometry/scale.js.map +1 -0
- package/lib/commonjs/geometry/ticks.js +120 -0
- package/lib/commonjs/geometry/ticks.js.map +1 -0
- package/lib/commonjs/geometry/timeSeries.js +157 -0
- package/lib/commonjs/geometry/timeSeries.js.map +1 -0
- package/lib/commonjs/geometry/types.js +43 -0
- package/lib/commonjs/geometry/types.js.map +1 -0
- package/lib/commonjs/index.js +284 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/react/ChartBoundary.js +82 -0
- package/lib/commonjs/react/ChartBoundary.js.map +1 -0
- package/lib/commonjs/react/ChartFrame.js +270 -0
- package/lib/commonjs/react/ChartFrame.js.map +1 -0
- package/lib/commonjs/react/ChartLegend.js +242 -0
- package/lib/commonjs/react/ChartLegend.js.map +1 -0
- package/lib/commonjs/react/ChartPlot.js +62 -0
- package/lib/commonjs/react/ChartPlot.js.map +1 -0
- package/lib/commonjs/react/ChartStateOverlay.js +89 -0
- package/lib/commonjs/react/ChartStateOverlay.js.map +1 -0
- package/lib/commonjs/react/ChartThemeProvider.js +51 -0
- package/lib/commonjs/react/ChartThemeProvider.js.map +1 -0
- package/lib/commonjs/react/charts.js +108 -0
- package/lib/commonjs/react/charts.js.map +1 -0
- package/lib/commonjs/react/pointEvent.js +35 -0
- package/lib/commonjs/react/pointEvent.js.map +1 -0
- package/lib/commonjs/react/useChartA11y.js +97 -0
- package/lib/commonjs/react/useChartA11y.js.map +1 -0
- package/lib/commonjs/react/useChartFormatters.js +66 -0
- package/lib/commonjs/react/useChartFormatters.js.map +1 -0
- package/lib/commonjs/react/useChartLayout.js +100 -0
- package/lib/commonjs/react/useChartLayout.js.map +1 -0
- package/lib/commonjs/react/useChartSelection.js +131 -0
- package/lib/commonjs/react/useChartSelection.js.map +1 -0
- package/lib/commonjs/react/useChartStatus.js +74 -0
- package/lib/commonjs/react/useChartStatus.js.map +1 -0
- package/lib/commonjs/render/Axis.js +88 -0
- package/lib/commonjs/render/Axis.js.map +1 -0
- package/lib/commonjs/render/Marks.js +184 -0
- package/lib/commonjs/render/Marks.js.map +1 -0
- package/lib/commonjs/validation/budgets.js +106 -0
- package/lib/commonjs/validation/budgets.js.map +1 -0
- package/lib/commonjs/validation/validateDatasource.js +189 -0
- package/lib/commonjs/validation/validateDatasource.js.map +1 -0
- package/lib/module/a11y/summary.js +184 -0
- package/lib/module/a11y/summary.js.map +1 -0
- package/lib/module/contract/datasource.js +57 -0
- package/lib/module/contract/datasource.js.map +1 -0
- package/lib/module/contract/distribution.js +40 -0
- package/lib/module/contract/distribution.js.map +1 -0
- package/lib/module/contract/errors.js +80 -0
- package/lib/module/contract/errors.js.map +1 -0
- package/lib/module/contract/format.js +123 -0
- package/lib/module/contract/format.js.map +1 -0
- package/lib/module/contract/interaction.js +20 -0
- package/lib/module/contract/interaction.js.map +1 -0
- package/lib/module/contract/marker.js +49 -0
- package/lib/module/contract/marker.js.map +1 -0
- package/lib/module/contract/props.js +4 -0
- package/lib/module/contract/props.js.map +1 -0
- package/lib/module/contract/state.js +61 -0
- package/lib/module/contract/state.js.map +1 -0
- package/lib/module/contract/strings.js +49 -0
- package/lib/module/contract/strings.js.map +1 -0
- package/lib/module/contract/theme.js +109 -0
- package/lib/module/contract/theme.js.map +1 -0
- package/lib/module/contract/value.js +101 -0
- package/lib/module/contract/value.js.map +1 -0
- package/lib/module/geometry/bar.js +253 -0
- package/lib/module/geometry/bar.js.map +1 -0
- package/lib/module/geometry/distribution.js +193 -0
- package/lib/module/geometry/distribution.js.map +1 -0
- package/lib/module/geometry/heatmap.js +182 -0
- package/lib/module/geometry/heatmap.js.map +1 -0
- package/lib/module/geometry/hitTest.js +110 -0
- package/lib/module/geometry/hitTest.js.map +1 -0
- package/lib/module/geometry/layout.js +122 -0
- package/lib/module/geometry/layout.js.map +1 -0
- package/lib/module/geometry/markers.js +53 -0
- package/lib/module/geometry/markers.js.map +1 -0
- package/lib/module/geometry/path.js +157 -0
- package/lib/module/geometry/path.js.map +1 -0
- package/lib/module/geometry/progress.js +199 -0
- package/lib/module/geometry/progress.js.map +1 -0
- package/lib/module/geometry/scale.js +119 -0
- package/lib/module/geometry/scale.js.map +1 -0
- package/lib/module/geometry/ticks.js +112 -0
- package/lib/module/geometry/ticks.js.map +1 -0
- package/lib/module/geometry/timeSeries.js +153 -0
- package/lib/module/geometry/timeSeries.js.map +1 -0
- package/lib/module/geometry/types.js +39 -0
- package/lib/module/geometry/types.js.map +1 -0
- package/lib/module/index.js +85 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/react/ChartBoundary.js +75 -0
- package/lib/module/react/ChartBoundary.js.map +1 -0
- package/lib/module/react/ChartFrame.js +263 -0
- package/lib/module/react/ChartFrame.js.map +1 -0
- package/lib/module/react/ChartLegend.js +234 -0
- package/lib/module/react/ChartLegend.js.map +1 -0
- package/lib/module/react/ChartPlot.js +56 -0
- package/lib/module/react/ChartPlot.js.map +1 -0
- package/lib/module/react/ChartStateOverlay.js +82 -0
- package/lib/module/react/ChartStateOverlay.js.map +1 -0
- package/lib/module/react/ChartThemeProvider.js +44 -0
- package/lib/module/react/ChartThemeProvider.js.map +1 -0
- package/lib/module/react/charts.js +102 -0
- package/lib/module/react/charts.js.map +1 -0
- package/lib/module/react/pointEvent.js +30 -0
- package/lib/module/react/pointEvent.js.map +1 -0
- package/lib/module/react/useChartA11y.js +92 -0
- package/lib/module/react/useChartA11y.js.map +1 -0
- package/lib/module/react/useChartFormatters.js +61 -0
- package/lib/module/react/useChartFormatters.js.map +1 -0
- package/lib/module/react/useChartLayout.js +95 -0
- package/lib/module/react/useChartLayout.js.map +1 -0
- package/lib/module/react/useChartSelection.js +125 -0
- package/lib/module/react/useChartSelection.js.map +1 -0
- package/lib/module/react/useChartStatus.js +69 -0
- package/lib/module/react/useChartStatus.js.map +1 -0
- package/lib/module/render/Axis.js +80 -0
- package/lib/module/render/Axis.js.map +1 -0
- package/lib/module/render/Marks.js +173 -0
- package/lib/module/render/Marks.js.map +1 -0
- package/lib/module/validation/budgets.js +97 -0
- package/lib/module/validation/budgets.js.map +1 -0
- package/lib/module/validation/validateDatasource.js +184 -0
- package/lib/module/validation/validateDatasource.js.map +1 -0
- package/lib/typescript/commonjs/a11y/summary.d.ts +38 -0
- package/lib/typescript/commonjs/a11y/summary.d.ts.map +1 -0
- package/lib/typescript/commonjs/contract/datasource.d.ts +94 -0
- package/lib/typescript/commonjs/contract/datasource.d.ts.map +1 -0
- package/lib/typescript/commonjs/contract/distribution.d.ts +17 -0
- package/lib/typescript/commonjs/contract/distribution.d.ts.map +1 -0
- package/lib/typescript/commonjs/contract/errors.d.ts +53 -0
- package/lib/typescript/commonjs/contract/errors.d.ts.map +1 -0
- package/lib/typescript/commonjs/contract/format.d.ts +63 -0
- package/lib/typescript/commonjs/contract/format.d.ts.map +1 -0
- package/lib/typescript/commonjs/contract/interaction.d.ts +53 -0
- package/lib/typescript/commonjs/contract/interaction.d.ts.map +1 -0
- package/lib/typescript/commonjs/contract/marker.d.ts +39 -0
- package/lib/typescript/commonjs/contract/marker.d.ts.map +1 -0
- package/lib/typescript/commonjs/contract/props.d.ts +58 -0
- package/lib/typescript/commonjs/contract/props.d.ts.map +1 -0
- package/lib/typescript/commonjs/contract/state.d.ts +45 -0
- package/lib/typescript/commonjs/contract/state.d.ts.map +1 -0
- package/lib/typescript/commonjs/contract/strings.d.ts +39 -0
- package/lib/typescript/commonjs/contract/strings.d.ts.map +1 -0
- package/lib/typescript/commonjs/contract/theme.d.ts +85 -0
- package/lib/typescript/commonjs/contract/theme.d.ts.map +1 -0
- package/lib/typescript/commonjs/contract/value.d.ts +69 -0
- package/lib/typescript/commonjs/contract/value.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/bar.d.ts +18 -0
- package/lib/typescript/commonjs/geometry/bar.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/distribution.d.ts +23 -0
- package/lib/typescript/commonjs/geometry/distribution.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/heatmap.d.ts +24 -0
- package/lib/typescript/commonjs/geometry/heatmap.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/hitTest.d.ts +68 -0
- package/lib/typescript/commonjs/geometry/hitTest.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/layout.d.ts +67 -0
- package/lib/typescript/commonjs/geometry/layout.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/markers.d.ts +29 -0
- package/lib/typescript/commonjs/geometry/markers.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/path.d.ts +66 -0
- package/lib/typescript/commonjs/geometry/path.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/progress.d.ts +17 -0
- package/lib/typescript/commonjs/geometry/progress.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/scale.d.ts +55 -0
- package/lib/typescript/commonjs/geometry/scale.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/ticks.d.ts +48 -0
- package/lib/typescript/commonjs/geometry/ticks.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/timeSeries.d.ts +20 -0
- package/lib/typescript/commonjs/geometry/timeSeries.d.ts.map +1 -0
- package/lib/typescript/commonjs/geometry/types.d.ts +91 -0
- package/lib/typescript/commonjs/geometry/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/index.d.ts +47 -0
- package/lib/typescript/commonjs/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/react/ChartBoundary.d.ts +47 -0
- package/lib/typescript/commonjs/react/ChartBoundary.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/ChartFrame.d.ts +31 -0
- package/lib/typescript/commonjs/react/ChartFrame.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/ChartLegend.d.ts +31 -0
- package/lib/typescript/commonjs/react/ChartLegend.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/ChartPlot.d.ts +19 -0
- package/lib/typescript/commonjs/react/ChartPlot.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/ChartStateOverlay.d.ts +25 -0
- package/lib/typescript/commonjs/react/ChartStateOverlay.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/ChartThemeProvider.d.ts +21 -0
- package/lib/typescript/commonjs/react/ChartThemeProvider.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/charts.d.ts +51 -0
- package/lib/typescript/commonjs/react/charts.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/pointEvent.d.ts +13 -0
- package/lib/typescript/commonjs/react/pointEvent.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/useChartA11y.d.ts +37 -0
- package/lib/typescript/commonjs/react/useChartA11y.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/useChartFormatters.d.ts +21 -0
- package/lib/typescript/commonjs/react/useChartFormatters.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/useChartLayout.d.ts +27 -0
- package/lib/typescript/commonjs/react/useChartLayout.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/useChartSelection.d.ts +52 -0
- package/lib/typescript/commonjs/react/useChartSelection.d.ts.map +1 -0
- package/lib/typescript/commonjs/react/useChartStatus.d.ts +22 -0
- package/lib/typescript/commonjs/react/useChartStatus.d.ts.map +1 -0
- package/lib/typescript/commonjs/render/Axis.d.ts +31 -0
- package/lib/typescript/commonjs/render/Axis.d.ts.map +1 -0
- package/lib/typescript/commonjs/render/Marks.d.ts +44 -0
- package/lib/typescript/commonjs/render/Marks.d.ts.map +1 -0
- package/lib/typescript/commonjs/validation/budgets.d.ts +24 -0
- package/lib/typescript/commonjs/validation/budgets.d.ts.map +1 -0
- package/lib/typescript/commonjs/validation/validateDatasource.d.ts +26 -0
- package/lib/typescript/commonjs/validation/validateDatasource.d.ts.map +1 -0
- package/lib/typescript/module/a11y/summary.d.ts +38 -0
- package/lib/typescript/module/a11y/summary.d.ts.map +1 -0
- package/lib/typescript/module/contract/datasource.d.ts +94 -0
- package/lib/typescript/module/contract/datasource.d.ts.map +1 -0
- package/lib/typescript/module/contract/distribution.d.ts +17 -0
- package/lib/typescript/module/contract/distribution.d.ts.map +1 -0
- package/lib/typescript/module/contract/errors.d.ts +53 -0
- package/lib/typescript/module/contract/errors.d.ts.map +1 -0
- package/lib/typescript/module/contract/format.d.ts +63 -0
- package/lib/typescript/module/contract/format.d.ts.map +1 -0
- package/lib/typescript/module/contract/interaction.d.ts +53 -0
- package/lib/typescript/module/contract/interaction.d.ts.map +1 -0
- package/lib/typescript/module/contract/marker.d.ts +39 -0
- package/lib/typescript/module/contract/marker.d.ts.map +1 -0
- package/lib/typescript/module/contract/props.d.ts +58 -0
- package/lib/typescript/module/contract/props.d.ts.map +1 -0
- package/lib/typescript/module/contract/state.d.ts +45 -0
- package/lib/typescript/module/contract/state.d.ts.map +1 -0
- package/lib/typescript/module/contract/strings.d.ts +39 -0
- package/lib/typescript/module/contract/strings.d.ts.map +1 -0
- package/lib/typescript/module/contract/theme.d.ts +85 -0
- package/lib/typescript/module/contract/theme.d.ts.map +1 -0
- package/lib/typescript/module/contract/value.d.ts +69 -0
- package/lib/typescript/module/contract/value.d.ts.map +1 -0
- package/lib/typescript/module/geometry/bar.d.ts +18 -0
- package/lib/typescript/module/geometry/bar.d.ts.map +1 -0
- package/lib/typescript/module/geometry/distribution.d.ts +23 -0
- package/lib/typescript/module/geometry/distribution.d.ts.map +1 -0
- package/lib/typescript/module/geometry/heatmap.d.ts +24 -0
- package/lib/typescript/module/geometry/heatmap.d.ts.map +1 -0
- package/lib/typescript/module/geometry/hitTest.d.ts +68 -0
- package/lib/typescript/module/geometry/hitTest.d.ts.map +1 -0
- package/lib/typescript/module/geometry/layout.d.ts +67 -0
- package/lib/typescript/module/geometry/layout.d.ts.map +1 -0
- package/lib/typescript/module/geometry/markers.d.ts +29 -0
- package/lib/typescript/module/geometry/markers.d.ts.map +1 -0
- package/lib/typescript/module/geometry/path.d.ts +66 -0
- package/lib/typescript/module/geometry/path.d.ts.map +1 -0
- package/lib/typescript/module/geometry/progress.d.ts +17 -0
- package/lib/typescript/module/geometry/progress.d.ts.map +1 -0
- package/lib/typescript/module/geometry/scale.d.ts +55 -0
- package/lib/typescript/module/geometry/scale.d.ts.map +1 -0
- package/lib/typescript/module/geometry/ticks.d.ts +48 -0
- package/lib/typescript/module/geometry/ticks.d.ts.map +1 -0
- package/lib/typescript/module/geometry/timeSeries.d.ts +20 -0
- package/lib/typescript/module/geometry/timeSeries.d.ts.map +1 -0
- package/lib/typescript/module/geometry/types.d.ts +91 -0
- package/lib/typescript/module/geometry/types.d.ts.map +1 -0
- package/lib/typescript/module/index.d.ts +47 -0
- package/lib/typescript/module/index.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/react/ChartBoundary.d.ts +47 -0
- package/lib/typescript/module/react/ChartBoundary.d.ts.map +1 -0
- package/lib/typescript/module/react/ChartFrame.d.ts +31 -0
- package/lib/typescript/module/react/ChartFrame.d.ts.map +1 -0
- package/lib/typescript/module/react/ChartLegend.d.ts +31 -0
- package/lib/typescript/module/react/ChartLegend.d.ts.map +1 -0
- package/lib/typescript/module/react/ChartPlot.d.ts +19 -0
- package/lib/typescript/module/react/ChartPlot.d.ts.map +1 -0
- package/lib/typescript/module/react/ChartStateOverlay.d.ts +25 -0
- package/lib/typescript/module/react/ChartStateOverlay.d.ts.map +1 -0
- package/lib/typescript/module/react/ChartThemeProvider.d.ts +21 -0
- package/lib/typescript/module/react/ChartThemeProvider.d.ts.map +1 -0
- package/lib/typescript/module/react/charts.d.ts +51 -0
- package/lib/typescript/module/react/charts.d.ts.map +1 -0
- package/lib/typescript/module/react/pointEvent.d.ts +13 -0
- package/lib/typescript/module/react/pointEvent.d.ts.map +1 -0
- package/lib/typescript/module/react/useChartA11y.d.ts +37 -0
- package/lib/typescript/module/react/useChartA11y.d.ts.map +1 -0
- package/lib/typescript/module/react/useChartFormatters.d.ts +21 -0
- package/lib/typescript/module/react/useChartFormatters.d.ts.map +1 -0
- package/lib/typescript/module/react/useChartLayout.d.ts +27 -0
- package/lib/typescript/module/react/useChartLayout.d.ts.map +1 -0
- package/lib/typescript/module/react/useChartSelection.d.ts +52 -0
- package/lib/typescript/module/react/useChartSelection.d.ts.map +1 -0
- package/lib/typescript/module/react/useChartStatus.d.ts +22 -0
- package/lib/typescript/module/react/useChartStatus.d.ts.map +1 -0
- package/lib/typescript/module/render/Axis.d.ts +31 -0
- package/lib/typescript/module/render/Axis.d.ts.map +1 -0
- package/lib/typescript/module/render/Marks.d.ts +44 -0
- package/lib/typescript/module/render/Marks.d.ts.map +1 -0
- package/lib/typescript/module/validation/budgets.d.ts +24 -0
- package/lib/typescript/module/validation/budgets.d.ts.map +1 -0
- package/lib/typescript/module/validation/validateDatasource.d.ts +26 -0
- package/lib/typescript/module/validation/validateDatasource.d.ts.map +1 -0
- package/package.json +40 -15
- package/skills/sdk-rn-chart/SKILL.md +49 -14
- package/src/a11y/summary.ts +34 -15
- package/src/contract/distribution.ts +44 -0
- package/src/contract/errors.ts +6 -0
- package/src/contract/interaction.ts +2 -2
- package/src/contract/strings.ts +2 -0
- package/src/geometry/bar.ts +27 -16
- package/src/geometry/distribution.ts +11 -6
- package/src/geometry/heatmap.ts +34 -17
- package/src/geometry/hitTest.ts +35 -0
- package/src/geometry/progress.ts +0 -1
- package/src/geometry/ticks.ts +0 -13
- package/src/geometry/timeSeries.ts +7 -2
- package/src/geometry/types.ts +11 -0
- package/src/index.ts +4 -1
- package/src/react/ChartFrame.tsx +126 -373
- package/src/react/ChartLegend.tsx +60 -3
- package/src/react/ChartPlot.tsx +57 -0
- package/src/react/ChartStateOverlay.tsx +94 -0
- package/src/react/charts.tsx +27 -12
- package/src/react/pointEvent.ts +40 -0
- package/src/react/useChartA11y.ts +113 -0
- package/src/react/useChartFormatters.ts +105 -0
- package/src/react/useChartLayout.ts +5 -2
- package/src/react/useChartSelection.ts +77 -40
- package/src/react/useChartStatus.ts +85 -0
- package/src/render/Axis.tsx +26 -22
- package/src/render/Marks.tsx +2 -9
- package/src/validation/budgets.ts +6 -4
- package/src/validation/validateDatasource.ts +38 -14
|
@@ -18,18 +18,36 @@ accessibility support and enforced dataset budgets.
|
|
|
18
18
|
- Shaping data to the datasource contract
|
|
19
19
|
- Theming series colors or building a legend
|
|
20
20
|
- Handling selection / interaction on a chart
|
|
21
|
-
- A chart
|
|
21
|
+
- A chart is slower than expected, or a dataset is larger than its budget
|
|
22
|
+
- A formatter or a prop appears to have no effect
|
|
22
23
|
|
|
23
24
|
## Check what it deliberately does not do
|
|
24
25
|
|
|
25
26
|
The README opens with `## What it does not do`. Read that first — it prevents building
|
|
26
27
|
a feature this package intentionally excludes.
|
|
27
28
|
|
|
28
|
-
##
|
|
29
|
+
## Dataset budgets are a development warning, not a limit
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
the
|
|
31
|
+
Nothing is enforced and nothing is refused: over budget a chart **still renders and
|
|
32
|
+
drops nothing**, warns once per chart in development, and in production does not even
|
|
33
|
+
measure the dataset. `DATASET_BUDGETS` and `checkBudget` are the figures and the check,
|
|
34
|
+
for a consumer that wants to aggregate or window its own data. Do not debug a "chart
|
|
35
|
+
refuses to render": no such behaviour exists. See `## Dataset budgets`.
|
|
36
|
+
|
|
37
|
+
## Memoization has two requirements
|
|
38
|
+
|
|
39
|
+
`datasource` must be referentially stable, and so must each **formatter function**
|
|
40
|
+
(module scope, or `useCallback`). The `formatters` object itself may be inline, as may
|
|
41
|
+
`range={{ from, to }}` — those are compared by what they carry. An unstable datasource
|
|
42
|
+
or formatter rebuilds every scale and path string on each parent render.
|
|
43
|
+
|
|
44
|
+
## What the numbers mean, where it is ambiguous
|
|
45
|
+
|
|
46
|
+
`DistributionChart` reads values as proportions (0..1, summing to 1) or as absolute
|
|
47
|
+
amounts, inferred from the data. Pass `valuesAre="absolute" | "proportions"` when counts
|
|
48
|
+
could be read as shares (two counts of `1`). Known values and instants must be finite:
|
|
49
|
+
`NaN` is a `nonFiniteValue` validation error, and a negative distribution segment is a
|
|
50
|
+
`negativeDistributionValue` one. Use `unknown()` for data you do not have.
|
|
33
51
|
|
|
34
52
|
## Chart theming is separate from app theming
|
|
35
53
|
|
|
@@ -38,18 +56,35 @@ Charts use `ChartThemeProvider` / `useChartTheme`, not the app-wide theme provid
|
|
|
38
56
|
|
|
39
57
|
## Public API
|
|
40
58
|
|
|
41
|
-
|
|
59
|
+
Components — all five families are memoized, and all share `BaseChartProps`
|
|
60
|
+
(`datasource`, `status`, `stateContent`, `title`, `legend`, `theme`, `formatters`,
|
|
61
|
+
`strings`, `accessibility`, `height`, `style`, `footer`, selection and visibility
|
|
62
|
+
props):
|
|
42
63
|
|
|
43
|
-
- `
|
|
44
|
-
|
|
45
|
-
- `
|
|
46
|
-
- `DATASET_BUDGETS`, `checkBudget`
|
|
47
|
-
- `isChartError`
|
|
64
|
+
- `BarChart` (`grouping`), `TimeSeriesChart` (`range`), `DistributionChart` (`shape`,
|
|
65
|
+
`innerRadiusRatio`, `valuesAre`), `ProgressChart`, `HeatmapChart` (`buckets`)
|
|
66
|
+
- `ChartLegend`, `ColorScaleLegend`, `buildLegendEntries`, `ChartThemeProvider`
|
|
48
67
|
|
|
49
|
-
|
|
68
|
+
Runtime:
|
|
50
69
|
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
- `known`, `unknown`, `notApplicable`, `withheld`, `isKnown`, `isWithheld`, `sumKnown`,
|
|
71
|
+
`participatesInTotals`
|
|
72
|
+
- `useChartTheme`, `defaultChartTheme`, `resolveSeriesColor`
|
|
73
|
+
- `findTarget`, `isUsableTarget`, `readProgress`, `sameSelection`
|
|
74
|
+
- `resolveStatus`, `drawsData`, `suppressesInteraction`, `STATUS_PRECEDENCE`
|
|
75
|
+
- `formatValueDefault`, `formatPositionDefault`, `formatLegendDefault`, `seriesRef`,
|
|
76
|
+
`NON_VALUE_TEXT`
|
|
77
|
+
- `buildChartSummary`, `buildPointAnnouncement`, `buildTotalAnnouncement`
|
|
78
|
+
- `DATASET_BUDGETS`, `checkBudget`, `isChartError`, `chartStrings`
|
|
79
|
+
- `FAMILY_POSITION_KINDS`, `familyAcceptsPositionKind`, `resolveFormat`, `resolveUnit`
|
|
80
|
+
|
|
81
|
+
Types: `ValueState`, `ChartDatasource`, `Series`, `DataPoint`, `Position`, `ChartFamily`,
|
|
82
|
+
`ChartStatus`, `ChartError`, `SelectionRef`, `PointInteractionEvent`, `ChartFormatters`,
|
|
83
|
+
`ChartTheme`, `LegendEntry`, `ColorScale`, `DistributionValues`, `ChartStrings`,
|
|
84
|
+
`BudgetCheck`, `DatasetBudget`
|
|
85
|
+
|
|
86
|
+
Use `sameSelection` to compare two selections; comparing the objects by reference
|
|
87
|
+
reports a change that did not happen.
|
|
53
88
|
|
|
54
89
|
## Where the detail lives
|
|
55
90
|
|
package/src/a11y/summary.ts
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* Withheld values never enter a summary — that would leak exactly what the
|
|
9
9
|
* withheld class exists to protect (FR-033).
|
|
10
10
|
*/
|
|
11
|
-
import type { ChartDatasource, ChartFamily } from '../contract/datasource';
|
|
11
|
+
import type { ChartDatasource, ChartFamily, Position } from '../contract/datasource';
|
|
12
|
+
import { formatPositionDefault } from '../contract/format';
|
|
12
13
|
import type { ReferenceMarker } from '../contract/marker';
|
|
13
14
|
import type { ChartStatus } from '../contract/state';
|
|
14
15
|
import { formatMessage } from '@chipmobilesdk/rn-core';
|
|
@@ -44,6 +45,8 @@ export interface SummaryInput<M = unknown> {
|
|
|
44
45
|
readonly status: ChartStatus;
|
|
45
46
|
readonly title?: string;
|
|
46
47
|
readonly formatValue: (value: ValueState) => string;
|
|
48
|
+
/** How a position is spoken; the app's axis formatter when it has one. */
|
|
49
|
+
readonly formatPosition?: (position: Position) => string;
|
|
47
50
|
readonly incomplete?: boolean;
|
|
48
51
|
/** The texts to speak; the English defaults when omitted. */
|
|
49
52
|
readonly strings?: ChartStrings;
|
|
@@ -53,9 +56,12 @@ function describeExtremes<M>(
|
|
|
53
56
|
datasource: ChartDatasource<M>,
|
|
54
57
|
formatValue: (v: ValueState) => string,
|
|
55
58
|
strings: ChartStrings,
|
|
59
|
+
formatPosition: (position: Position) => string,
|
|
56
60
|
): string {
|
|
57
|
-
|
|
58
|
-
let
|
|
61
|
+
type Extreme = { label: () => string; value: number; point: unknown };
|
|
62
|
+
let min: Extreme | undefined;
|
|
63
|
+
let max: Extreme | undefined;
|
|
64
|
+
const several = datasource.series.length > 1;
|
|
59
65
|
|
|
60
66
|
for (const series of datasource.series) {
|
|
61
67
|
for (const point of series.points) {
|
|
@@ -63,18 +69,25 @@ function describeExtremes<M>(
|
|
|
63
69
|
if (!isKnown(point.value)) {
|
|
64
70
|
continue;
|
|
65
71
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
// Named by where the point is. A time series used to name both
|
|
73
|
+
// extremes after the series, so a one-series chart reported a single
|
|
74
|
+
// value and lost its lowest (CHT-07). Built lazily: only two are read.
|
|
75
|
+
const label = (): string => {
|
|
76
|
+
if (point.position.kind === 'category') {
|
|
77
|
+
return point.position.category;
|
|
78
|
+
}
|
|
79
|
+
if (point.position.kind === 'matrix') {
|
|
80
|
+
return `${point.position.row} ${point.position.column}`;
|
|
81
|
+
}
|
|
82
|
+
const at = formatPosition(point.position);
|
|
83
|
+
return several ? `${series.label} ${at}` : at;
|
|
84
|
+
};
|
|
72
85
|
|
|
73
86
|
if (!min || point.value.value < min.value) {
|
|
74
|
-
min = { label, value: point.value.value };
|
|
87
|
+
min = { label, value: point.value.value, point };
|
|
75
88
|
}
|
|
76
89
|
if (!max || point.value.value > max.value) {
|
|
77
|
-
max = { label, value: point.value.value };
|
|
90
|
+
max = { label, value: point.value.value, point };
|
|
78
91
|
}
|
|
79
92
|
}
|
|
80
93
|
}
|
|
@@ -83,14 +96,15 @@ function describeExtremes<M>(
|
|
|
83
96
|
return '';
|
|
84
97
|
}
|
|
85
98
|
const maxText = formatValue({ kind: 'known', value: max.value });
|
|
86
|
-
|
|
99
|
+
// One figure only when the highest and the lowest are the same point.
|
|
100
|
+
if (min.point === max.point) {
|
|
87
101
|
return formatMessage(strings['chart.summary.single'], { value: maxText });
|
|
88
102
|
}
|
|
89
103
|
|
|
90
104
|
return formatMessage(strings['chart.summary.extremes'], {
|
|
91
|
-
maxLabel: max.label,
|
|
105
|
+
maxLabel: max.label(),
|
|
92
106
|
max: maxText,
|
|
93
|
-
minLabel: min.label,
|
|
107
|
+
minLabel: min.label(),
|
|
94
108
|
min: formatValue({ kind: 'known', value: min.value }),
|
|
95
109
|
});
|
|
96
110
|
}
|
|
@@ -149,7 +163,12 @@ export function buildChartSummary<M>(input: SummaryInput<M>): string {
|
|
|
149
163
|
heading,
|
|
150
164
|
statusPhrase,
|
|
151
165
|
scale,
|
|
152
|
-
describeExtremes(
|
|
166
|
+
describeExtremes(
|
|
167
|
+
input.datasource,
|
|
168
|
+
input.formatValue,
|
|
169
|
+
strings,
|
|
170
|
+
input.formatPosition ?? formatPositionDefault,
|
|
171
|
+
),
|
|
153
172
|
describeMarkers(input.datasource.markers, input.formatValue, strings),
|
|
154
173
|
input.incomplete ? strings['chart.summary.incomplete'] : '',
|
|
155
174
|
];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the numbers of a distribution mean (CHT-10).
|
|
3
|
+
*
|
|
4
|
+
* `proportions`: each value is already a share of the whole, in 0..1. A sum
|
|
5
|
+
* short of 1 is drawn short and reported incomplete. `absolute`: counts or
|
|
6
|
+
* amounts, normalised by their own total.
|
|
7
|
+
*
|
|
8
|
+
* Until 0.4.0 this was guessed from the values alone: anything in 0..1 was a
|
|
9
|
+
* proportion, so two segments counting 1 each drew two full circles on top of
|
|
10
|
+
* one another. The guess now also requires the shares to fit in one whole, and
|
|
11
|
+
* an app that knows can say so.
|
|
12
|
+
*/
|
|
13
|
+
import type { ChartDatasource } from './datasource';
|
|
14
|
+
import { isKnown } from './value';
|
|
15
|
+
|
|
16
|
+
export type DistributionValues = 'proportions' | 'absolute';
|
|
17
|
+
|
|
18
|
+
export const DISTRIBUTION_SUM_TOLERANCE = 0.001;
|
|
19
|
+
|
|
20
|
+
export function resolveDistributionValues<M>(
|
|
21
|
+
datasource: ChartDatasource<M>,
|
|
22
|
+
declared?: DistributionValues,
|
|
23
|
+
): DistributionValues {
|
|
24
|
+
if (declared !== undefined) {
|
|
25
|
+
return declared;
|
|
26
|
+
}
|
|
27
|
+
if (datasource.format?.kind === 'percent') {
|
|
28
|
+
return 'proportions';
|
|
29
|
+
}
|
|
30
|
+
let sum = 0;
|
|
31
|
+
for (const series of datasource.series) {
|
|
32
|
+
for (const point of series.points) {
|
|
33
|
+
if (!isKnown(point.value)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const value = point.value.value;
|
|
37
|
+
if (value < 0 || value > 1) {
|
|
38
|
+
return 'absolute';
|
|
39
|
+
}
|
|
40
|
+
sum += value;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return sum <= 1 + DISTRIBUTION_SUM_TOLERANCE ? 'proportions' : 'absolute';
|
|
44
|
+
}
|
package/src/contract/errors.ts
CHANGED
|
@@ -18,6 +18,8 @@ export type ChartErrorCode =
|
|
|
18
18
|
| 'familyPositionKindUnsupported'
|
|
19
19
|
| 'inconsistentMatrix'
|
|
20
20
|
| 'distributionNotWhole'
|
|
21
|
+
| 'nonFiniteValue'
|
|
22
|
+
| 'negativeDistributionValue'
|
|
21
23
|
| 'declaredByApp'
|
|
22
24
|
| 'renderException';
|
|
23
25
|
|
|
@@ -34,6 +36,10 @@ const MESSAGES: Readonly<Record<ChartErrorCode, string>> = {
|
|
|
34
36
|
inconsistentMatrix: 'Matrix datasource rows and columns are inconsistent across points.',
|
|
35
37
|
distributionNotWhole:
|
|
36
38
|
'Distribution segments do not sum to the whole. The chart marks the total incomplete rather than rescaling.',
|
|
39
|
+
nonFiniteValue:
|
|
40
|
+
'A known value or an instant is not a finite number. Use an unknown value for missing data.',
|
|
41
|
+
negativeDistributionValue:
|
|
42
|
+
'A distribution segment has a negative value. A share of a whole cannot be negative.',
|
|
37
43
|
declaredByApp: 'The application declared this chart to be in an error state.',
|
|
38
44
|
renderException:
|
|
39
45
|
'The chart failed while rendering and was contained. See the report for details.',
|
|
@@ -18,7 +18,7 @@ export interface SelectionRef {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export interface PointInteractionEvent<M = unknown> {
|
|
21
|
-
readonly type: 'pointSelect'
|
|
21
|
+
readonly type: 'pointSelect';
|
|
22
22
|
readonly seriesId: string;
|
|
23
23
|
readonly pointId: string;
|
|
24
24
|
readonly value: ValueState;
|
|
@@ -27,7 +27,7 @@ export interface PointInteractionEvent<M = unknown> {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export interface SeriesInteractionEvent<M = unknown> {
|
|
30
|
-
readonly type: '
|
|
30
|
+
readonly type: 'visibilityChange';
|
|
31
31
|
readonly seriesId: string;
|
|
32
32
|
readonly visible?: boolean;
|
|
33
33
|
readonly metadata?: M;
|
package/src/contract/strings.ts
CHANGED
|
@@ -41,6 +41,8 @@ export const chartStrings = defineMessages({
|
|
|
41
41
|
'chart.state.noPermission': 'Not available',
|
|
42
42
|
'chart.legend.hide': 'Hides this series',
|
|
43
43
|
'chart.legend.show': 'Shows this series',
|
|
44
|
+
'chart.legend.scale': 'Colour scale from {min} to {max}',
|
|
45
|
+
'chart.note.incomplete': 'Some values are unavailable, so totals are incomplete.',
|
|
44
46
|
},
|
|
45
47
|
});
|
|
46
48
|
|
package/src/geometry/bar.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { insetForAxes, plotBottom, plotRight } from './layout';
|
|
|
14
14
|
import { horizontalMarkers, markerDomainValues } from './markers';
|
|
15
15
|
import { rectBatchPath, type RectSpec } from './path';
|
|
16
16
|
import { bandScale, domainFrom, linearScale, widenDegenerateDomain } from './scale';
|
|
17
|
-
import { thinTicks, type Tick } from './ticks';
|
|
17
|
+
import { niceTickValues, thinTicks, type Tick } from './ticks';
|
|
18
18
|
import type { GeometryInput, MarkPath, ResolvedGeometry } from './types';
|
|
19
19
|
|
|
20
20
|
export type BarGrouping = 'grouped' | 'stacked';
|
|
@@ -75,21 +75,24 @@ function stackedDomainValues<M>(
|
|
|
75
75
|
): number[] {
|
|
76
76
|
const values: number[] = [];
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
negative += point.value.value;
|
|
89
|
-
}
|
|
78
|
+
// One pass over the points instead of a search per category and series.
|
|
79
|
+
const positive = new Map<string, number>();
|
|
80
|
+
const negative = new Map<string, number>();
|
|
81
|
+
for (const series of visible) {
|
|
82
|
+
const counted = new Set<string>();
|
|
83
|
+
for (const point of series.points) {
|
|
84
|
+
const category = categoryKey(point.position);
|
|
85
|
+
// The first point of a series in a category is the one drawn there.
|
|
86
|
+
if (counted.has(category) || !isKnown(point.value)) {
|
|
87
|
+
continue;
|
|
90
88
|
}
|
|
89
|
+
counted.add(category);
|
|
90
|
+
const sums = point.value.value >= 0 ? positive : negative;
|
|
91
|
+
sums.set(category, (sums.get(category) ?? 0) + point.value.value);
|
|
91
92
|
}
|
|
92
|
-
|
|
93
|
+
}
|
|
94
|
+
for (const category of categories) {
|
|
95
|
+
values.push(positive.get(category) ?? 0, negative.get(category) ?? 0);
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
return values;
|
|
@@ -149,6 +152,13 @@ export function buildBarGeometry<M>(
|
|
|
149
152
|
}
|
|
150
153
|
}
|
|
151
154
|
|
|
155
|
+
// Instants have an order of their own. First-seen order put Tuesday before
|
|
156
|
+
// Monday when one series started a day late, and disagreed with the
|
|
157
|
+
// time-series chart drawn from the same datasource (CHT-20).
|
|
158
|
+
if (datasource.positionKind === 'instant') {
|
|
159
|
+
categories.sort((a, b) => Number(a) - Number(b));
|
|
160
|
+
}
|
|
161
|
+
|
|
152
162
|
const visible = datasource.series.filter(s => !s.hidden);
|
|
153
163
|
const excluded = countExcluded(datasource);
|
|
154
164
|
const values = [
|
|
@@ -168,7 +178,8 @@ export function buildBarGeometry<M>(
|
|
|
168
178
|
min: values.some(v => v < 0) ? padded.min : Math.max(padded.min, 0),
|
|
169
179
|
max: values.some(v => v > 0) ? padded.max : Math.min(padded.max, 0),
|
|
170
180
|
});
|
|
171
|
-
|
|
181
|
+
// Round steps a reader can count in, not the padded extremes (105.3).
|
|
182
|
+
const yTickValues = niceTickValues(domain, 4);
|
|
172
183
|
const xLabel = (c: string) =>
|
|
173
184
|
datasource.positionKind === 'instant'
|
|
174
185
|
? input.formatInstant(Number(c))
|
|
@@ -219,7 +230,7 @@ export function buildBarGeometry<M>(
|
|
|
219
230
|
y: top,
|
|
220
231
|
width: barWidth,
|
|
221
232
|
height,
|
|
222
|
-
radius: options.radius,
|
|
233
|
+
radius: options.radius ?? input.radius?.bar,
|
|
223
234
|
});
|
|
224
235
|
|
|
225
236
|
hitRegions.push({
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* PURE. No React, no SVG.
|
|
10
10
|
*/
|
|
11
11
|
import type { ChartDatasource } from '../contract/datasource';
|
|
12
|
+
import { resolveDistributionValues, type DistributionValues } from '../contract/distribution';
|
|
12
13
|
import { segmentKey } from '../contract/theme';
|
|
13
14
|
import { isKnown, isWithheld, sumKnown } from '../contract/value';
|
|
14
15
|
import type { HitRegion } from './hitTest';
|
|
@@ -21,6 +22,8 @@ export interface DistributionOptions {
|
|
|
21
22
|
readonly shape?: DistributionShape;
|
|
22
23
|
/** Donut hole as a fraction of the outer radius. */
|
|
23
24
|
readonly innerRadiusRatio?: number;
|
|
25
|
+
/** What the values mean; inferred from the data when omitted. */
|
|
26
|
+
readonly valuesAre?: DistributionValues;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
const TAU = Math.PI * 2;
|
|
@@ -69,10 +72,11 @@ export function buildDistributionGeometry<M>(
|
|
|
69
72
|
// Proportions supplied directly (each in 0..1, summing to ~1) are honoured
|
|
70
73
|
// as given; absolute values are normalised by their own total. Either way we
|
|
71
74
|
// never stretch a short sum to fill the circle.
|
|
72
|
-
const suppliedAsProportions =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
const suppliedAsProportions =
|
|
76
|
+
resolveDistributionValues(datasource, options.valuesAre) === 'proportions';
|
|
77
|
+
// Shares declared as proportions that add up past the whole are normalised
|
|
78
|
+
// rather than drawn over one another.
|
|
79
|
+
const denominator = suppliedAsProportions ? Math.max(1, total.total) : total.total;
|
|
76
80
|
const coverage = total.total / denominator;
|
|
77
81
|
const incomplete = total.incomplete || coverage < 0.999;
|
|
78
82
|
|
|
@@ -88,7 +92,7 @@ export function buildDistributionGeometry<M>(
|
|
|
88
92
|
let cursor = plot.x;
|
|
89
93
|
|
|
90
94
|
for (const { series, point } of segments) {
|
|
91
|
-
if (!isKnown(point.value)) {
|
|
95
|
+
if (!isKnown(point.value) || point.value.value < 0) {
|
|
92
96
|
continue;
|
|
93
97
|
}
|
|
94
98
|
const fraction = point.value.value / denominator;
|
|
@@ -135,7 +139,7 @@ export function buildDistributionGeometry<M>(
|
|
|
135
139
|
let angle = 0;
|
|
136
140
|
|
|
137
141
|
for (const { series, point } of segments) {
|
|
138
|
-
if (!isKnown(point.value)) {
|
|
142
|
+
if (!isKnown(point.value) || point.value.value < 0) {
|
|
139
143
|
continue;
|
|
140
144
|
}
|
|
141
145
|
|
|
@@ -180,6 +184,7 @@ export function buildDistributionGeometry<M>(
|
|
|
180
184
|
pointId: point.id,
|
|
181
185
|
x: cx + labelRadius * Math.cos(mid),
|
|
182
186
|
y: cy + labelRadius * Math.sin(mid),
|
|
187
|
+
arc: { cx, cy, innerRadius, outerRadius, startAngle: start, endAngle: end },
|
|
183
188
|
selectable: !isWithheld(point.value),
|
|
184
189
|
});
|
|
185
190
|
}
|
package/src/geometry/heatmap.ts
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
import type { ChartDatasource } from '../contract/datasource';
|
|
16
16
|
import { isKnown, isWithheld } from '../contract/value';
|
|
17
17
|
import type { HitRegion } from './hitTest';
|
|
18
|
+
import { insetForAxes } from './layout';
|
|
18
19
|
import { rectBatchPath, type RectSpec } from './path';
|
|
20
|
+
import { thinTicks } from './ticks';
|
|
19
21
|
import type { GeometryInput, MarkPath, ResolvedGeometry } from './types';
|
|
20
22
|
|
|
21
23
|
export interface HeatmapOptions {
|
|
@@ -30,8 +32,6 @@ export function buildHeatmapGeometry<M>(
|
|
|
30
32
|
input: GeometryInput,
|
|
31
33
|
options: HeatmapOptions = {},
|
|
32
34
|
): ResolvedGeometry {
|
|
33
|
-
const { plot } = input;
|
|
34
|
-
|
|
35
35
|
const rows: string[] = [];
|
|
36
36
|
const columns: string[] = [];
|
|
37
37
|
const rowSeen = new Set<string>();
|
|
@@ -53,9 +53,9 @@ export function buildHeatmapGeometry<M>(
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
if (rows.length === 0 || columns.length === 0 || plot.width <= 0) {
|
|
56
|
+
if (rows.length === 0 || columns.length === 0 || input.plot.width <= 0) {
|
|
57
57
|
return {
|
|
58
|
-
plot,
|
|
58
|
+
plot: input.plot,
|
|
59
59
|
paths: [],
|
|
60
60
|
markers: [],
|
|
61
61
|
labels: [],
|
|
@@ -66,6 +66,18 @@ export function buildHeatmapGeometry<M>(
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// Row and column names are the heatmap's axes. The plot makes room for
|
|
70
|
+
// them; they were computed before and never drawn (CHT-15).
|
|
71
|
+
const columnLabels = columns.map(input.formatCategory);
|
|
72
|
+
const rowLabels = rows.map(input.formatCategory);
|
|
73
|
+
const plot = insetForAxes(input.plot, {
|
|
74
|
+
xLabels: columnLabels,
|
|
75
|
+
yLabels: rowLabels,
|
|
76
|
+
fontSize: input.fontSize,
|
|
77
|
+
});
|
|
78
|
+
const columnIndex = new Map(columns.map((column, index) => [column, index]));
|
|
79
|
+
const rowIndex = new Map(rows.map((row, index) => [row, index]));
|
|
80
|
+
|
|
69
81
|
const gap = options.cellGap ?? 2;
|
|
70
82
|
const cellWidth = plot.width / columns.length;
|
|
71
83
|
const cellHeight = plot.height / rows.length;
|
|
@@ -102,18 +114,18 @@ export function buildHeatmapGeometry<M>(
|
|
|
102
114
|
continue;
|
|
103
115
|
}
|
|
104
116
|
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
if (
|
|
117
|
+
const col = columnIndex.get(point.position.column);
|
|
118
|
+
const row = rowIndex.get(point.position.row);
|
|
119
|
+
if (col === undefined || row === undefined) {
|
|
108
120
|
continue;
|
|
109
121
|
}
|
|
110
122
|
|
|
111
123
|
const rect: RectSpec = {
|
|
112
|
-
x: plot.x +
|
|
113
|
-
y: plot.y +
|
|
124
|
+
x: plot.x + col * cellWidth + gap / 2,
|
|
125
|
+
y: plot.y + row * cellHeight + gap / 2,
|
|
114
126
|
width: Math.max(0, cellWidth - gap),
|
|
115
127
|
height: Math.max(0, cellHeight - gap),
|
|
116
|
-
radius: options.cellRadius,
|
|
128
|
+
radius: options.cellRadius ?? input.radius?.cell,
|
|
117
129
|
};
|
|
118
130
|
|
|
119
131
|
if (isKnown(point.value)) {
|
|
@@ -173,17 +185,22 @@ export function buildHeatmapGeometry<M>(
|
|
|
173
185
|
paths,
|
|
174
186
|
markers: [],
|
|
175
187
|
labels: [],
|
|
176
|
-
xTicks:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
188
|
+
xTicks: thinTicks(
|
|
189
|
+
columnLabels.map((label, i) => ({
|
|
190
|
+
value: i,
|
|
191
|
+
position: plot.x + i * cellWidth + cellWidth / 2,
|
|
192
|
+
label,
|
|
193
|
+
})),
|
|
194
|
+
input.fontSize,
|
|
195
|
+
plot.width,
|
|
196
|
+
),
|
|
197
|
+
yTicks: rowLabels.map((label, i) => ({
|
|
182
198
|
value: i,
|
|
183
199
|
position: plot.y + i * cellHeight + cellHeight / 2,
|
|
184
|
-
label
|
|
200
|
+
label,
|
|
185
201
|
})),
|
|
186
202
|
hitRegions,
|
|
203
|
+
...(hasKnown ? { colorScale: { min, max, colors: ramp.slice(0, bucketCount) } } : {}),
|
|
187
204
|
incomplete: excluded > 0,
|
|
188
205
|
};
|
|
189
206
|
}
|
package/src/geometry/hitTest.ts
CHANGED
|
@@ -23,6 +23,19 @@ export interface HitRegion {
|
|
|
23
23
|
readonly width: number;
|
|
24
24
|
readonly height: number;
|
|
25
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* An arc segment: containment by radius and angle. Angles are radians,
|
|
28
|
+
* clockwise from twelve o'clock, as the distribution geometry draws them.
|
|
29
|
+
* A region with an arc is hit only inside it, never by proximity.
|
|
30
|
+
*/
|
|
31
|
+
readonly arc?: {
|
|
32
|
+
readonly cx: number;
|
|
33
|
+
readonly cy: number;
|
|
34
|
+
readonly innerRadius: number;
|
|
35
|
+
readonly outerRadius: number;
|
|
36
|
+
readonly startAngle: number;
|
|
37
|
+
readonly endAngle: number;
|
|
38
|
+
};
|
|
26
39
|
/**
|
|
27
40
|
* False for points that must never be selectable: withheld values, and
|
|
28
41
|
* anything the chart is not currently drawing (FR-053).
|
|
@@ -47,6 +60,20 @@ function inBounds(region: HitRegion, x: number, y: number): boolean {
|
|
|
47
60
|
return x >= b.x && x <= b.x + b.width && y >= b.y && y <= b.y + b.height;
|
|
48
61
|
}
|
|
49
62
|
|
|
63
|
+
const TAU = Math.PI * 2;
|
|
64
|
+
|
|
65
|
+
function inArc(arc: NonNullable<HitRegion['arc']>, x: number, y: number): boolean {
|
|
66
|
+
const dx = x - arc.cx;
|
|
67
|
+
const dy = y - arc.cy;
|
|
68
|
+
const radius = Math.sqrt(dx * dx + dy * dy);
|
|
69
|
+
if (radius < arc.innerRadius || radius > arc.outerRadius) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
// Clockwise from the top, the convention the arcs are drawn in.
|
|
73
|
+
const angle = (Math.atan2(dy, dx) + Math.PI / 2 + TAU) % TAU;
|
|
74
|
+
return angle >= arc.startAngle && angle < arc.endAngle;
|
|
75
|
+
}
|
|
76
|
+
|
|
50
77
|
/**
|
|
51
78
|
* Resolves a touch to a mark.
|
|
52
79
|
*
|
|
@@ -70,6 +97,14 @@ export function hitTest(
|
|
|
70
97
|
if (inBounds(region, x, y)) {
|
|
71
98
|
return { seriesId: region.seriesId, pointId: region.pointId, distance: 0 };
|
|
72
99
|
}
|
|
100
|
+
if (region.arc) {
|
|
101
|
+
// A large segment's edge is far from its midpoint, and a neighbour's
|
|
102
|
+
// midpoint can be nearer: only the segment under the finger counts.
|
|
103
|
+
if (inArc(region.arc, x, y)) {
|
|
104
|
+
return { seriesId: region.seriesId, pointId: region.pointId, distance: 0 };
|
|
105
|
+
}
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
73
108
|
|
|
74
109
|
const dx = region.x - x;
|
|
75
110
|
const dy = region.y - y;
|
package/src/geometry/progress.ts
CHANGED
|
@@ -17,7 +17,6 @@ import { rectSubpath } from './path';
|
|
|
17
17
|
import type { GeometryInput, GeometryLabel, MarkerLine, MarkPath, ResolvedGeometry } from './types';
|
|
18
18
|
|
|
19
19
|
export interface ProgressOptions {
|
|
20
|
-
readonly orientation?: 'horizontal' | 'vertical';
|
|
21
20
|
readonly trackRadius?: number;
|
|
22
21
|
}
|
|
23
22
|
|
package/src/geometry/ticks.ts
CHANGED
|
@@ -130,16 +130,3 @@ export function reserveAxisSpace(
|
|
|
130
130
|
}
|
|
131
131
|
return widest + LABEL_GUTTER;
|
|
132
132
|
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Shortens a label that cannot fit, signalling the truncation with an ellipsis.
|
|
136
|
-
* Callers must expose the full value through an accessible label (FR-070).
|
|
137
|
-
*/
|
|
138
|
-
export function truncateToWidth(text: string, fontSize: number, maxWidthPx: number): string {
|
|
139
|
-
if (estimateTextWidth(text, fontSize) <= maxWidthPx) {
|
|
140
|
-
return text;
|
|
141
|
-
}
|
|
142
|
-
const perChar = fontSize * AVERAGE_ADVANCE_RATIO;
|
|
143
|
-
const fits = Math.max(1, Math.floor(maxWidthPx / perChar) - 1);
|
|
144
|
-
return `${text.slice(0, fits)}…`;
|
|
145
|
-
}
|
|
@@ -50,8 +50,13 @@ export function buildTimeSeriesGeometry<M>(
|
|
|
50
50
|
const values: number[] = [];
|
|
51
51
|
let excluded = 0;
|
|
52
52
|
|
|
53
|
+
// Sorted and windowed once per series; both passes below read the result.
|
|
54
|
+
const ordered = new Map(
|
|
55
|
+
datasource.series.map(series => [series, visiblePoints(series, options.range)] as const),
|
|
56
|
+
);
|
|
57
|
+
|
|
53
58
|
for (const series of datasource.series) {
|
|
54
|
-
for (const point of
|
|
59
|
+
for (const point of ordered.get(series) ?? []) {
|
|
55
60
|
if (point.position.kind === 'instant') {
|
|
56
61
|
instants.push(point.position.at);
|
|
57
62
|
}
|
|
@@ -104,7 +109,7 @@ export function buildTimeSeriesGeometry<M>(
|
|
|
104
109
|
|
|
105
110
|
const pts: MaybePt[] = [];
|
|
106
111
|
|
|
107
|
-
for (const point of
|
|
112
|
+
for (const point of ordered.get(series) ?? []) {
|
|
108
113
|
const at = point.position.kind === 'instant' ? point.position.at : 0;
|
|
109
114
|
const px = x.project(at);
|
|
110
115
|
|
package/src/geometry/types.ts
CHANGED
|
@@ -46,6 +46,13 @@ export interface GeometryLabel {
|
|
|
46
46
|
readonly muted?: boolean;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/** What a colour ramp runs from and to, for the legend that explains it. */
|
|
50
|
+
export interface ColorScale {
|
|
51
|
+
readonly min: number;
|
|
52
|
+
readonly max: number;
|
|
53
|
+
readonly colors: readonly string[];
|
|
54
|
+
}
|
|
55
|
+
|
|
49
56
|
export interface ResolvedGeometry {
|
|
50
57
|
readonly plot: PlotRect;
|
|
51
58
|
readonly paths: readonly MarkPath[];
|
|
@@ -54,6 +61,8 @@ export interface ResolvedGeometry {
|
|
|
54
61
|
readonly xTicks: readonly Tick[];
|
|
55
62
|
readonly yTicks: readonly Tick[];
|
|
56
63
|
readonly hitRegions: readonly HitRegion[];
|
|
64
|
+
/** Present when colour encodes a value, as in a heatmap. */
|
|
65
|
+
readonly colorScale?: ColorScale;
|
|
57
66
|
/**
|
|
58
67
|
* True when something was excluded from a total or proportion, so the chart
|
|
59
68
|
* can say so rather than presenting a partial figure as whole (FR-013).
|
|
@@ -88,6 +97,8 @@ export interface GeometryInput {
|
|
|
88
97
|
Record<'unknown' | 'notApplicable' | 'withheld', NonValueTexture>
|
|
89
98
|
>;
|
|
90
99
|
readonly heatmapRamp: readonly string[];
|
|
100
|
+
/** Corner radii from the theme, for bars and heatmap cells. */
|
|
101
|
+
readonly radius?: { readonly bar: number; readonly cell: number };
|
|
91
102
|
readonly formatValue: (value: number) => string;
|
|
92
103
|
readonly formatCategory: (raw: string) => string;
|
|
93
104
|
readonly formatInstant: (at: number) => string;
|