@cdx-ui/components 0.3.5 → 0.3.7
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/README.md +1 -1
- package/coverage.svg +1 -1
- package/docs/components/bar-chart/content.md +703 -0
- package/docs/components/content.md +10 -1
- package/docs/components/country-flag/content.md +232 -0
- package/docs/components/data-grid/content.md +3 -1
- package/docs/components/icon/content.md +1 -1
- package/docs/components/input/content.md +1 -0
- package/docs/components/line-chart/content.md +567 -0
- package/docs/components/pie-chart/content.md +493 -0
- package/docs/components/progress-bar/content.md +164 -3
- package/docs/components/progress-segmented/content.md +1 -1
- package/docs/components/select/content.md +1 -0
- package/docs/components/stepper/content.md +599 -0
- package/docs/components/system-alert/content.md +533 -0
- package/docs/foundations/expressions/icons/content.md +2 -1
- package/docs/foundations/styles/tokens/all-tokens/content.md +2 -1
- package/docs/getting-started/developers/content.md +1 -1
- package/docs/llms.txt +12 -0
- package/docs/patterns/content.md +1 -0
- package/docs/patterns/dashboard/content.md +423 -19
- package/docs/patterns/insights/content.md +659 -0
- package/docs/resources/content.md +1 -0
- package/docs/resources/release-notes/0.3.5/content.md +7 -0
- package/docs/resources/release-notes/0.3.6/content.md +29 -0
- package/docs/resources/release-notes/content.md +2 -0
- package/docs/resources/skills/content.md +66 -0
- package/lib/commonjs/components/BarChart/geometry.js +1130 -0
- package/lib/commonjs/components/BarChart/geometry.js.map +1 -0
- package/lib/commonjs/components/BarChart/index.js +935 -0
- package/lib/commonjs/components/BarChart/index.js.map +1 -0
- package/lib/commonjs/components/BarChart/styles.js +26 -0
- package/lib/commonjs/components/BarChart/styles.js.map +1 -0
- package/lib/commonjs/components/Button/index.js +2 -2
- package/lib/commonjs/components/Button/index.js.map +1 -1
- package/lib/commonjs/components/CountryFlag/index.js +56 -0
- package/lib/commonjs/components/CountryFlag/index.js.map +1 -0
- package/lib/commonjs/components/Input/index.js.map +1 -1
- package/lib/commonjs/components/LineChart/geometry.js +698 -0
- package/lib/commonjs/components/LineChart/geometry.js.map +1 -0
- package/lib/commonjs/components/LineChart/index.js +744 -0
- package/lib/commonjs/components/LineChart/index.js.map +1 -0
- package/lib/commonjs/components/LineChart/styles.js +25 -0
- package/lib/commonjs/components/LineChart/styles.js.map +1 -0
- package/lib/commonjs/components/PieChart/geometry.js +216 -0
- package/lib/commonjs/components/PieChart/geometry.js.map +1 -0
- package/lib/commonjs/components/PieChart/index.js +476 -0
- package/lib/commonjs/components/PieChart/index.js.map +1 -0
- package/lib/commonjs/components/PieChart/styles.js +88 -0
- package/lib/commonjs/components/PieChart/styles.js.map +1 -0
- package/lib/commonjs/components/ProgressBar/index.js +44 -12
- package/lib/commonjs/components/ProgressBar/index.js.map +1 -1
- package/lib/commonjs/components/ProgressBar/styles.js +31 -2
- package/lib/commonjs/components/ProgressBar/styles.js.map +1 -1
- package/lib/commonjs/components/ProgressCircle/ProgressCircleRing.js +9 -31
- package/lib/commonjs/components/ProgressCircle/ProgressCircleRing.js.map +1 -1
- package/lib/commonjs/components/ProgressCircle/ProgressRing.js +52 -0
- package/lib/commonjs/components/ProgressCircle/ProgressRing.js.map +1 -0
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js +8 -7
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js.map +1 -1
- package/lib/commonjs/components/Select/index.js.map +1 -1
- package/lib/commonjs/components/Stepper/index.js +369 -0
- package/lib/commonjs/components/Stepper/index.js.map +1 -0
- package/lib/commonjs/components/Stepper/styles.js +211 -0
- package/lib/commonjs/components/Stepper/styles.js.map +1 -0
- package/lib/commonjs/components/Stepper/useStepperLabelFit.js +22 -0
- package/lib/commonjs/components/Stepper/useStepperLabelFit.js.map +1 -0
- package/lib/commonjs/components/Stepper/useStepperLabelFit.web.js +52 -0
- package/lib/commonjs/components/Stepper/useStepperLabelFit.web.js.map +1 -0
- package/lib/commonjs/components/Stepper/useStepperRingColors.js +31 -0
- package/lib/commonjs/components/Stepper/useStepperRingColors.js.map +1 -0
- package/lib/commonjs/components/SystemAlert/animation.js +218 -0
- package/lib/commonjs/components/SystemAlert/animation.js.map +1 -0
- package/lib/commonjs/components/SystemAlert/index.js +525 -0
- package/lib/commonjs/components/SystemAlert/index.js.map +1 -0
- package/lib/commonjs/components/SystemAlert/styles.js +178 -0
- package/lib/commonjs/components/SystemAlert/styles.js.map +1 -0
- package/lib/commonjs/components/SystemAlert/useSystemAlertStatusBarStyle.js +53 -0
- package/lib/commonjs/components/SystemAlert/useSystemAlertStatusBarStyle.js.map +1 -0
- package/lib/commonjs/components/Tabs/index.js +4 -0
- package/lib/commonjs/components/Tabs/index.js.map +1 -1
- package/lib/commonjs/components/Tabs/styles.js +1 -1
- package/lib/commonjs/components/Tabs/styles.js.map +1 -1
- package/lib/commonjs/components/chart/ChartInspector.js +610 -0
- package/lib/commonjs/components/chart/ChartInspector.js.map +1 -0
- package/lib/commonjs/components/chart/format.js +93 -0
- package/lib/commonjs/components/chart/format.js.map +1 -0
- package/lib/commonjs/components/chart/hiddenSvgProps.js +23 -0
- package/lib/commonjs/components/chart/hiddenSvgProps.js.map +1 -0
- package/lib/commonjs/components/chart/index.js +2 -0
- package/lib/commonjs/components/chart/index.js.map +1 -0
- package/lib/commonjs/components/chart/motion.js +97 -0
- package/lib/commonjs/components/chart/motion.js.map +1 -0
- package/lib/commonjs/components/chart/palette.js +178 -0
- package/lib/commonjs/components/chart/palette.js.map +1 -0
- package/lib/commonjs/components/chart/size.js +34 -0
- package/lib/commonjs/components/chart/size.js.map +1 -0
- package/lib/commonjs/components/chart/ticks.js +59 -0
- package/lib/commonjs/components/chart/ticks.js.map +1 -0
- package/lib/commonjs/components/chart/types.js +6 -0
- package/lib/commonjs/components/chart/types.js.map +1 -0
- package/lib/commonjs/components/chart/useChartHostWidth.js +27 -0
- package/lib/commonjs/components/chart/useChartHostWidth.js.map +1 -0
- package/lib/commonjs/components/index.js +72 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/figma/Accordion.figma.js +2 -2
- package/lib/commonjs/figma/Accordion.figma.js.map +1 -1
- package/lib/commonjs/figma/Alert.figma.js +7 -7
- package/lib/commonjs/figma/Alert.figma.js.map +1 -1
- package/lib/commonjs/figma/Badge.Indicator.figma.js +6 -5
- package/lib/commonjs/figma/Badge.Indicator.figma.js.map +1 -1
- package/lib/commonjs/figma/Badge.figma.js +22 -22
- package/lib/commonjs/figma/Badge.figma.js.map +1 -1
- package/lib/commonjs/figma/Button.figma.js +5 -2
- package/lib/commonjs/figma/Button.figma.js.map +1 -1
- package/lib/commonjs/figma/CLAUDE.md +1 -0
- package/lib/commonjs/figma/Card.Header.figma.js +8 -4
- package/lib/commonjs/figma/Card.Header.figma.js.map +1 -1
- package/lib/commonjs/figma/Card.figma.js +2 -1
- package/lib/commonjs/figma/Card.figma.js.map +1 -1
- package/lib/commonjs/figma/Chip.figma.js +2 -2
- package/lib/commonjs/figma/Chip.figma.js.map +1 -1
- package/lib/commonjs/figma/CurrencyInput.figma.js +1 -1
- package/lib/commonjs/figma/CurrencyInput.figma.js.map +1 -1
- package/lib/commonjs/figma/Dialog.figma.js +5 -2
- package/lib/commonjs/figma/Dialog.figma.js.map +1 -1
- package/lib/commonjs/figma/Divider.figma.js +1 -1
- package/lib/commonjs/figma/Divider.figma.js.map +1 -1
- package/lib/commonjs/figma/Drawer.figma.js +3 -3
- package/lib/commonjs/figma/Drawer.figma.js.map +1 -1
- package/lib/commonjs/figma/IconButton.figma.js +10 -4
- package/lib/commonjs/figma/IconButton.figma.js.map +1 -1
- package/lib/commonjs/figma/Input.figma.js +2 -1
- package/lib/commonjs/figma/Input.figma.js.map +1 -1
- package/lib/commonjs/figma/Insight.figma.js +3 -3
- package/lib/commonjs/figma/Insight.figma.js.map +1 -1
- package/lib/commonjs/figma/ProgressBar.figma.js +11 -2
- package/lib/commonjs/figma/ProgressBar.figma.js.map +1 -1
- package/lib/commonjs/figma/ProgressSegmented.figma.js +9 -1
- package/lib/commonjs/figma/ProgressSegmented.figma.js.map +1 -1
- package/lib/commonjs/figma/Select.figma.js +2 -1
- package/lib/commonjs/figma/Select.figma.js.map +1 -1
- package/lib/commonjs/figma/SystemAlert.Button.figma.js +28 -0
- package/lib/commonjs/figma/SystemAlert.Button.figma.js.map +1 -0
- package/lib/commonjs/figma/SystemAlert.Close.figma.js +22 -0
- package/lib/commonjs/figma/SystemAlert.Close.figma.js.map +1 -0
- package/lib/commonjs/figma/SystemAlert.Description.figma.js +20 -0
- package/lib/commonjs/figma/SystemAlert.Description.figma.js.map +1 -0
- package/lib/commonjs/figma/SystemAlert.IconButton.figma.js +31 -0
- package/lib/commonjs/figma/SystemAlert.IconButton.figma.js.map +1 -0
- package/lib/commonjs/figma/SystemAlert.figma.js +47 -0
- package/lib/commonjs/figma/SystemAlert.figma.js.map +1 -0
- package/lib/commonjs/styles.css +0 -1
- package/lib/module/components/BarChart/geometry.js +1110 -0
- package/lib/module/components/BarChart/geometry.js.map +1 -0
- package/lib/module/components/BarChart/index.js +929 -0
- package/lib/module/components/BarChart/index.js.map +1 -0
- package/lib/module/components/BarChart/styles.js +22 -0
- package/lib/module/components/BarChart/styles.js.map +1 -0
- package/lib/module/components/Button/index.js +2 -2
- package/lib/module/components/Button/index.js.map +1 -1
- package/lib/module/components/CountryFlag/index.js +52 -0
- package/lib/module/components/CountryFlag/index.js.map +1 -0
- package/lib/module/components/Input/index.js.map +1 -1
- package/lib/module/components/LineChart/geometry.js +685 -0
- package/lib/module/components/LineChart/geometry.js.map +1 -0
- package/lib/module/components/LineChart/index.js +738 -0
- package/lib/module/components/LineChart/index.js.map +1 -0
- package/lib/module/components/LineChart/styles.js +21 -0
- package/lib/module/components/LineChart/styles.js.map +1 -0
- package/lib/module/components/PieChart/geometry.js +209 -0
- package/lib/module/components/PieChart/geometry.js.map +1 -0
- package/lib/module/components/PieChart/index.js +471 -0
- package/lib/module/components/PieChart/index.js.map +1 -0
- package/lib/module/components/PieChart/styles.js +84 -0
- package/lib/module/components/PieChart/styles.js.map +1 -0
- package/lib/module/components/ProgressBar/index.js +47 -15
- package/lib/module/components/ProgressBar/index.js.map +1 -1
- package/lib/module/components/ProgressBar/styles.js +31 -2
- package/lib/module/components/ProgressBar/styles.js.map +1 -1
- package/lib/module/components/ProgressCircle/ProgressCircleRing.js +10 -32
- package/lib/module/components/ProgressCircle/ProgressCircleRing.js.map +1 -1
- package/lib/module/components/ProgressCircle/ProgressRing.js +48 -0
- package/lib/module/components/ProgressCircle/ProgressRing.js.map +1 -0
- package/lib/module/components/ScrollShadow/useScrollShadow.js +8 -7
- package/lib/module/components/ScrollShadow/useScrollShadow.js.map +1 -1
- package/lib/module/components/Select/index.js.map +1 -1
- package/lib/module/components/Stepper/index.js +360 -0
- package/lib/module/components/Stepper/index.js.map +1 -0
- package/lib/module/components/Stepper/styles.js +206 -0
- package/lib/module/components/Stepper/styles.js.map +1 -0
- package/lib/module/components/Stepper/useStepperLabelFit.js +18 -0
- package/lib/module/components/Stepper/useStepperLabelFit.js.map +1 -0
- package/lib/module/components/Stepper/useStepperLabelFit.web.js +48 -0
- package/lib/module/components/Stepper/useStepperLabelFit.web.js.map +1 -0
- package/lib/module/components/Stepper/useStepperRingColors.js +28 -0
- package/lib/module/components/Stepper/useStepperRingColors.js.map +1 -0
- package/lib/module/components/SystemAlert/animation.js +213 -0
- package/lib/module/components/SystemAlert/animation.js.map +1 -0
- package/lib/module/components/SystemAlert/index.js +515 -0
- package/lib/module/components/SystemAlert/index.js.map +1 -0
- package/lib/module/components/SystemAlert/styles.js +174 -0
- package/lib/module/components/SystemAlert/styles.js.map +1 -0
- package/lib/module/components/SystemAlert/useSystemAlertStatusBarStyle.js +49 -0
- package/lib/module/components/SystemAlert/useSystemAlertStatusBarStyle.js.map +1 -0
- package/lib/module/components/Tabs/index.js +4 -0
- package/lib/module/components/Tabs/index.js.map +1 -1
- package/lib/module/components/Tabs/styles.js +1 -1
- package/lib/module/components/Tabs/styles.js.map +1 -1
- package/lib/module/components/chart/ChartInspector.js +600 -0
- package/lib/module/components/chart/ChartInspector.js.map +1 -0
- package/lib/module/components/chart/format.js +84 -0
- package/lib/module/components/chart/format.js.map +1 -0
- package/lib/module/components/chart/hiddenSvgProps.js +20 -0
- package/lib/module/components/chart/hiddenSvgProps.js.map +1 -0
- package/lib/module/components/chart/index.js +2 -0
- package/lib/module/components/chart/index.js.map +1 -0
- package/lib/module/components/chart/motion.js +89 -0
- package/lib/module/components/chart/motion.js.map +1 -0
- package/lib/module/components/chart/palette.js +168 -0
- package/lib/module/components/chart/palette.js.map +1 -0
- package/lib/module/components/chart/size.js +29 -0
- package/lib/module/components/chart/size.js.map +1 -0
- package/lib/module/components/chart/ticks.js +55 -0
- package/lib/module/components/chart/ticks.js.map +1 -0
- package/lib/module/components/chart/types.js +4 -0
- package/lib/module/components/chart/types.js.map +1 -0
- package/lib/module/components/chart/useChartHostWidth.js +23 -0
- package/lib/module/components/chart/useChartHostWidth.js.map +1 -0
- package/lib/module/components/index.js +6 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/figma/Accordion.figma.js +2 -2
- package/lib/module/figma/Accordion.figma.js.map +1 -1
- package/lib/module/figma/Alert.figma.js +7 -7
- package/lib/module/figma/Alert.figma.js.map +1 -1
- package/lib/module/figma/Badge.Indicator.figma.js +6 -5
- package/lib/module/figma/Badge.Indicator.figma.js.map +1 -1
- package/lib/module/figma/Badge.figma.js +22 -22
- package/lib/module/figma/Badge.figma.js.map +1 -1
- package/lib/module/figma/Button.figma.js +5 -2
- package/lib/module/figma/Button.figma.js.map +1 -1
- package/lib/module/figma/CLAUDE.md +1 -0
- package/lib/module/figma/Card.Header.figma.js +8 -4
- package/lib/module/figma/Card.Header.figma.js.map +1 -1
- package/lib/module/figma/Card.figma.js +2 -1
- package/lib/module/figma/Card.figma.js.map +1 -1
- package/lib/module/figma/Chip.figma.js +2 -2
- package/lib/module/figma/Chip.figma.js.map +1 -1
- package/lib/module/figma/CurrencyInput.figma.js +1 -1
- package/lib/module/figma/CurrencyInput.figma.js.map +1 -1
- package/lib/module/figma/Dialog.figma.js +5 -2
- package/lib/module/figma/Dialog.figma.js.map +1 -1
- package/lib/module/figma/Divider.figma.js +1 -1
- package/lib/module/figma/Divider.figma.js.map +1 -1
- package/lib/module/figma/Drawer.figma.js +3 -3
- package/lib/module/figma/Drawer.figma.js.map +1 -1
- package/lib/module/figma/IconButton.figma.js +10 -4
- package/lib/module/figma/IconButton.figma.js.map +1 -1
- package/lib/module/figma/Input.figma.js +2 -1
- package/lib/module/figma/Input.figma.js.map +1 -1
- package/lib/module/figma/Insight.figma.js +3 -3
- package/lib/module/figma/Insight.figma.js.map +1 -1
- package/lib/module/figma/ProgressBar.figma.js +12 -2
- package/lib/module/figma/ProgressBar.figma.js.map +1 -1
- package/lib/module/figma/ProgressSegmented.figma.js +9 -1
- package/lib/module/figma/ProgressSegmented.figma.js.map +1 -1
- package/lib/module/figma/Select.figma.js +2 -1
- package/lib/module/figma/Select.figma.js.map +1 -1
- package/lib/module/figma/SystemAlert.Button.figma.js +22 -0
- package/lib/module/figma/SystemAlert.Button.figma.js.map +1 -0
- package/lib/module/figma/SystemAlert.Close.figma.js +18 -0
- package/lib/module/figma/SystemAlert.Close.figma.js.map +1 -0
- package/lib/module/figma/SystemAlert.Description.figma.js +14 -0
- package/lib/module/figma/SystemAlert.Description.figma.js.map +1 -0
- package/lib/module/figma/SystemAlert.IconButton.figma.js +25 -0
- package/lib/module/figma/SystemAlert.IconButton.figma.js.map +1 -0
- package/lib/module/figma/SystemAlert.figma.js +41 -0
- package/lib/module/figma/SystemAlert.figma.js.map +1 -0
- package/lib/module/styles.css +0 -1
- package/lib/typescript/components/BarChart/geometry.d.ts +236 -0
- package/lib/typescript/components/BarChart/geometry.d.ts.map +1 -0
- package/lib/typescript/components/BarChart/index.d.ts +193 -0
- package/lib/typescript/components/BarChart/index.d.ts.map +1 -0
- package/lib/typescript/components/BarChart/styles.d.ts +32 -0
- package/lib/typescript/components/BarChart/styles.d.ts.map +1 -0
- package/lib/typescript/components/CountryFlag/index.d.ts +35 -0
- package/lib/typescript/components/CountryFlag/index.d.ts.map +1 -0
- package/lib/typescript/components/Input/index.d.ts +2 -0
- package/lib/typescript/components/Input/index.d.ts.map +1 -1
- package/lib/typescript/components/LineChart/geometry.d.ts +248 -0
- package/lib/typescript/components/LineChart/geometry.d.ts.map +1 -0
- package/lib/typescript/components/LineChart/index.d.ts +105 -0
- package/lib/typescript/components/LineChart/index.d.ts.map +1 -0
- package/lib/typescript/components/LineChart/styles.d.ts +12 -0
- package/lib/typescript/components/LineChart/styles.d.ts.map +1 -0
- package/lib/typescript/components/PieChart/geometry.d.ts +43 -0
- package/lib/typescript/components/PieChart/geometry.d.ts.map +1 -0
- package/lib/typescript/components/PieChart/index.d.ts +147 -0
- package/lib/typescript/components/PieChart/index.d.ts.map +1 -0
- package/lib/typescript/components/PieChart/styles.d.ts +27 -0
- package/lib/typescript/components/PieChart/styles.d.ts.map +1 -0
- package/lib/typescript/components/ProgressBar/index.d.ts +16 -1
- package/lib/typescript/components/ProgressBar/index.d.ts.map +1 -1
- package/lib/typescript/components/ProgressBar/styles.d.ts +7 -2
- package/lib/typescript/components/ProgressBar/styles.d.ts.map +1 -1
- package/lib/typescript/components/ProgressCircle/ProgressCircleRing.d.ts.map +1 -1
- package/lib/typescript/components/ProgressCircle/ProgressRing.d.ts +17 -0
- package/lib/typescript/components/ProgressCircle/ProgressRing.d.ts.map +1 -0
- package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts.map +1 -1
- package/lib/typescript/components/Select/index.d.ts +2 -0
- package/lib/typescript/components/Select/index.d.ts.map +1 -1
- package/lib/typescript/components/Stepper/index.d.ts +68 -0
- package/lib/typescript/components/Stepper/index.d.ts.map +1 -0
- package/lib/typescript/components/Stepper/styles.d.ts +46 -0
- package/lib/typescript/components/Stepper/styles.d.ts.map +1 -0
- package/lib/typescript/components/Stepper/useStepperLabelFit.d.ts +15 -0
- package/lib/typescript/components/Stepper/useStepperLabelFit.d.ts.map +1 -0
- package/lib/typescript/components/Stepper/useStepperLabelFit.web.d.ts +10 -0
- package/lib/typescript/components/Stepper/useStepperLabelFit.web.d.ts.map +1 -0
- package/lib/typescript/components/Stepper/useStepperRingColors.d.ts +6 -0
- package/lib/typescript/components/Stepper/useStepperRingColors.d.ts.map +1 -0
- package/lib/typescript/components/SystemAlert/animation.d.ts +56 -0
- package/lib/typescript/components/SystemAlert/animation.d.ts.map +1 -0
- package/lib/typescript/components/SystemAlert/index.d.ts +110 -0
- package/lib/typescript/components/SystemAlert/index.d.ts.map +1 -0
- package/lib/typescript/components/SystemAlert/styles.d.ts +38 -0
- package/lib/typescript/components/SystemAlert/styles.d.ts.map +1 -0
- package/lib/typescript/components/SystemAlert/useSystemAlertStatusBarStyle.d.ts +14 -0
- package/lib/typescript/components/SystemAlert/useSystemAlertStatusBarStyle.d.ts.map +1 -0
- package/lib/typescript/components/Tabs/index.d.ts.map +1 -1
- package/lib/typescript/components/chart/ChartInspector.d.ts +101 -0
- package/lib/typescript/components/chart/ChartInspector.d.ts.map +1 -0
- package/lib/typescript/components/chart/format.d.ts +16 -0
- package/lib/typescript/components/chart/format.d.ts.map +1 -0
- package/lib/typescript/components/chart/hiddenSvgProps.d.ts +17 -0
- package/lib/typescript/components/chart/hiddenSvgProps.d.ts.map +1 -0
- package/lib/typescript/components/chart/index.d.ts +3 -0
- package/lib/typescript/components/chart/index.d.ts.map +1 -0
- package/lib/typescript/components/chart/motion.d.ts +27 -0
- package/lib/typescript/components/chart/motion.d.ts.map +1 -0
- package/lib/typescript/components/chart/palette.d.ts +44 -0
- package/lib/typescript/components/chart/palette.d.ts.map +1 -0
- package/lib/typescript/components/chart/size.d.ts +25 -0
- package/lib/typescript/components/chart/size.d.ts.map +1 -0
- package/lib/typescript/components/chart/ticks.d.ts +3 -0
- package/lib/typescript/components/chart/ticks.d.ts.map +1 -0
- package/lib/typescript/components/chart/types.d.ts +163 -0
- package/lib/typescript/components/chart/types.d.ts.map +1 -0
- package/lib/typescript/components/chart/useChartHostWidth.d.ts +9 -0
- package/lib/typescript/components/chart/useChartHostWidth.d.ts.map +1 -0
- package/lib/typescript/components/index.d.ts +6 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/figma/Alert.figma.d.ts.map +1 -1
- package/lib/typescript/figma/Badge.Indicator.figma.d.ts.map +1 -1
- package/lib/typescript/figma/Badge.figma.d.ts.map +1 -1
- package/lib/typescript/figma/Button.figma.d.ts.map +1 -1
- package/lib/typescript/figma/Card.Header.figma.d.ts.map +1 -1
- package/lib/typescript/figma/Card.figma.d.ts.map +1 -1
- package/lib/typescript/figma/Dialog.figma.d.ts.map +1 -1
- package/lib/typescript/figma/IconButton.figma.d.ts.map +1 -1
- package/lib/typescript/figma/Input.figma.d.ts.map +1 -1
- package/lib/typescript/figma/ProgressBar.figma.d.ts.map +1 -1
- package/lib/typescript/figma/ProgressSegmented.figma.d.ts.map +1 -1
- package/lib/typescript/figma/Select.figma.d.ts.map +1 -1
- package/lib/typescript/figma/SystemAlert.Button.figma.d.ts +11 -0
- package/lib/typescript/figma/SystemAlert.Button.figma.d.ts.map +1 -0
- package/lib/typescript/figma/SystemAlert.Close.figma.d.ts +11 -0
- package/lib/typescript/figma/SystemAlert.Close.figma.d.ts.map +1 -0
- package/lib/typescript/figma/SystemAlert.Description.figma.d.ts +8 -0
- package/lib/typescript/figma/SystemAlert.Description.figma.d.ts.map +1 -0
- package/lib/typescript/figma/SystemAlert.IconButton.figma.d.ts +11 -0
- package/lib/typescript/figma/SystemAlert.IconButton.figma.d.ts.map +1 -0
- package/lib/typescript/figma/SystemAlert.figma.d.ts +8 -0
- package/lib/typescript/figma/SystemAlert.figma.d.ts.map +1 -0
- package/package.json +5 -5
- package/src/components/BarChart/geometry.ts +1469 -0
- package/src/components/BarChart/index.tsx +1417 -0
- package/src/components/BarChart/styles.ts +31 -0
- package/src/components/Button/index.tsx +1 -1
- package/src/components/CountryFlag/index.tsx +53 -0
- package/src/components/Input/index.tsx +2 -0
- package/src/components/LineChart/geometry.ts +957 -0
- package/src/components/LineChart/index.tsx +1106 -0
- package/src/components/LineChart/styles.ts +26 -0
- package/src/components/PieChart/geometry.ts +295 -0
- package/src/components/PieChart/index.tsx +715 -0
- package/src/components/PieChart/styles.ts +101 -0
- package/src/components/ProgressBar/index.tsx +66 -14
- package/src/components/ProgressBar/styles.ts +35 -4
- package/src/components/ProgressCircle/ProgressCircleRing.tsx +10 -39
- package/src/components/ProgressCircle/ProgressRing.tsx +60 -0
- package/src/components/ScrollShadow/useScrollShadow.ts +12 -11
- package/src/components/Select/index.tsx +2 -0
- package/src/components/Stepper/index.tsx +452 -0
- package/src/components/Stepper/styles.ts +234 -0
- package/src/components/Stepper/useStepperLabelFit.ts +21 -0
- package/src/components/Stepper/useStepperLabelFit.web.ts +53 -0
- package/src/components/Stepper/useStepperRingColors.ts +29 -0
- package/src/components/SystemAlert/animation.ts +252 -0
- package/src/components/SystemAlert/index.tsx +684 -0
- package/src/components/SystemAlert/styles.ts +212 -0
- package/src/components/SystemAlert/useSystemAlertStatusBarStyle.ts +57 -0
- package/src/components/Tabs/index.tsx +4 -0
- package/src/components/Tabs/styles.ts +1 -1
- package/src/components/chart/ChartInspector.tsx +726 -0
- package/src/components/chart/format.ts +95 -0
- package/src/components/chart/hiddenSvgProps.ts +15 -0
- package/src/components/chart/index.ts +24 -0
- package/src/components/chart/motion.ts +98 -0
- package/src/components/chart/palette.tsx +247 -0
- package/src/components/chart/size.ts +21 -0
- package/src/components/chart/ticks.ts +53 -0
- package/src/components/chart/types.ts +183 -0
- package/src/components/chart/useChartHostWidth.ts +26 -0
- package/src/components/index.ts +6 -0
- package/src/styles.css +0 -1
|
@@ -0,0 +1,1417 @@
|
|
|
1
|
+
/* SVG `x`/`y` are paint coordinates. RN View `x`/`y` layout props are deprecated and share the name. */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-deprecated */
|
|
3
|
+
import {
|
|
4
|
+
forwardRef,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
type ForwardedRef,
|
|
10
|
+
type ReactElement,
|
|
11
|
+
type ReactNode,
|
|
12
|
+
} from 'react';
|
|
13
|
+
import {
|
|
14
|
+
ScrollView,
|
|
15
|
+
Text,
|
|
16
|
+
View,
|
|
17
|
+
type NativeScrollEvent,
|
|
18
|
+
type NativeSyntheticEvent,
|
|
19
|
+
type ViewProps,
|
|
20
|
+
} from 'react-native';
|
|
21
|
+
import Animated, { useAnimatedProps } from 'react-native-reanimated';
|
|
22
|
+
import { G, Line, Path, Svg, Text as SvgText } from 'react-native-svg';
|
|
23
|
+
import { dataAttributes, mergeDataAttributes } from '@cdx-ui/primitives';
|
|
24
|
+
import { cn, useControllableState } from '@cdx-ui/utils';
|
|
25
|
+
import type { AnimationRootDisableAll } from '../../animation';
|
|
26
|
+
import {
|
|
27
|
+
ChartInspector,
|
|
28
|
+
ChartTooltipLayer,
|
|
29
|
+
expandHitBox,
|
|
30
|
+
type ChartInspectorTarget,
|
|
31
|
+
} from '../chart/ChartInspector';
|
|
32
|
+
import {
|
|
33
|
+
defaultChartValueFormatter,
|
|
34
|
+
formatChartPercent,
|
|
35
|
+
resolveChartLocaleText,
|
|
36
|
+
resolveValueFormatter,
|
|
37
|
+
} from '../chart/format';
|
|
38
|
+
import { HIDDEN_SVG_PROPS } from '../chart/hiddenSvgProps';
|
|
39
|
+
import type {
|
|
40
|
+
ChartAxis,
|
|
41
|
+
ChartGrid,
|
|
42
|
+
ChartLocaleText,
|
|
43
|
+
ChartReferenceLine,
|
|
44
|
+
ChartSelectedItem,
|
|
45
|
+
ChartSeriesBase,
|
|
46
|
+
ChartSizeProps,
|
|
47
|
+
ChartSummaryCategory,
|
|
48
|
+
ChartTooltipContent,
|
|
49
|
+
ChartTooltipProps,
|
|
50
|
+
ChartValueFormatter,
|
|
51
|
+
ChartValueLocation,
|
|
52
|
+
} from '../chart/types';
|
|
53
|
+
import { useChartHostWidth } from '../chart/useChartHostWidth';
|
|
54
|
+
import {
|
|
55
|
+
CHART_MOTION_DURATION,
|
|
56
|
+
useChartMorph,
|
|
57
|
+
useChartMotionEnabled,
|
|
58
|
+
lerpVector,
|
|
59
|
+
} from '../chart/motion';
|
|
60
|
+
import {
|
|
61
|
+
ChartLinearFills,
|
|
62
|
+
chartFillUrl,
|
|
63
|
+
chartSeriesColor,
|
|
64
|
+
resolveChartColor,
|
|
65
|
+
useChartChrome,
|
|
66
|
+
useChartColorPaints,
|
|
67
|
+
useChartGradientId,
|
|
68
|
+
useChartPalette,
|
|
69
|
+
useChartSimpleColor,
|
|
70
|
+
type ChartChrome,
|
|
71
|
+
type ChartPalette,
|
|
72
|
+
} from '../chart/palette';
|
|
73
|
+
import {
|
|
74
|
+
BAR_GAP_RATIO,
|
|
75
|
+
CATEGORY_GAP_RATIO,
|
|
76
|
+
barKeyboardNeighbor,
|
|
77
|
+
barPath,
|
|
78
|
+
barValueAnchor,
|
|
79
|
+
DEFAULT_STACK_GAP,
|
|
80
|
+
formatWaterfallBarValue,
|
|
81
|
+
isExpandAxis,
|
|
82
|
+
layoutBarChart,
|
|
83
|
+
overlayDelta,
|
|
84
|
+
readBarValue,
|
|
85
|
+
resolveBarChartContentSize,
|
|
86
|
+
resolveBarChartGutters,
|
|
87
|
+
resolveBarGroups,
|
|
88
|
+
stackedTotalAnchors,
|
|
89
|
+
waterfallValueAnchor,
|
|
90
|
+
type BarChartKind,
|
|
91
|
+
type BarChartLayout,
|
|
92
|
+
type BarChartOrientation,
|
|
93
|
+
type BarGeometrySeries,
|
|
94
|
+
type BarRect,
|
|
95
|
+
type BarStackOffset,
|
|
96
|
+
} from './geometry';
|
|
97
|
+
import {
|
|
98
|
+
BAR_RADIUS,
|
|
99
|
+
barChartLegendItemVariants,
|
|
100
|
+
barChartLegendLabelVariants,
|
|
101
|
+
barChartLegendVariants,
|
|
102
|
+
barChartRootVariants,
|
|
103
|
+
resolveBarChartBox,
|
|
104
|
+
type BarChartSize,
|
|
105
|
+
} from './styles';
|
|
106
|
+
|
|
107
|
+
const INACTIVE_OPACITY = 0.4;
|
|
108
|
+
const DASH = '6 4';
|
|
109
|
+
const SVG_OVERFLOW_VISIBLE = { overflow: 'visible' } as const;
|
|
110
|
+
const LABEL_FADE_DURATION = 220;
|
|
111
|
+
|
|
112
|
+
const AnimatedPath = Animated.createAnimatedComponent(Path);
|
|
113
|
+
|
|
114
|
+
export type * from '../chart';
|
|
115
|
+
export type { BarChartSize, BarStackOffset };
|
|
116
|
+
|
|
117
|
+
/** Keys of `TRow` whose value is a boolean. */
|
|
118
|
+
export type BarChartBooleanKey<TRow> = {
|
|
119
|
+
[K in keyof TRow]-?: TRow[K] extends boolean | undefined ? K : never;
|
|
120
|
+
}[keyof TRow] &
|
|
121
|
+
string;
|
|
122
|
+
|
|
123
|
+
/** Running-total series. Each row is a change, except rows flagged as totals. */
|
|
124
|
+
export interface BarChartWaterfall<TRow> {
|
|
125
|
+
/**
|
|
126
|
+
* Row key whose `true` value marks a total bar, drawn from zero to the running total. A total
|
|
127
|
+
* row with a value on the first row sets the starting total; otherwise its value is ignored.
|
|
128
|
+
*/
|
|
129
|
+
totalKey: BarChartBooleanKey<TRow>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Draws this series as a thinner comparison bar beside another series' bar. */
|
|
133
|
+
export interface BarChartOverlay {
|
|
134
|
+
/** `id` of the series this one is compared against. */
|
|
135
|
+
seriesId: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface BarChartSeries<TRow> extends ChartSeriesBase<TRow> {
|
|
139
|
+
/** Series type. Reserved for mixed charts; bar is the only type today. */
|
|
140
|
+
type?: 'bar';
|
|
141
|
+
/**
|
|
142
|
+
* Stack group id. Series with the same `stack` draw as one bar per category; different ids
|
|
143
|
+
* draw side by side. Omit to draw the series on its own.
|
|
144
|
+
*/
|
|
145
|
+
stack?: string;
|
|
146
|
+
/**
|
|
147
|
+
* How the stack group piles its values. Set it on any one series in the group.
|
|
148
|
+
* `expand` scales each category to 100%.
|
|
149
|
+
* @default 'diverging'
|
|
150
|
+
*/
|
|
151
|
+
stackOffset?: BarStackOffset;
|
|
152
|
+
/**
|
|
153
|
+
* Draws the series as a waterfall (running total). A waterfall series draws alone: other
|
|
154
|
+
* series are not plotted.
|
|
155
|
+
*/
|
|
156
|
+
waterfall?: BarChartWaterfall<TRow>;
|
|
157
|
+
/**
|
|
158
|
+
* Draws the series as a thin comparison bar beside the series it names (this month versus last
|
|
159
|
+
* month). The pair draws alone, and the named series is announced with the change.
|
|
160
|
+
*/
|
|
161
|
+
overlay?: BarChartOverlay;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface BarChartProps<TRow extends object = object> extends ViewProps, ChartSizeProps {
|
|
165
|
+
/** One object per category. Series and axes read it through `dataKey`. */
|
|
166
|
+
dataset: readonly TRow[];
|
|
167
|
+
/** Bars to draw. Each reads one numeric column through `dataKey`. */
|
|
168
|
+
series: readonly BarChartSeries<TRow>[];
|
|
169
|
+
/**
|
|
170
|
+
* Horizontal axis. In `vertical` layout it is the category (band) axis: `dataKey` names the
|
|
171
|
+
* category column, `position` (`'bottom'`, `'top'`, `'none'`) places its labels, and
|
|
172
|
+
* `tickLabelInterval` picks which labels draw. In `horizontal` layout it is the value axis.
|
|
173
|
+
* Only the first axis is used.
|
|
174
|
+
*/
|
|
175
|
+
xAxis?: readonly ChartAxis<TRow>[];
|
|
176
|
+
/**
|
|
177
|
+
* Vertical axis. In `vertical` layout it is the value axis (`min`, `max`, `tickNumber`,
|
|
178
|
+
* `valueFormatter`; `position: 'none'` hides its ticks). In `horizontal` layout it is the
|
|
179
|
+
* category axis (`position` `'left'`, `'right'`, or `'none'`). Only the first axis is used.
|
|
180
|
+
*/
|
|
181
|
+
yAxis?: readonly ChartAxis<TRow>[];
|
|
182
|
+
/**
|
|
183
|
+
* `vertical` grows bars up from the baseline. `horizontal` grows them to the right.
|
|
184
|
+
* @default 'vertical'
|
|
185
|
+
*/
|
|
186
|
+
layout?: BarChartOrientation;
|
|
187
|
+
/**
|
|
188
|
+
* Hides the series legend under the chart. Waterfall charts never show one.
|
|
189
|
+
* @default false
|
|
190
|
+
*/
|
|
191
|
+
hideLegend?: boolean;
|
|
192
|
+
/**
|
|
193
|
+
* Grid lines along the value axis: `horizontal` in `vertical` layout, `vertical` in
|
|
194
|
+
* `horizontal` layout. The other direction is not drawn. Omit to draw the value-axis lines;
|
|
195
|
+
* pass an object without that key set to `true` to hide them and keep only the baseline.
|
|
196
|
+
* @default { horizontal: true } (vertical layout), { vertical: true } (horizontal layout)
|
|
197
|
+
*/
|
|
198
|
+
grid?: ChartGrid;
|
|
199
|
+
/**
|
|
200
|
+
* Default series colors. `auto` uses the FI brand family (`simple-primary`, `-secondary`,
|
|
201
|
+
* `-tertiary`) for up to three series and switches the whole chart to `categorical-1`…`6`
|
|
202
|
+
* past that. `categorical` always uses the categorical slots. A series `color` wins.
|
|
203
|
+
* @default 'auto'
|
|
204
|
+
*/
|
|
205
|
+
palette?: ChartPalette;
|
|
206
|
+
/**
|
|
207
|
+
* Dashed guides at a value: horizontal on a vertical chart, vertical on a horizontal chart.
|
|
208
|
+
*/
|
|
209
|
+
referenceLines?: readonly ChartReferenceLine[];
|
|
210
|
+
/**
|
|
211
|
+
* Keeps this `dataset` row full color and mutes the rest. Use with `barLabel` to label only
|
|
212
|
+
* that bar, like "this week" in a spending insight.
|
|
213
|
+
*/
|
|
214
|
+
emphasizedIndex?: number;
|
|
215
|
+
/**
|
|
216
|
+
* Draws formatted values at the end of each bar (each stack's total when stacked).
|
|
217
|
+
* @default false
|
|
218
|
+
*/
|
|
219
|
+
barLabel?: boolean;
|
|
220
|
+
/**
|
|
221
|
+
* Corner radius in pixels. `0` draws square bars.
|
|
222
|
+
* @default 6
|
|
223
|
+
*/
|
|
224
|
+
borderRadius?: number;
|
|
225
|
+
/**
|
|
226
|
+
* Pixel gap between stacked segments. With a gap every segment is rounded; `0` joins them and
|
|
227
|
+
* rounds only the outer cap.
|
|
228
|
+
* @default 3
|
|
229
|
+
*/
|
|
230
|
+
stackGap?: number;
|
|
231
|
+
/**
|
|
232
|
+
* Share of each category slot left empty around its bars, from `0` to `0.95`. Higher is
|
|
233
|
+
* thinner bars with more space between categories.
|
|
234
|
+
* @default 0.36
|
|
235
|
+
*/
|
|
236
|
+
categoryGapRatio?: number;
|
|
237
|
+
/**
|
|
238
|
+
* Gap between side-by-side bars in one category, as a share of one bar's thickness.
|
|
239
|
+
* @default 0.5
|
|
240
|
+
*/
|
|
241
|
+
barGapRatio?: number;
|
|
242
|
+
/**
|
|
243
|
+
* When the categories need more room than the visible box, scroll instead of
|
|
244
|
+
* squeezing bars. Vertical charts scroll sideways; horizontal charts scroll vertically.
|
|
245
|
+
* The value axis stays pinned. Turn off to pack every category into `width` / `height`.
|
|
246
|
+
* @default true
|
|
247
|
+
*/
|
|
248
|
+
scrollable?: boolean;
|
|
249
|
+
/**
|
|
250
|
+
* How many categories fit in the viewport when `scrollable` is on. Omit to use a
|
|
251
|
+
* readable minimum band (~48px).
|
|
252
|
+
*/
|
|
253
|
+
visiblePoints?: number;
|
|
254
|
+
/**
|
|
255
|
+
* Controlled selected bar: `seriesId` and the `dataset` row. `null` clears the selection while
|
|
256
|
+
* staying controlled.
|
|
257
|
+
*/
|
|
258
|
+
selectedItem?: ChartSelectedItem;
|
|
259
|
+
/**
|
|
260
|
+
* Initial selected bar when `selectedItem` is omitted.
|
|
261
|
+
*/
|
|
262
|
+
defaultSelectedItem?: ChartSelectedItem;
|
|
263
|
+
/**
|
|
264
|
+
* Called when a bar is pressed, with that bar, or with `null` when the selected bar is pressed
|
|
265
|
+
* again. Providing this, `selectedItem`, or `defaultSelectedItem` makes the chart selectable.
|
|
266
|
+
*/
|
|
267
|
+
onSelectedItemChange?: (item: ChartSelectedItem) => void;
|
|
268
|
+
/**
|
|
269
|
+
* Tooltip on hover, keyboard focus, or tap. `axis` lists every series in the category, `item`
|
|
270
|
+
* only the bar under the pointer, `none` hides it. On web the plot is one tab stop. Tab /
|
|
271
|
+
* Shift+Tab move between categories and leave the chart after the last (or before the first)
|
|
272
|
+
* group. Arrow keys move along the category axis (and between series). Home and End jump to
|
|
273
|
+
* the first or last category. Enter or Space selects. Escape hides the tooltip. Each bar is
|
|
274
|
+
* announced to screen readers.
|
|
275
|
+
* @default { trigger: 'axis' }
|
|
276
|
+
*/
|
|
277
|
+
tooltip?: ChartTooltipProps;
|
|
278
|
+
/** BCP 47 locale for default number and percent formatting. */
|
|
279
|
+
locale?: string;
|
|
280
|
+
/**
|
|
281
|
+
* Overrides for generated strings: summary, trend, waterfall step names, legend fallback, and
|
|
282
|
+
* no-value text. Keys you omit keep the English defaults.
|
|
283
|
+
*/
|
|
284
|
+
localeText?: Partial<ChartLocaleText>;
|
|
285
|
+
/**
|
|
286
|
+
* Bars grow from the baseline on mount and tween to new values when `dataset` changes. Pass
|
|
287
|
+
* `'disable-all'` to draw the final state. Reduce Motion and `GlobalAnimationSettingsProvider`
|
|
288
|
+
* `disable-all` also turn it off.
|
|
289
|
+
*/
|
|
290
|
+
animation?: AnimationRootDisableAll;
|
|
291
|
+
/**
|
|
292
|
+
* Accessible summary. Defaults to `localeText.barChartSummary` over every category.
|
|
293
|
+
*/
|
|
294
|
+
accessibilityLabel?: string;
|
|
295
|
+
/** Additional Uniwind/Tailwind classes, merged after the root's own styles. */
|
|
296
|
+
className?: string;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
interface BarSelection {
|
|
300
|
+
readonly seriesId: string;
|
|
301
|
+
readonly dataIndex: number;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
interface BarPlan<TRow> {
|
|
305
|
+
readonly kind: BarChartKind;
|
|
306
|
+
readonly series: readonly BarChartSeries<TRow>[];
|
|
307
|
+
readonly geometry: readonly BarGeometrySeries[];
|
|
308
|
+
readonly totalKey: string | undefined;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function resolveBarPlan<TRow>(series: readonly BarChartSeries<TRow>[]): BarPlan<TRow> {
|
|
312
|
+
const toGeometry = (item: BarChartSeries<TRow>): BarGeometrySeries => ({
|
|
313
|
+
id: item.id,
|
|
314
|
+
dataKey: item.dataKey,
|
|
315
|
+
label: item.label ?? '',
|
|
316
|
+
stack: item.stack,
|
|
317
|
+
stackOffset: item.stackOffset,
|
|
318
|
+
});
|
|
319
|
+
const waterfall = series.find((item) => item.waterfall !== undefined);
|
|
320
|
+
if (waterfall !== undefined) {
|
|
321
|
+
return {
|
|
322
|
+
kind: 'waterfall',
|
|
323
|
+
series: [waterfall],
|
|
324
|
+
geometry: [toGeometry(waterfall)],
|
|
325
|
+
totalKey: waterfall.waterfall?.totalKey,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
for (const comparison of series) {
|
|
329
|
+
const primary =
|
|
330
|
+
comparison.overlay === undefined
|
|
331
|
+
? undefined
|
|
332
|
+
: series.find((item) => item.id === comparison.overlay?.seriesId && item !== comparison);
|
|
333
|
+
if (primary !== undefined) {
|
|
334
|
+
const pair = [primary, comparison];
|
|
335
|
+
return {
|
|
336
|
+
kind: 'overlay',
|
|
337
|
+
series: pair,
|
|
338
|
+
geometry: pair.map((item) => ({ ...toGeometry(item), stack: undefined })),
|
|
339
|
+
totalKey: undefined,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return { kind: 'bars', series, geometry: series.map(toGeometry), totalKey: undefined };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function sameItem(a: ChartSelectedItem | undefined, b: BarSelection): boolean {
|
|
347
|
+
return a?.seriesId === b.seriesId && a.dataIndex === b.dataIndex;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function barSelection(bar: BarRect): BarSelection {
|
|
351
|
+
return { seriesId: bar.seriesId, dataIndex: bar.index };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function barAccessibilityName(
|
|
355
|
+
label: string,
|
|
356
|
+
seriesLabel: string,
|
|
357
|
+
formatted: string,
|
|
358
|
+
change: string | null = null,
|
|
359
|
+
): string {
|
|
360
|
+
const base =
|
|
361
|
+
seriesLabel.length > 0 ? `${label}, ${seriesLabel}, ${formatted}` : `${label}, ${formatted}`;
|
|
362
|
+
return change === null ? base : `${base}, ${change}`;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
type BarFormatter = (bar: BarRect, location: ChartValueLocation) => string;
|
|
366
|
+
|
|
367
|
+
function BarChartInner<TRow extends object>(
|
|
368
|
+
{
|
|
369
|
+
dataset,
|
|
370
|
+
series: seriesProp,
|
|
371
|
+
xAxis,
|
|
372
|
+
yAxis,
|
|
373
|
+
size = 'lg',
|
|
374
|
+
width: widthProp,
|
|
375
|
+
height: heightProp,
|
|
376
|
+
layout: orientation = 'vertical',
|
|
377
|
+
hideLegend = false,
|
|
378
|
+
grid,
|
|
379
|
+
palette: paletteProp = 'auto',
|
|
380
|
+
barLabel = false,
|
|
381
|
+
borderRadius = BAR_RADIUS,
|
|
382
|
+
stackGap = DEFAULT_STACK_GAP,
|
|
383
|
+
categoryGapRatio = CATEGORY_GAP_RATIO,
|
|
384
|
+
barGapRatio = BAR_GAP_RATIO,
|
|
385
|
+
referenceLines,
|
|
386
|
+
emphasizedIndex,
|
|
387
|
+
scrollable = true,
|
|
388
|
+
visiblePoints,
|
|
389
|
+
selectedItem,
|
|
390
|
+
defaultSelectedItem,
|
|
391
|
+
onSelectedItemChange,
|
|
392
|
+
tooltip: tooltipProp,
|
|
393
|
+
locale,
|
|
394
|
+
localeText: localeTextProp,
|
|
395
|
+
animation,
|
|
396
|
+
accessibilityLabel,
|
|
397
|
+
className,
|
|
398
|
+
style,
|
|
399
|
+
onLayout,
|
|
400
|
+
...props
|
|
401
|
+
}: BarChartProps<TRow>,
|
|
402
|
+
ref: ForwardedRef<View>,
|
|
403
|
+
) {
|
|
404
|
+
const animated = useChartMotionEnabled(animation);
|
|
405
|
+
const simpleColor = useChartSimpleColor();
|
|
406
|
+
const gradientId = useChartGradientId('bar-fill');
|
|
407
|
+
const chrome = useChartChrome();
|
|
408
|
+
const palette = useChartPalette();
|
|
409
|
+
const paints = useChartColorPaints();
|
|
410
|
+
const localeText = useMemo(() => resolveChartLocaleText(localeTextProp), [localeTextProp]);
|
|
411
|
+
const plan = useMemo(() => resolveBarPlan(seriesProp), [seriesProp]);
|
|
412
|
+
const { kind, geometry } = plan;
|
|
413
|
+
const overlay = kind === 'overlay';
|
|
414
|
+
const waterfall = kind === 'waterfall';
|
|
415
|
+
const expandAxis = useMemo(
|
|
416
|
+
() => isExpandAxis(kind, resolveBarGroups(geometry)),
|
|
417
|
+
[kind, geometry],
|
|
418
|
+
);
|
|
419
|
+
const categoryAxis = (orientation === 'vertical' ? xAxis : yAxis)?.[0];
|
|
420
|
+
const valueAxis = (orientation === 'vertical' ? yAxis : xAxis)?.[0];
|
|
421
|
+
const categoryKey = categoryAxis?.dataKey;
|
|
422
|
+
const categoryLabelPosition = categoryAxis?.position;
|
|
423
|
+
const showTicks = valueAxis?.position !== 'none';
|
|
424
|
+
const showGrid =
|
|
425
|
+
grid === undefined
|
|
426
|
+
? true
|
|
427
|
+
: (orientation === 'vertical' ? grid.horizontal : grid.vertical) === true;
|
|
428
|
+
const tickLabelInterval = categoryAxis?.tickLabelInterval;
|
|
429
|
+
const categories = useMemo(
|
|
430
|
+
() =>
|
|
431
|
+
dataset.map((row, index) => {
|
|
432
|
+
if (categoryKey === undefined) {
|
|
433
|
+
return String(index);
|
|
434
|
+
}
|
|
435
|
+
const raw: unknown = row[categoryKey];
|
|
436
|
+
return raw === null || raw === undefined ? '' : String(raw as string | number | boolean);
|
|
437
|
+
}),
|
|
438
|
+
[dataset, categoryKey],
|
|
439
|
+
);
|
|
440
|
+
const hasNegative = useMemo(
|
|
441
|
+
() =>
|
|
442
|
+
dataset.some((row) => geometry.some((item) => (readBarValue(row, item.dataKey) ?? 0) < 0)),
|
|
443
|
+
[dataset, geometry],
|
|
444
|
+
);
|
|
445
|
+
const sourceById = useMemo(
|
|
446
|
+
() => new Map(plan.series.map((item) => [item.id, item] as const)),
|
|
447
|
+
[plan.series],
|
|
448
|
+
);
|
|
449
|
+
const valueAxisMin = valueAxis?.min;
|
|
450
|
+
const valueAxisMax = valueAxis?.max;
|
|
451
|
+
const valueAxisTicks = valueAxis?.tickNumber;
|
|
452
|
+
const valueAxisFormatter = valueAxis?.valueFormatter;
|
|
453
|
+
const valueAxisConfig = useMemo(
|
|
454
|
+
() => ({ min: valueAxisMin, max: valueAxisMax, tickNumber: valueAxisTicks }),
|
|
455
|
+
[valueAxisMin, valueAxisMax, valueAxisTicks],
|
|
456
|
+
);
|
|
457
|
+
const percentFormatter: ChartValueFormatter = useMemo(
|
|
458
|
+
() => (value) => (value === null ? '' : formatChartPercent(value, locale)),
|
|
459
|
+
[locale],
|
|
460
|
+
);
|
|
461
|
+
const formatTick = useMemo(() => {
|
|
462
|
+
const formatter =
|
|
463
|
+
valueAxisFormatter ?? (expandAxis ? percentFormatter : defaultChartValueFormatter(locale));
|
|
464
|
+
return (value: number) => formatter(value, { location: 'tick' });
|
|
465
|
+
}, [valueAxisFormatter, expandAxis, percentFormatter, locale]);
|
|
466
|
+
const formatBar: BarFormatter = (bar, location) => {
|
|
467
|
+
const source = sourceById.get(bar.seriesId);
|
|
468
|
+
const context = { location, seriesId: bar.seriesId, dataIndex: bar.index };
|
|
469
|
+
if (bar.expand === true && source?.valueFormatter === undefined) {
|
|
470
|
+
return (valueAxisFormatter ?? percentFormatter)(bar.plotValue / 100, context);
|
|
471
|
+
}
|
|
472
|
+
const formatter = resolveValueFormatter(source, valueAxis, locale);
|
|
473
|
+
const format = (value: number) => formatter(value, context);
|
|
474
|
+
return bar.waterfallRole === undefined
|
|
475
|
+
? format(bar.value)
|
|
476
|
+
: formatWaterfallBarValue(bar, format);
|
|
477
|
+
};
|
|
478
|
+
const seriesLabelOf = (bar: BarRect): string => {
|
|
479
|
+
if (bar.waterfallRole === 'increase') {
|
|
480
|
+
return localeText.waterfallIncrease;
|
|
481
|
+
}
|
|
482
|
+
if (bar.waterfallRole === 'decrease') {
|
|
483
|
+
return localeText.waterfallDecrease;
|
|
484
|
+
}
|
|
485
|
+
return bar.seriesLabel;
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
const box = resolveBarChartBox(size, widthProp, heightProp);
|
|
489
|
+
const [hostWidth, handleHostLayout] = useChartHostWidth(onLayout);
|
|
490
|
+
const viewportWidth = hostWidth > 0 ? Math.min(hostWidth, box.width) : box.width;
|
|
491
|
+
const viewportHeight = box.height;
|
|
492
|
+
const gutters = useMemo(
|
|
493
|
+
() =>
|
|
494
|
+
resolveBarChartGutters({
|
|
495
|
+
categories,
|
|
496
|
+
hasNegative,
|
|
497
|
+
orientation,
|
|
498
|
+
barLabel,
|
|
499
|
+
categoryLabelPosition,
|
|
500
|
+
showTicks,
|
|
501
|
+
overlay,
|
|
502
|
+
}),
|
|
503
|
+
[categories, hasNegative, orientation, barLabel, categoryLabelPosition, showTicks, overlay],
|
|
504
|
+
);
|
|
505
|
+
const content = useMemo(
|
|
506
|
+
() =>
|
|
507
|
+
resolveBarChartContentSize({
|
|
508
|
+
viewportWidth,
|
|
509
|
+
viewportHeight,
|
|
510
|
+
categoryCount: dataset.length,
|
|
511
|
+
orientation,
|
|
512
|
+
gutters,
|
|
513
|
+
scrollable,
|
|
514
|
+
visiblePoints,
|
|
515
|
+
}),
|
|
516
|
+
[
|
|
517
|
+
viewportWidth,
|
|
518
|
+
viewportHeight,
|
|
519
|
+
dataset.length,
|
|
520
|
+
orientation,
|
|
521
|
+
gutters,
|
|
522
|
+
scrollable,
|
|
523
|
+
visiblePoints,
|
|
524
|
+
],
|
|
525
|
+
);
|
|
526
|
+
const showCategoryLabel = useMemo(() => {
|
|
527
|
+
if (typeof tickLabelInterval !== 'function') {
|
|
528
|
+
return undefined;
|
|
529
|
+
}
|
|
530
|
+
return (index: number) => {
|
|
531
|
+
const row = dataset.at(index);
|
|
532
|
+
const value: unknown =
|
|
533
|
+
row === undefined || categoryKey === undefined ? index : row[categoryKey];
|
|
534
|
+
return tickLabelInterval(value, index);
|
|
535
|
+
};
|
|
536
|
+
}, [tickLabelInterval, dataset, categoryKey]);
|
|
537
|
+
const layout = useMemo(
|
|
538
|
+
() =>
|
|
539
|
+
layoutBarChart({
|
|
540
|
+
dataset,
|
|
541
|
+
categories,
|
|
542
|
+
series: geometry,
|
|
543
|
+
kind,
|
|
544
|
+
waterfallTotalKey: plan.totalKey,
|
|
545
|
+
width: content.width,
|
|
546
|
+
height: content.height,
|
|
547
|
+
orientation,
|
|
548
|
+
barLabel,
|
|
549
|
+
categoryLabelPosition,
|
|
550
|
+
showTicks,
|
|
551
|
+
referenceLines,
|
|
552
|
+
valueAxis: valueAxisConfig,
|
|
553
|
+
formatTick,
|
|
554
|
+
stackGap,
|
|
555
|
+
categoryGapRatio,
|
|
556
|
+
barGapRatio,
|
|
557
|
+
showCategoryLabel,
|
|
558
|
+
}),
|
|
559
|
+
[
|
|
560
|
+
dataset,
|
|
561
|
+
categories,
|
|
562
|
+
geometry,
|
|
563
|
+
kind,
|
|
564
|
+
plan.totalKey,
|
|
565
|
+
content.width,
|
|
566
|
+
content.height,
|
|
567
|
+
orientation,
|
|
568
|
+
barLabel,
|
|
569
|
+
categoryLabelPosition,
|
|
570
|
+
showTicks,
|
|
571
|
+
referenceLines,
|
|
572
|
+
valueAxisConfig,
|
|
573
|
+
formatTick,
|
|
574
|
+
stackGap,
|
|
575
|
+
categoryGapRatio,
|
|
576
|
+
barGapRatio,
|
|
577
|
+
showCategoryLabel,
|
|
578
|
+
],
|
|
579
|
+
);
|
|
580
|
+
const overflowsX = content.width > viewportWidth + 0.5;
|
|
581
|
+
const overflowsY = content.height > viewportHeight + 0.5;
|
|
582
|
+
const fills = waterfall
|
|
583
|
+
? dataset.map((_, index) => palette[index % palette.length] ?? simpleColor)
|
|
584
|
+
: plan.series.map((item, seriesIndex) => {
|
|
585
|
+
const custom = resolveChartColor(item.color, paints);
|
|
586
|
+
if (custom !== undefined) {
|
|
587
|
+
return custom;
|
|
588
|
+
}
|
|
589
|
+
if (overlay) {
|
|
590
|
+
return seriesIndex === 0 ? simpleColor : chrome.grid;
|
|
591
|
+
}
|
|
592
|
+
return paints[chartSeriesColor(seriesIndex, plan.series.length, paletteProp, 'bar')];
|
|
593
|
+
});
|
|
594
|
+
const summaryCategories: ChartSummaryCategory[] = dataset.map((row, dataIndex) => ({
|
|
595
|
+
label: categories[dataIndex] ?? '',
|
|
596
|
+
values: plan.series.flatMap((item) => {
|
|
597
|
+
const value = readBarValue(row, item.dataKey);
|
|
598
|
+
if (value === null) {
|
|
599
|
+
return [];
|
|
600
|
+
}
|
|
601
|
+
// An expand axis formats 0–1 shares; the summary reads raw values.
|
|
602
|
+
const formatter = resolveValueFormatter(item, expandAxis ? null : valueAxis, locale);
|
|
603
|
+
return [
|
|
604
|
+
{
|
|
605
|
+
label: item.label ?? '',
|
|
606
|
+
formattedValue: formatter(value, { location: 'a11y', seriesId: item.id, dataIndex }),
|
|
607
|
+
},
|
|
608
|
+
];
|
|
609
|
+
}),
|
|
610
|
+
}));
|
|
611
|
+
const summary = accessibilityLabel ?? localeText.barChartSummary(summaryCategories);
|
|
612
|
+
const trigger = tooltipProp?.trigger ?? 'axis';
|
|
613
|
+
const tooltip = trigger !== 'none';
|
|
614
|
+
const interactive =
|
|
615
|
+
onSelectedItemChange !== undefined ||
|
|
616
|
+
selectedItem !== undefined ||
|
|
617
|
+
defaultSelectedItem !== undefined;
|
|
618
|
+
const [selectedState, setSelectedItem] = useControllableState<ChartSelectedItem>({
|
|
619
|
+
prop: selectedItem,
|
|
620
|
+
defaultProp: defaultSelectedItem ?? null,
|
|
621
|
+
onChange: onSelectedItemChange,
|
|
622
|
+
});
|
|
623
|
+
const selected = selectedState ?? null;
|
|
624
|
+
const visibleBars = layout.bars.filter((bar) =>
|
|
625
|
+
bar.stackId !== undefined
|
|
626
|
+
? bar.width > 0 && bar.height > 0
|
|
627
|
+
: orientation === 'vertical'
|
|
628
|
+
? bar.width > 0
|
|
629
|
+
: bar.height > 0,
|
|
630
|
+
);
|
|
631
|
+
const inspectable = (interactive || tooltip) && visibleBars.length > 0;
|
|
632
|
+
const [activeIndex, setActiveIndex] = useState<number | null>(null);
|
|
633
|
+
const [dismissed, setDismissed] = useState(false);
|
|
634
|
+
const [scrubbing, setScrubbing] = useState(false);
|
|
635
|
+
const scrollOffset = useRef({ x: 0, y: 0 });
|
|
636
|
+
const selectedIndex = selected
|
|
637
|
+
? visibleBars.findIndex((bar) => sameItem(selected, barSelection(bar)))
|
|
638
|
+
: -1;
|
|
639
|
+
const activeBar = activeIndex === null ? undefined : visibleBars.at(activeIndex);
|
|
640
|
+
const highlightBar: BarSelection | null = activeBar
|
|
641
|
+
? barSelection(activeBar)
|
|
642
|
+
: selectedIndex >= 0 && selected !== null
|
|
643
|
+
? selected
|
|
644
|
+
: null;
|
|
645
|
+
const tooltipIndex = tooltip && !dismissed && activeIndex !== null ? activeIndex : null;
|
|
646
|
+
const activate = (index: number) => {
|
|
647
|
+
setDismissed(false);
|
|
648
|
+
setActiveIndex((prev) => (prev === index ? prev : index));
|
|
649
|
+
};
|
|
650
|
+
const deactivate = (index: number) => {
|
|
651
|
+
setActiveIndex((prev) => (prev === index ? null : prev));
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
const showLegend = !waterfall && plan.series.length > 0 && !hideLegend;
|
|
655
|
+
|
|
656
|
+
const plot = (
|
|
657
|
+
<BarPlot
|
|
658
|
+
layout={layout}
|
|
659
|
+
orientation={orientation}
|
|
660
|
+
overlay={overlay}
|
|
661
|
+
waterfall={waterfall}
|
|
662
|
+
showGrid={showGrid}
|
|
663
|
+
barLabel={barLabel}
|
|
664
|
+
formatBar={formatBar}
|
|
665
|
+
formatStackTotal={(anchor) => {
|
|
666
|
+
const context = { location: 'label' as const, dataIndex: anchor.index };
|
|
667
|
+
if (anchor.expand) {
|
|
668
|
+
return (valueAxisFormatter ?? percentFormatter)(anchor.total / 100, context);
|
|
669
|
+
}
|
|
670
|
+
return resolveValueFormatter(
|
|
671
|
+
sourceById.get(anchor.seriesId),
|
|
672
|
+
valueAxis,
|
|
673
|
+
locale,
|
|
674
|
+
)(anchor.total, context);
|
|
675
|
+
}}
|
|
676
|
+
fills={fills}
|
|
677
|
+
gradientId={gradientId}
|
|
678
|
+
chrome={chrome}
|
|
679
|
+
accent={simpleColor}
|
|
680
|
+
borderRadius={Math.max(borderRadius, 0)}
|
|
681
|
+
animated={animated}
|
|
682
|
+
highlightBar={highlightBar}
|
|
683
|
+
emphasizedIndex={emphasizedIndex}
|
|
684
|
+
/>
|
|
685
|
+
);
|
|
686
|
+
|
|
687
|
+
const vertical = orientation === 'vertical';
|
|
688
|
+
const targets = inspectable
|
|
689
|
+
? barInspectorTargets({
|
|
690
|
+
bars: visibleBars,
|
|
691
|
+
layout,
|
|
692
|
+
orientation,
|
|
693
|
+
overlay,
|
|
694
|
+
waterfall,
|
|
695
|
+
trigger,
|
|
696
|
+
offsetX: vertical ? layout.plot.x : 0,
|
|
697
|
+
fills,
|
|
698
|
+
fallbackFill: chrome.grid,
|
|
699
|
+
formatBar,
|
|
700
|
+
seriesLabelOf,
|
|
701
|
+
localeText,
|
|
702
|
+
selectedIndex: selectedIndex >= 0 ? selectedIndex : null,
|
|
703
|
+
})
|
|
704
|
+
: [];
|
|
705
|
+
const tip = tooltipIndex === null ? undefined : targets.at(tooltipIndex);
|
|
706
|
+
const dismissOnScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
707
|
+
scrollOffset.current = event.nativeEvent.contentOffset;
|
|
708
|
+
if (tooltipIndex !== null) {
|
|
709
|
+
setDismissed(true);
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
const inspector = inspectable ? (
|
|
714
|
+
<BarInspector
|
|
715
|
+
bars={visibleBars}
|
|
716
|
+
targets={targets}
|
|
717
|
+
orientation={orientation}
|
|
718
|
+
summary={summary}
|
|
719
|
+
selectable={interactive}
|
|
720
|
+
activeIndex={activeIndex}
|
|
721
|
+
selectedIndex={selectedIndex >= 0 ? selectedIndex : null}
|
|
722
|
+
holdToScrub={overflowsX || overflowsY}
|
|
723
|
+
onScrubLockChange={setScrubbing}
|
|
724
|
+
onActivate={activate}
|
|
725
|
+
onDeactivate={deactivate}
|
|
726
|
+
onDismiss={() => {
|
|
727
|
+
setDismissed(true);
|
|
728
|
+
}}
|
|
729
|
+
onSelectIndex={(index) => {
|
|
730
|
+
const bar = visibleBars.at(index);
|
|
731
|
+
if (bar) {
|
|
732
|
+
setDismissed(false);
|
|
733
|
+
const next = barSelection(bar);
|
|
734
|
+
setSelectedItem(sameItem(selected, next) ? null : next);
|
|
735
|
+
}
|
|
736
|
+
}}
|
|
737
|
+
/>
|
|
738
|
+
) : null;
|
|
739
|
+
const tooltipLayer =
|
|
740
|
+
tip !== undefined ? (
|
|
741
|
+
<ChartTooltipLayer
|
|
742
|
+
content={tip.tooltip}
|
|
743
|
+
noValueText={localeText.noValue}
|
|
744
|
+
slot="bar-chart"
|
|
745
|
+
anchorX={vertical ? layout.plot.x + tip.anchorX - scrollOffset.current.x : tip.anchorX}
|
|
746
|
+
anchorY={vertical ? tip.anchorY : tip.anchorY - scrollOffset.current.y}
|
|
747
|
+
anchorRadius={tip.anchorRadius}
|
|
748
|
+
width={viewportWidth}
|
|
749
|
+
height={viewportHeight}
|
|
750
|
+
/>
|
|
751
|
+
) : null;
|
|
752
|
+
|
|
753
|
+
const ticks = showTicks ? (
|
|
754
|
+
<>
|
|
755
|
+
{layout.valueTicks.map((tick) => (
|
|
756
|
+
<SvgText
|
|
757
|
+
key={`tick-${String(tick.value)}`}
|
|
758
|
+
x={tick.x}
|
|
759
|
+
y={tick.y}
|
|
760
|
+
fill={chrome.label}
|
|
761
|
+
fontFamily={chrome.fontFamily}
|
|
762
|
+
fontSize={chrome.labelSize}
|
|
763
|
+
textAnchor={orientation === 'vertical' ? 'end' : 'middle'}
|
|
764
|
+
alignmentBaseline="middle"
|
|
765
|
+
>
|
|
766
|
+
{tick.text}
|
|
767
|
+
</SvgText>
|
|
768
|
+
))}
|
|
769
|
+
</>
|
|
770
|
+
) : null;
|
|
771
|
+
|
|
772
|
+
const chart = (
|
|
773
|
+
<View
|
|
774
|
+
style={{ width: '100%', maxWidth: viewportWidth, height: viewportHeight }}
|
|
775
|
+
{...dataAttributes({ slot: 'bar-chart-plot' })}
|
|
776
|
+
>
|
|
777
|
+
{orientation === 'vertical' ? (
|
|
778
|
+
<View className="w-full flex-row" style={{ height: viewportHeight }}>
|
|
779
|
+
<Svg
|
|
780
|
+
width={layout.plot.x}
|
|
781
|
+
height={viewportHeight}
|
|
782
|
+
viewBox={`0 0 ${String(layout.plot.x)} ${String(content.height)}`}
|
|
783
|
+
{...HIDDEN_SVG_PROPS}
|
|
784
|
+
>
|
|
785
|
+
{ticks}
|
|
786
|
+
</Svg>
|
|
787
|
+
<ScrollView
|
|
788
|
+
horizontal
|
|
789
|
+
nestedScrollEnabled
|
|
790
|
+
scrollEnabled={overflowsX && !scrubbing}
|
|
791
|
+
showsHorizontalScrollIndicator={overflowsX}
|
|
792
|
+
scrollEventThrottle={16}
|
|
793
|
+
onScroll={dismissOnScroll}
|
|
794
|
+
className="flex-1"
|
|
795
|
+
style={{ height: viewportHeight }}
|
|
796
|
+
>
|
|
797
|
+
<View style={{ width: content.width - layout.plot.x, height: viewportHeight }}>
|
|
798
|
+
<Svg
|
|
799
|
+
width={content.width - layout.plot.x}
|
|
800
|
+
height={viewportHeight}
|
|
801
|
+
viewBox={`0 0 ${String(content.width - layout.plot.x)} ${String(content.height)}`}
|
|
802
|
+
style={SVG_OVERFLOW_VISIBLE}
|
|
803
|
+
pointerEvents="none"
|
|
804
|
+
{...HIDDEN_SVG_PROPS}
|
|
805
|
+
>
|
|
806
|
+
<G transform={`translate(${String(-layout.plot.x)}, 0)`}>{plot}</G>
|
|
807
|
+
</Svg>
|
|
808
|
+
{inspector}
|
|
809
|
+
</View>
|
|
810
|
+
</ScrollView>
|
|
811
|
+
</View>
|
|
812
|
+
) : (
|
|
813
|
+
<View style={{ width: viewportWidth, height: viewportHeight }}>
|
|
814
|
+
<ScrollView
|
|
815
|
+
nestedScrollEnabled
|
|
816
|
+
scrollEnabled={overflowsY && !scrubbing}
|
|
817
|
+
showsVerticalScrollIndicator={overflowsY}
|
|
818
|
+
scrollEventThrottle={16}
|
|
819
|
+
onScroll={dismissOnScroll}
|
|
820
|
+
style={{ height: viewportHeight - gutters.paddingBottom }}
|
|
821
|
+
>
|
|
822
|
+
<View style={{ width: viewportWidth, height: content.height - gutters.paddingBottom }}>
|
|
823
|
+
<Svg
|
|
824
|
+
width={viewportWidth}
|
|
825
|
+
height={content.height - gutters.paddingBottom}
|
|
826
|
+
viewBox={`0 0 ${String(content.width)} ${String(content.height - gutters.paddingBottom)}`}
|
|
827
|
+
pointerEvents="none"
|
|
828
|
+
{...HIDDEN_SVG_PROPS}
|
|
829
|
+
>
|
|
830
|
+
{plot}
|
|
831
|
+
</Svg>
|
|
832
|
+
{inspector}
|
|
833
|
+
</View>
|
|
834
|
+
</ScrollView>
|
|
835
|
+
<Svg
|
|
836
|
+
width={viewportWidth}
|
|
837
|
+
height={gutters.paddingBottom}
|
|
838
|
+
viewBox={`0 0 ${String(content.width)} ${String(gutters.paddingBottom)}`}
|
|
839
|
+
{...HIDDEN_SVG_PROPS}
|
|
840
|
+
>
|
|
841
|
+
<G transform={`translate(0, ${String(-(content.height - gutters.paddingBottom))})`}>
|
|
842
|
+
{ticks}
|
|
843
|
+
</G>
|
|
844
|
+
</Svg>
|
|
845
|
+
</View>
|
|
846
|
+
)}
|
|
847
|
+
{tooltipLayer}
|
|
848
|
+
</View>
|
|
849
|
+
);
|
|
850
|
+
|
|
851
|
+
return (
|
|
852
|
+
<View
|
|
853
|
+
ref={ref}
|
|
854
|
+
className={cn(barChartRootVariants(), className)}
|
|
855
|
+
style={style}
|
|
856
|
+
accessible={!inspectable && !showLegend}
|
|
857
|
+
accessibilityRole={!inspectable && !showLegend ? 'image' : undefined}
|
|
858
|
+
accessibilityLabel={!inspectable && !showLegend ? summary : undefined}
|
|
859
|
+
{...props}
|
|
860
|
+
onLayout={handleHostLayout}
|
|
861
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'bar-chart' }))}
|
|
862
|
+
>
|
|
863
|
+
{chart}
|
|
864
|
+
{showLegend ? (
|
|
865
|
+
<View
|
|
866
|
+
className={barChartLegendVariants()}
|
|
867
|
+
{...dataAttributes({ slot: 'bar-chart-legend' })}
|
|
868
|
+
>
|
|
869
|
+
{plan.series.map((item, seriesIndex) => {
|
|
870
|
+
const isSelected = highlightBar?.seriesId === item.id;
|
|
871
|
+
return (
|
|
872
|
+
<View
|
|
873
|
+
key={item.id}
|
|
874
|
+
className={barChartLegendItemVariants()}
|
|
875
|
+
{...dataAttributes({ slot: 'bar-chart-legend-item' })}
|
|
876
|
+
>
|
|
877
|
+
<View
|
|
878
|
+
className="h-2 w-3 rounded-sm"
|
|
879
|
+
style={{ backgroundColor: fills[seriesIndex] }}
|
|
880
|
+
/>
|
|
881
|
+
<Text className={barChartLegendLabelVariants({ selected: isSelected })}>
|
|
882
|
+
{item.label !== undefined && item.label.length > 0
|
|
883
|
+
? item.label
|
|
884
|
+
: localeText.defaultSeriesLabel}
|
|
885
|
+
</Text>
|
|
886
|
+
</View>
|
|
887
|
+
);
|
|
888
|
+
})}
|
|
889
|
+
</View>
|
|
890
|
+
) : null}
|
|
891
|
+
</View>
|
|
892
|
+
);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Category bars from `dataset` rows. MUI X Charts–shaped: `series[].dataKey`, axis arrays,
|
|
897
|
+
* `layout`, `stack` groups, and `selectedItem`.
|
|
898
|
+
*/
|
|
899
|
+
export const BarChart = forwardRef(BarChartInner) as <TRow extends object>(
|
|
900
|
+
props: BarChartProps<TRow> & { ref?: ForwardedRef<View> },
|
|
901
|
+
) => ReactElement | null;
|
|
902
|
+
|
|
903
|
+
(BarChart as { displayName?: string }).displayName = 'BarChart';
|
|
904
|
+
|
|
905
|
+
function BarPlot({
|
|
906
|
+
layout,
|
|
907
|
+
orientation,
|
|
908
|
+
overlay,
|
|
909
|
+
waterfall,
|
|
910
|
+
showGrid,
|
|
911
|
+
barLabel,
|
|
912
|
+
formatBar,
|
|
913
|
+
formatStackTotal,
|
|
914
|
+
fills,
|
|
915
|
+
gradientId,
|
|
916
|
+
chrome,
|
|
917
|
+
accent,
|
|
918
|
+
borderRadius,
|
|
919
|
+
animated,
|
|
920
|
+
highlightBar,
|
|
921
|
+
emphasizedIndex,
|
|
922
|
+
}: {
|
|
923
|
+
layout: BarChartLayout;
|
|
924
|
+
orientation: BarChartOrientation;
|
|
925
|
+
overlay: boolean;
|
|
926
|
+
waterfall: boolean;
|
|
927
|
+
showGrid: boolean;
|
|
928
|
+
barLabel: boolean;
|
|
929
|
+
formatBar: BarFormatter;
|
|
930
|
+
formatStackTotal: (anchor: ReturnType<typeof stackedTotalAnchors>[number]) => string;
|
|
931
|
+
fills: readonly string[];
|
|
932
|
+
gradientId: string;
|
|
933
|
+
chrome: ChartChrome;
|
|
934
|
+
accent: string;
|
|
935
|
+
borderRadius: number;
|
|
936
|
+
animated: boolean;
|
|
937
|
+
highlightBar: BarSelection | null;
|
|
938
|
+
emphasizedIndex: number | undefined;
|
|
939
|
+
}) {
|
|
940
|
+
const labeledBars = layout.bars.filter((bar) => {
|
|
941
|
+
if (bar.stackId !== undefined) {
|
|
942
|
+
return false;
|
|
943
|
+
}
|
|
944
|
+
if (emphasizedIndex !== undefined && bar.index !== emphasizedIndex) {
|
|
945
|
+
return false;
|
|
946
|
+
}
|
|
947
|
+
return !overlay || bar.seriesIndex === 0;
|
|
948
|
+
});
|
|
949
|
+
const stackLabels = stackedTotalAnchors(layout.bars, orientation).filter(
|
|
950
|
+
(anchor) => emphasizedIndex === undefined || anchor.index === emphasizedIndex,
|
|
951
|
+
);
|
|
952
|
+
const paintedBars = overlay
|
|
953
|
+
? [...layout.bars].sort((a, b) => b.seriesIndex - a.seriesIndex)
|
|
954
|
+
: layout.bars;
|
|
955
|
+
|
|
956
|
+
return (
|
|
957
|
+
<>
|
|
958
|
+
<ChartLinearFills
|
|
959
|
+
idPrefix={gradientId}
|
|
960
|
+
colors={fills}
|
|
961
|
+
x2={orientation === 'vertical' ? '0%' : '100%'}
|
|
962
|
+
y2={orientation === 'vertical' ? '100%' : '0%'}
|
|
963
|
+
/>
|
|
964
|
+
{showGrid ? (
|
|
965
|
+
layout.gridLines.map((line) => (
|
|
966
|
+
<Line
|
|
967
|
+
key={`grid-${String(line.x1)}-${String(line.y1)}`}
|
|
968
|
+
x1={line.x1}
|
|
969
|
+
y1={line.y1}
|
|
970
|
+
x2={line.x2}
|
|
971
|
+
y2={line.y2}
|
|
972
|
+
stroke={chrome.grid}
|
|
973
|
+
strokeWidth={1}
|
|
974
|
+
/>
|
|
975
|
+
))
|
|
976
|
+
) : (
|
|
977
|
+
<Line
|
|
978
|
+
x1={orientation === 'vertical' ? layout.plot.x : layout.baseline}
|
|
979
|
+
y1={orientation === 'vertical' ? layout.baseline : layout.plot.y}
|
|
980
|
+
x2={orientation === 'vertical' ? layout.plot.x + layout.plot.width : layout.baseline}
|
|
981
|
+
y2={orientation === 'vertical' ? layout.baseline : layout.plot.y + layout.plot.height}
|
|
982
|
+
stroke={chrome.grid}
|
|
983
|
+
strokeWidth={1}
|
|
984
|
+
/>
|
|
985
|
+
)}
|
|
986
|
+
{layout.referenceLines.map((line, index) => (
|
|
987
|
+
<G key={`ref-${String(line.x1)}-${String(line.y1)}-${String(index)}`}>
|
|
988
|
+
<Line
|
|
989
|
+
x1={line.x1}
|
|
990
|
+
y1={line.y1}
|
|
991
|
+
x2={line.x2}
|
|
992
|
+
y2={line.y2}
|
|
993
|
+
stroke={accent}
|
|
994
|
+
strokeWidth={1}
|
|
995
|
+
strokeDasharray={DASH}
|
|
996
|
+
/>
|
|
997
|
+
{line.label !== undefined && line.label.length > 0 ? (
|
|
998
|
+
<SvgText
|
|
999
|
+
x={line.x1 + 4}
|
|
1000
|
+
y={orientation === 'vertical' ? line.y1 - 4 : line.y1 + 12}
|
|
1001
|
+
fill={accent}
|
|
1002
|
+
fontFamily={chrome.fontFamily}
|
|
1003
|
+
fontSize={chrome.captionSize}
|
|
1004
|
+
textAnchor="start"
|
|
1005
|
+
>
|
|
1006
|
+
{line.label}
|
|
1007
|
+
</SvgText>
|
|
1008
|
+
) : null}
|
|
1009
|
+
</G>
|
|
1010
|
+
))}
|
|
1011
|
+
{layout.connectors.map((line, index) => (
|
|
1012
|
+
<Line
|
|
1013
|
+
key={`wf-${String(line.x1)}-${String(line.y1)}-${String(index)}`}
|
|
1014
|
+
x1={line.x1}
|
|
1015
|
+
y1={line.y1}
|
|
1016
|
+
x2={line.x2}
|
|
1017
|
+
y2={line.y2}
|
|
1018
|
+
stroke={chrome.grid}
|
|
1019
|
+
strokeWidth={1}
|
|
1020
|
+
strokeDasharray={DASH}
|
|
1021
|
+
/>
|
|
1022
|
+
))}
|
|
1023
|
+
{paintedBars.map((bar) => {
|
|
1024
|
+
if (bar.width <= 0 || bar.height <= 0) {
|
|
1025
|
+
return null;
|
|
1026
|
+
}
|
|
1027
|
+
const dimmed =
|
|
1028
|
+
highlightBar !== null &&
|
|
1029
|
+
(bar.stackId !== undefined
|
|
1030
|
+
? !sameItem(highlightBar, barSelection(bar))
|
|
1031
|
+
: highlightBar.dataIndex !== bar.index);
|
|
1032
|
+
const muted = emphasizedIndex !== undefined && bar.index !== emphasizedIndex;
|
|
1033
|
+
const comparison = overlay && bar.seriesIndex > 0;
|
|
1034
|
+
return (
|
|
1035
|
+
<AnimatedBar
|
|
1036
|
+
key={`${String(bar.index)}-${bar.seriesId}`}
|
|
1037
|
+
bar={bar}
|
|
1038
|
+
orientation={orientation}
|
|
1039
|
+
baseline={layout.baseline}
|
|
1040
|
+
animated={animated}
|
|
1041
|
+
fill={
|
|
1042
|
+
muted || (waterfall && dimmed)
|
|
1043
|
+
? chrome.grid
|
|
1044
|
+
: comparison || waterfall
|
|
1045
|
+
? (fills[bar.seriesIndex] ?? chrome.grid)
|
|
1046
|
+
: chartFillUrl(gradientId, bar.seriesIndex)
|
|
1047
|
+
}
|
|
1048
|
+
opacity={waterfall ? 1 : dimmed ? INACTIVE_OPACITY : 1}
|
|
1049
|
+
radius={borderRadius}
|
|
1050
|
+
/>
|
|
1051
|
+
);
|
|
1052
|
+
})}
|
|
1053
|
+
{layout.categoryLabels.map((item, index) => (
|
|
1054
|
+
<SvgText
|
|
1055
|
+
key={`cat-${String(index)}-${item.label}`}
|
|
1056
|
+
x={item.x}
|
|
1057
|
+
y={item.y}
|
|
1058
|
+
fill={chrome.label}
|
|
1059
|
+
fontFamily={chrome.fontFamily}
|
|
1060
|
+
fontSize={chrome.labelSize}
|
|
1061
|
+
textAnchor={item.textAnchor}
|
|
1062
|
+
alignmentBaseline="middle"
|
|
1063
|
+
>
|
|
1064
|
+
{item.label}
|
|
1065
|
+
</SvgText>
|
|
1066
|
+
))}
|
|
1067
|
+
{barLabel ? (
|
|
1068
|
+
<ValueLabels
|
|
1069
|
+
animated={animated}
|
|
1070
|
+
motionKey={`${orientation}-${overlay ? '1' : '0'}-${layout.bars
|
|
1071
|
+
.map((bar) => [bar.x, bar.y, bar.width, bar.height].join(','))
|
|
1072
|
+
.join(';')}`}
|
|
1073
|
+
>
|
|
1074
|
+
{(labelOpacity) => (
|
|
1075
|
+
<>
|
|
1076
|
+
{stackLabels.map((label) => (
|
|
1077
|
+
<SvgText
|
|
1078
|
+
key={`stack-val-${label.key}`}
|
|
1079
|
+
x={label.x}
|
|
1080
|
+
y={label.y}
|
|
1081
|
+
fill={chrome.label}
|
|
1082
|
+
fillOpacity={labelOpacity}
|
|
1083
|
+
fontFamily={chrome.fontFamily}
|
|
1084
|
+
fontSize={chrome.captionSize}
|
|
1085
|
+
textAnchor={label.textAnchor}
|
|
1086
|
+
alignmentBaseline="middle"
|
|
1087
|
+
>
|
|
1088
|
+
{formatStackTotal(label)}
|
|
1089
|
+
</SvgText>
|
|
1090
|
+
))}
|
|
1091
|
+
{labeledBars.map((bar) => {
|
|
1092
|
+
const anchor = waterfall
|
|
1093
|
+
? waterfallValueAnchor(bar, orientation)
|
|
1094
|
+
: barValueAnchor(bar, orientation);
|
|
1095
|
+
return (
|
|
1096
|
+
<G key={`val-${String(bar.index)}-${bar.seriesId}`}>
|
|
1097
|
+
<SvgText
|
|
1098
|
+
x={anchor.x}
|
|
1099
|
+
y={anchor.y}
|
|
1100
|
+
fill={chrome.label}
|
|
1101
|
+
fillOpacity={labelOpacity}
|
|
1102
|
+
fontFamily={chrome.fontFamily}
|
|
1103
|
+
fontSize={chrome.captionSize}
|
|
1104
|
+
textAnchor={anchor.textAnchor}
|
|
1105
|
+
alignmentBaseline="middle"
|
|
1106
|
+
>
|
|
1107
|
+
{formatBar(bar, 'label')}
|
|
1108
|
+
</SvgText>
|
|
1109
|
+
</G>
|
|
1110
|
+
);
|
|
1111
|
+
})}
|
|
1112
|
+
</>
|
|
1113
|
+
)}
|
|
1114
|
+
</ValueLabels>
|
|
1115
|
+
) : null}
|
|
1116
|
+
</>
|
|
1117
|
+
);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
function barTooltipContent(
|
|
1121
|
+
group: readonly BarRect[],
|
|
1122
|
+
bar: BarRect,
|
|
1123
|
+
trigger: 'item' | 'axis',
|
|
1124
|
+
fills: readonly string[],
|
|
1125
|
+
fallbackFill: string,
|
|
1126
|
+
formatBar: BarFormatter,
|
|
1127
|
+
seriesLabelOf: (bar: BarRect) => string,
|
|
1128
|
+
): ChartTooltipContent {
|
|
1129
|
+
const shown = trigger === 'axis' ? group : [bar];
|
|
1130
|
+
return {
|
|
1131
|
+
title: bar.label,
|
|
1132
|
+
items: shown.map((item) => ({
|
|
1133
|
+
seriesId: item.seriesId,
|
|
1134
|
+
label: seriesLabelOf(item),
|
|
1135
|
+
color: fills[item.seriesIndex] ?? fallbackFill,
|
|
1136
|
+
formattedValue: formatBar(item, 'tooltip'),
|
|
1137
|
+
})),
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/**
|
|
1142
|
+
* One hit target per drawn bar. Grouped bars get the full value-axis span of their slot so short
|
|
1143
|
+
* bars stay easy to hover. Stacked segments keep their own rect, expanded to a 44px minimum
|
|
1144
|
+
* (WCAG 2.5.8 / HIG) so a 9px-tall bar is still a usable touch target.
|
|
1145
|
+
*/
|
|
1146
|
+
/** Targets in scroll-content coordinates, shifted left by `offsetX`. */
|
|
1147
|
+
function barInspectorTargets({
|
|
1148
|
+
bars,
|
|
1149
|
+
layout,
|
|
1150
|
+
orientation,
|
|
1151
|
+
overlay,
|
|
1152
|
+
waterfall,
|
|
1153
|
+
trigger,
|
|
1154
|
+
offsetX,
|
|
1155
|
+
fills,
|
|
1156
|
+
fallbackFill,
|
|
1157
|
+
formatBar,
|
|
1158
|
+
seriesLabelOf,
|
|
1159
|
+
localeText,
|
|
1160
|
+
selectedIndex,
|
|
1161
|
+
}: {
|
|
1162
|
+
bars: readonly BarRect[];
|
|
1163
|
+
layout: BarChartLayout;
|
|
1164
|
+
orientation: BarChartOrientation;
|
|
1165
|
+
overlay: boolean;
|
|
1166
|
+
waterfall: boolean;
|
|
1167
|
+
trigger: 'item' | 'axis' | 'none';
|
|
1168
|
+
offsetX: number;
|
|
1169
|
+
fills: readonly string[];
|
|
1170
|
+
fallbackFill: string;
|
|
1171
|
+
formatBar: BarFormatter;
|
|
1172
|
+
seriesLabelOf: (bar: BarRect) => string;
|
|
1173
|
+
localeText: ChartLocaleText;
|
|
1174
|
+
selectedIndex: number | null;
|
|
1175
|
+
}): ChartInspectorTarget[] {
|
|
1176
|
+
const { plot } = layout;
|
|
1177
|
+
const vertical = orientation === 'vertical';
|
|
1178
|
+
const groups = new Map<number, BarRect[]>();
|
|
1179
|
+
bars.forEach((bar) => {
|
|
1180
|
+
const group = groups.get(bar.index);
|
|
1181
|
+
if (group === undefined) {
|
|
1182
|
+
groups.set(bar.index, [bar]);
|
|
1183
|
+
} else {
|
|
1184
|
+
group.push(bar);
|
|
1185
|
+
}
|
|
1186
|
+
});
|
|
1187
|
+
const outlines = new Map(
|
|
1188
|
+
[...groups].map(([categoryIndex, group]) => {
|
|
1189
|
+
const start = Math.min(...group.map((item) => (vertical ? item.x : item.y)));
|
|
1190
|
+
const end = Math.max(
|
|
1191
|
+
...group.map((item) => (vertical ? item.x + item.width : item.y + item.height)),
|
|
1192
|
+
);
|
|
1193
|
+
const outline = vertical
|
|
1194
|
+
? { left: start - offsetX - 4, top: plot.y, width: end - start + 8, height: plot.height }
|
|
1195
|
+
: { left: plot.x - offsetX, top: start - 4, width: plot.width, height: end - start + 8 };
|
|
1196
|
+
return [categoryIndex, outline] as const;
|
|
1197
|
+
}),
|
|
1198
|
+
);
|
|
1199
|
+
const trendText = (bar: BarRect): string | null => {
|
|
1200
|
+
if (!overlay || bar.seriesIndex !== 0) {
|
|
1201
|
+
return null;
|
|
1202
|
+
}
|
|
1203
|
+
const group = groups.get(bar.index) ?? [];
|
|
1204
|
+
const delta = overlayDelta(group, bar.index);
|
|
1205
|
+
if (delta === null) {
|
|
1206
|
+
return null;
|
|
1207
|
+
}
|
|
1208
|
+
const comparison = group.find((item) => item.seriesIndex === 1);
|
|
1209
|
+
return localeText.trend({
|
|
1210
|
+
direction: delta > 0 ? 'up' : delta < 0 ? 'down' : 'none',
|
|
1211
|
+
formattedDelta: formatBar({ ...bar, value: Math.abs(delta) }, 'a11y'),
|
|
1212
|
+
comparisonLabel: comparison?.seriesLabel ?? '',
|
|
1213
|
+
});
|
|
1214
|
+
};
|
|
1215
|
+
return bars.map((bar, index): ChartInspectorTarget => {
|
|
1216
|
+
const x = bar.x - offsetX;
|
|
1217
|
+
const raw =
|
|
1218
|
+
bar.stackId !== undefined || waterfall || !vertical
|
|
1219
|
+
? { left: x, top: bar.y, width: bar.width, height: bar.height }
|
|
1220
|
+
: { left: x, top: plot.y, width: bar.width, height: plot.height };
|
|
1221
|
+
const box = expandHitBox(raw.left, raw.top, raw.width, raw.height);
|
|
1222
|
+
const group = groups.get(bar.index) ?? [bar];
|
|
1223
|
+
const outline = outlines.get(bar.index);
|
|
1224
|
+
return {
|
|
1225
|
+
key: `hit-${String(bar.index)}-${bar.seriesId}`,
|
|
1226
|
+
...box,
|
|
1227
|
+
outline,
|
|
1228
|
+
anchorX: vertical ? x + bar.width / 2 : x + bar.width,
|
|
1229
|
+
anchorY: bar.y,
|
|
1230
|
+
anchorRadius: vertical ? bar.width / 2 : 0,
|
|
1231
|
+
tooltip: barTooltipContent(
|
|
1232
|
+
group,
|
|
1233
|
+
bar,
|
|
1234
|
+
trigger === 'item' ? 'item' : 'axis',
|
|
1235
|
+
fills,
|
|
1236
|
+
fallbackFill,
|
|
1237
|
+
formatBar,
|
|
1238
|
+
seriesLabelOf,
|
|
1239
|
+
),
|
|
1240
|
+
accessibilityLabel: barAccessibilityName(
|
|
1241
|
+
bar.label,
|
|
1242
|
+
seriesLabelOf(bar),
|
|
1243
|
+
formatBar(bar, 'a11y'),
|
|
1244
|
+
trendText(bar),
|
|
1245
|
+
),
|
|
1246
|
+
selected: selectedIndex === index,
|
|
1247
|
+
};
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
function BarInspector({
|
|
1252
|
+
bars,
|
|
1253
|
+
targets,
|
|
1254
|
+
orientation,
|
|
1255
|
+
summary,
|
|
1256
|
+
selectable,
|
|
1257
|
+
activeIndex,
|
|
1258
|
+
selectedIndex,
|
|
1259
|
+
onActivate,
|
|
1260
|
+
onDeactivate,
|
|
1261
|
+
onDismiss,
|
|
1262
|
+
onSelectIndex,
|
|
1263
|
+
holdToScrub,
|
|
1264
|
+
onScrubLockChange,
|
|
1265
|
+
}: {
|
|
1266
|
+
onScrubLockChange: (locked: boolean) => void;
|
|
1267
|
+
bars: readonly BarRect[];
|
|
1268
|
+
targets: readonly ChartInspectorTarget[];
|
|
1269
|
+
orientation: BarChartOrientation;
|
|
1270
|
+
summary: string;
|
|
1271
|
+
selectable: boolean;
|
|
1272
|
+
activeIndex: number | null;
|
|
1273
|
+
selectedIndex: number | null;
|
|
1274
|
+
holdToScrub: boolean;
|
|
1275
|
+
onActivate: (index: number) => void;
|
|
1276
|
+
onDeactivate: (index: number) => void;
|
|
1277
|
+
onDismiss: () => void;
|
|
1278
|
+
onSelectIndex: (index: number) => void;
|
|
1279
|
+
}) {
|
|
1280
|
+
return (
|
|
1281
|
+
<ChartInspector
|
|
1282
|
+
targets={targets}
|
|
1283
|
+
summary={summary}
|
|
1284
|
+
selectable={selectable}
|
|
1285
|
+
activeIndex={activeIndex}
|
|
1286
|
+
selectedIndex={selectedIndex}
|
|
1287
|
+
slot="bar-chart"
|
|
1288
|
+
holdToScrub={holdToScrub}
|
|
1289
|
+
onScrubLockChange={onScrubLockChange}
|
|
1290
|
+
hitTest={(x, y) => {
|
|
1291
|
+
let best: number | null = null;
|
|
1292
|
+
let bestDist = Number.POSITIVE_INFINITY;
|
|
1293
|
+
targets.forEach((target, index) => {
|
|
1294
|
+
if (
|
|
1295
|
+
x < target.left ||
|
|
1296
|
+
x > target.left + target.width ||
|
|
1297
|
+
y < target.top ||
|
|
1298
|
+
y > target.top + target.height
|
|
1299
|
+
) {
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
const dist = Math.hypot(
|
|
1303
|
+
x - (target.left + target.width / 2),
|
|
1304
|
+
y - (target.top + target.height / 2),
|
|
1305
|
+
);
|
|
1306
|
+
if (dist < bestDist) {
|
|
1307
|
+
bestDist = dist;
|
|
1308
|
+
best = index;
|
|
1309
|
+
}
|
|
1310
|
+
});
|
|
1311
|
+
return best;
|
|
1312
|
+
}}
|
|
1313
|
+
onActivate={onActivate}
|
|
1314
|
+
onDeactivate={onDeactivate}
|
|
1315
|
+
onDismiss={onDismiss}
|
|
1316
|
+
onSelectIndex={onSelectIndex}
|
|
1317
|
+
neighborIndex={(index, key) => barKeyboardNeighbor(bars, index, key, orientation)}
|
|
1318
|
+
/>
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
function collapsedBar(bar: BarRect, orientation: BarChartOrientation, baseline: number): number[] {
|
|
1323
|
+
if (orientation === 'vertical') {
|
|
1324
|
+
const onBaseline = Math.abs((bar.plotValue >= 0 ? bar.y + bar.height : bar.y) - baseline) < 1;
|
|
1325
|
+
return onBaseline ? [bar.x, baseline, bar.width, 0] : [bar.x, bar.y + bar.height, bar.width, 0];
|
|
1326
|
+
}
|
|
1327
|
+
const onBaseline = Math.abs((bar.plotValue >= 0 ? bar.x : bar.x + bar.width) - baseline) < 1;
|
|
1328
|
+
return onBaseline ? [baseline, bar.y, 0, bar.height] : [bar.x, bar.y, 0, bar.height];
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
function AnimatedBar({
|
|
1332
|
+
bar,
|
|
1333
|
+
orientation,
|
|
1334
|
+
baseline,
|
|
1335
|
+
animated,
|
|
1336
|
+
fill,
|
|
1337
|
+
opacity,
|
|
1338
|
+
radius,
|
|
1339
|
+
}: {
|
|
1340
|
+
bar: BarRect;
|
|
1341
|
+
orientation: BarChartOrientation;
|
|
1342
|
+
baseline: number;
|
|
1343
|
+
animated: boolean;
|
|
1344
|
+
fill: string;
|
|
1345
|
+
opacity: number;
|
|
1346
|
+
radius: number;
|
|
1347
|
+
}) {
|
|
1348
|
+
const { rounded, roundAll, value } = bar;
|
|
1349
|
+
const morph = useChartMorph(
|
|
1350
|
+
[bar.x, bar.y, bar.width, bar.height],
|
|
1351
|
+
collapsedBar(bar, orientation, baseline),
|
|
1352
|
+
animated,
|
|
1353
|
+
);
|
|
1354
|
+
const { from, to, progress } = morph;
|
|
1355
|
+
const animatedProps = useAnimatedProps(() => {
|
|
1356
|
+
const [x = 0, y = 0, width = 0, height = 0] = lerpVector(from.get(), to.get(), progress.get());
|
|
1357
|
+
return {
|
|
1358
|
+
d: barPath({ x, y, width, height, rounded, roundAll, value }, orientation, radius),
|
|
1359
|
+
};
|
|
1360
|
+
}, [from, to, progress, rounded, roundAll, value, orientation, radius]);
|
|
1361
|
+
|
|
1362
|
+
return <AnimatedPath animatedProps={animatedProps} fill={fill} opacity={opacity} />;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
function ValueLabels({
|
|
1366
|
+
animated,
|
|
1367
|
+
motionKey,
|
|
1368
|
+
children,
|
|
1369
|
+
}: {
|
|
1370
|
+
animated: boolean;
|
|
1371
|
+
motionKey: string;
|
|
1372
|
+
children: (opacity: number) => ReactNode;
|
|
1373
|
+
}) {
|
|
1374
|
+
const [shownKey, setShownKey] = useState(animated ? null : motionKey);
|
|
1375
|
+
|
|
1376
|
+
useEffect(() => {
|
|
1377
|
+
if (!animated) {
|
|
1378
|
+
setShownKey(motionKey);
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
const id = setTimeout(() => setShownKey(motionKey), CHART_MOTION_DURATION);
|
|
1382
|
+
return () => {
|
|
1383
|
+
clearTimeout(id);
|
|
1384
|
+
};
|
|
1385
|
+
}, [animated, motionKey]);
|
|
1386
|
+
|
|
1387
|
+
if (!animated) {
|
|
1388
|
+
return <>{children(1)}</>;
|
|
1389
|
+
}
|
|
1390
|
+
if (shownKey !== motionKey) {
|
|
1391
|
+
return null;
|
|
1392
|
+
}
|
|
1393
|
+
return <FadeInLabels key={motionKey}>{children}</FadeInLabels>;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
/** Group opacity on native SVG flashes a full-opacity frame; fade each text's fill instead. */
|
|
1397
|
+
function FadeInLabels({ children }: { children: (opacity: number) => ReactNode }) {
|
|
1398
|
+
const [opacity, setOpacity] = useState(0);
|
|
1399
|
+
|
|
1400
|
+
useEffect(() => {
|
|
1401
|
+
let frame = 0;
|
|
1402
|
+
const started = Date.now();
|
|
1403
|
+
const tick = () => {
|
|
1404
|
+
const t = Math.min(1, (Date.now() - started) / LABEL_FADE_DURATION);
|
|
1405
|
+
setOpacity(1 - (1 - t) ** 3);
|
|
1406
|
+
if (t < 1) {
|
|
1407
|
+
frame = requestAnimationFrame(tick);
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
frame = requestAnimationFrame(tick);
|
|
1411
|
+
return () => {
|
|
1412
|
+
cancelAnimationFrame(frame);
|
|
1413
|
+
};
|
|
1414
|
+
}, []);
|
|
1415
|
+
|
|
1416
|
+
return <>{children(opacity)}</>;
|
|
1417
|
+
}
|