@coinbase/cds-mobile-visualization 3.4.0-beta.22 → 3.4.0-beta.23

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.
Files changed (100) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dts/chart/CartesianChart.d.ts +39 -7
  3. package/dts/chart/CartesianChart.d.ts.map +1 -1
  4. package/dts/chart/Path.d.ts.map +1 -1
  5. package/dts/chart/area/Area.d.ts +7 -0
  6. package/dts/chart/area/Area.d.ts.map +1 -1
  7. package/dts/chart/area/AreaChart.d.ts +5 -5
  8. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  9. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  10. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  11. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  12. package/dts/chart/axis/Axis.d.ts +3 -1
  13. package/dts/chart/axis/Axis.d.ts.map +1 -1
  14. package/dts/chart/axis/XAxis.d.ts +6 -0
  15. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  16. package/dts/chart/axis/YAxis.d.ts +1 -0
  17. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  18. package/dts/chart/bar/Bar.d.ts +4 -2
  19. package/dts/chart/bar/Bar.d.ts.map +1 -1
  20. package/dts/chart/bar/BarChart.d.ts +49 -9
  21. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  22. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  23. package/dts/chart/bar/BarStack.d.ts +30 -9
  24. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  25. package/dts/chart/bar/BarStackGroup.d.ts +1 -1
  26. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  27. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  28. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  29. package/dts/chart/gradient/Gradient.d.ts +5 -0
  30. package/dts/chart/gradient/Gradient.d.ts.map +1 -1
  31. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  32. package/dts/chart/line/Line.d.ts +7 -0
  33. package/dts/chart/line/Line.d.ts.map +1 -1
  34. package/dts/chart/line/LineChart.d.ts +5 -5
  35. package/dts/chart/line/LineChart.d.ts.map +1 -1
  36. package/dts/chart/line/ReferenceLine.d.ts +1 -0
  37. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  38. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  39. package/dts/chart/point/Point.d.ts +7 -0
  40. package/dts/chart/point/Point.d.ts.map +1 -1
  41. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -1
  42. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +2 -1
  43. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -1
  44. package/dts/chart/scrubber/Scrubber.d.ts +8 -0
  45. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  46. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -1
  47. package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
  48. package/dts/chart/utils/axis.d.ts +20 -9
  49. package/dts/chart/utils/axis.d.ts.map +1 -1
  50. package/dts/chart/utils/bar.d.ts +4 -3
  51. package/dts/chart/utils/bar.d.ts.map +1 -1
  52. package/dts/chart/utils/chart.d.ts +13 -0
  53. package/dts/chart/utils/chart.d.ts.map +1 -1
  54. package/dts/chart/utils/context.d.ts +21 -6
  55. package/dts/chart/utils/context.d.ts.map +1 -1
  56. package/dts/chart/utils/gradient.d.ts +3 -1
  57. package/dts/chart/utils/gradient.d.ts.map +1 -1
  58. package/dts/chart/utils/path.d.ts +20 -0
  59. package/dts/chart/utils/path.d.ts.map +1 -1
  60. package/dts/chart/utils/point.d.ts +7 -0
  61. package/dts/chart/utils/point.d.ts.map +1 -1
  62. package/esm/chart/CartesianChart.js +107 -68
  63. package/esm/chart/Path.js +10 -7
  64. package/esm/chart/area/Area.js +19 -9
  65. package/esm/chart/area/AreaChart.js +11 -9
  66. package/esm/chart/area/DottedArea.js +11 -6
  67. package/esm/chart/area/GradientArea.js +11 -6
  68. package/esm/chart/area/SolidArea.js +3 -1
  69. package/esm/chart/area/__stories__/AreaChart.stories.js +30 -2
  70. package/esm/chart/axis/XAxis.js +14 -21
  71. package/esm/chart/axis/YAxis.js +4 -3
  72. package/esm/chart/bar/Bar.js +9 -5
  73. package/esm/chart/bar/BarChart.js +34 -31
  74. package/esm/chart/bar/BarPlot.js +7 -5
  75. package/esm/chart/bar/BarStack.js +176 -36
  76. package/esm/chart/bar/BarStackGroup.js +37 -27
  77. package/esm/chart/bar/DefaultBar.js +24 -8
  78. package/esm/chart/bar/DefaultBarStack.js +24 -10
  79. package/esm/chart/bar/__stories__/BarChart.stories.js +99 -3
  80. package/esm/chart/gradient/Gradient.js +2 -1
  81. package/esm/chart/line/DottedLine.js +3 -1
  82. package/esm/chart/line/Line.js +32 -19
  83. package/esm/chart/line/LineChart.js +9 -8
  84. package/esm/chart/line/SolidLine.js +3 -1
  85. package/esm/chart/line/__stories__/LineChart.stories.js +31 -0
  86. package/esm/chart/point/Point.js +2 -1
  87. package/esm/chart/scrubber/DefaultScrubberBeacon.js +1 -1
  88. package/esm/chart/scrubber/DefaultScrubberLabel.js +26 -10
  89. package/esm/chart/scrubber/Scrubber.js +47 -21
  90. package/esm/chart/scrubber/ScrubberBeaconGroup.js +24 -20
  91. package/esm/chart/scrubber/ScrubberProvider.js +29 -24
  92. package/esm/chart/scrubber/__stories__/Scrubber.stories.js +135 -1
  93. package/esm/chart/utils/axis.js +42 -14
  94. package/esm/chart/utils/bar.js +5 -4
  95. package/esm/chart/utils/chart.js +18 -5
  96. package/esm/chart/utils/context.js +7 -0
  97. package/esm/chart/utils/gradient.js +8 -4
  98. package/esm/chart/utils/path.js +90 -61
  99. package/esm/chart/utils/point.js +28 -18
  100. package/package.json +5 -5
@@ -1,4 +1,4 @@
1
- const _excluded = ["position", "showGrid", "requestedTickCount", "ticks", "tickLabelFormatter", "TickLabelComponent", "GridLineComponent", "LineComponent", "TickMarkLineComponent", "tickMarkLabelGap", "minTickLabelGap", "showTickMarks", "showLine", "tickMarkSize", "tickInterval", "tickMinStep", "tickMaxStep", "label", "labelGap", "height", "bandGridLinePlacement", "bandTickMarkPlacement"];
1
+ const _excluded = ["axisId", "position", "showGrid", "requestedTickCount", "ticks", "tickLabelFormatter", "TickLabelComponent", "GridLineComponent", "LineComponent", "TickMarkLineComponent", "tickMarkLabelGap", "minTickLabelGap", "showTickMarks", "showLine", "tickMarkSize", "tickInterval", "tickMinStep", "tickMaxStep", "label", "labelGap", "height", "bandGridLinePlacement", "bandTickMarkPlacement"];
2
2
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
3
3
  import { memo, useCallback, useEffect, useId, useMemo } from 'react';
4
4
  import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
@@ -15,6 +15,7 @@ const AXIS_HEIGHT = 32;
15
15
  const LABEL_SIZE = 20;
16
16
  export const XAxis = /*#__PURE__*/memo(_ref => {
17
17
  let {
18
+ axisId,
18
19
  position = 'bottom',
19
20
  showGrid,
20
21
  requestedTickCount,
@@ -44,14 +45,15 @@ export const XAxis = /*#__PURE__*/memo(_ref => {
44
45
  const {
45
46
  animate,
46
47
  drawingArea,
48
+ layout,
47
49
  getXScale,
48
50
  getXAxis,
49
51
  registerAxis,
50
52
  unregisterAxis,
51
53
  getAxisBounds
52
54
  } = useCartesianChartContext();
53
- const xScale = getXScale();
54
- const xAxis = getXAxis();
55
+ const xScale = getXScale(axisId);
56
+ const xAxis = getXAxis(axisId);
55
57
  const axisBounds = getAxisBounds(registrationId);
56
58
  useEffect(() => {
57
59
  registerAxis(registrationId, position, height);
@@ -62,15 +64,12 @@ export const XAxis = /*#__PURE__*/memo(_ref => {
62
64
  // If we have string labels and no custom formatter, use the labels
63
65
  const axisData = xAxis == null ? void 0 : xAxis.data;
64
66
  const hasStringLabels = axisData && Array.isArray(axisData) && typeof axisData[0] === 'string';
65
- let finalValue = value;
66
-
67
- // For band scales with string data, value is an index
68
- if (hasStringLabels && typeof value === 'number' && axisData[value] !== undefined) {
69
- finalValue = axisData[value];
67
+ if (hasStringLabels && !tickLabelFormatter && axisData[value] !== undefined) {
68
+ return axisData[value];
70
69
  }
71
70
 
72
- // Use the formatter (if provided) or the value itself
73
- return (_tickLabelFormatter = tickLabelFormatter == null ? void 0 : tickLabelFormatter(finalValue)) != null ? _tickLabelFormatter : finalValue;
71
+ // Otherwise passes raw index to formatter
72
+ return (_tickLabelFormatter = tickLabelFormatter == null ? void 0 : tickLabelFormatter(value)) != null ? _tickLabelFormatter : value;
74
73
  }, [xAxis == null ? void 0 : xAxis.data, tickLabelFormatter]);
75
74
  const ticksData = useMemo(() => {
76
75
  if (!xScale) return [];
@@ -89,27 +88,21 @@ export const XAxis = /*#__PURE__*/memo(_ref => {
89
88
  const domain = xScale.domain();
90
89
  categories = domain.map(String);
91
90
  }
92
- let possibleTickValues;
93
-
94
- // If we have discrete data, we can use the indices as possible tick values
95
- if (axisData && Array.isArray(axisData) && (typeof axisData[0] === 'string' || typeof axisData[0] === 'number' && isCategoricalScale(xScale))) {
96
- possibleTickValues = Array.from({
97
- length: axisData.length
98
- }, (_, i) => i);
99
- }
100
91
  return getAxisTicksData({
101
92
  scaleFunction: xScale,
102
93
  ticks,
103
- requestedTickCount,
94
+ requestedTickCount: requestedTickCount != null ? requestedTickCount : layout === 'horizontal' ? 5 : undefined,
104
95
  categories,
105
- possibleTickValues,
96
+ possibleTickValues: axisData && Array.isArray(axisData) && typeof axisData[0] === 'string' ? Array.from({
97
+ length: axisData.length
98
+ }, (_, i) => i) : undefined,
106
99
  tickInterval: tickInterval,
107
100
  options: {
108
101
  minStep: tickMinStep,
109
102
  maxStep: tickMaxStep
110
103
  }
111
104
  });
112
- }, [ticks, xScale, requestedTickCount, tickInterval, tickMinStep, tickMaxStep, xAxis == null ? void 0 : xAxis.data]);
105
+ }, [ticks, xScale, requestedTickCount, tickInterval, tickMinStep, tickMaxStep, xAxis == null ? void 0 : xAxis.data, layout]);
113
106
  const isBandScale = useMemo(() => {
114
107
  if (!xScale) return false;
115
108
  return isCategoricalScale(xScale);
@@ -18,7 +18,7 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
18
18
  axisId,
19
19
  position = 'right',
20
20
  showGrid,
21
- requestedTickCount = 5,
21
+ requestedTickCount,
22
22
  ticks,
23
23
  tickLabelFormatter,
24
24
  TickLabelComponent = DefaultAxisTickLabel,
@@ -43,6 +43,7 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
43
43
  const {
44
44
  animate,
45
45
  drawingArea,
46
+ layout,
46
47
  getYScale,
47
48
  getYAxis,
48
49
  registerAxis,
@@ -94,12 +95,12 @@ export const YAxis = /*#__PURE__*/memo(_ref => {
94
95
  return getAxisTicksData({
95
96
  scaleFunction: yScale,
96
97
  ticks,
97
- requestedTickCount: tickInterval !== undefined ? undefined : requestedTickCount != null ? requestedTickCount : 5,
98
+ requestedTickCount: tickInterval !== undefined ? undefined : requestedTickCount != null ? requestedTickCount : layout === 'horizontal' ? undefined : 5,
98
99
  categories,
99
100
  possibleTickValues: axisData && Array.isArray(axisData) && typeof axisData[0] === 'number' ? axisData : undefined,
100
101
  tickInterval: tickInterval
101
102
  });
102
- }, [ticks, yScale, requestedTickCount, tickInterval, yAxis == null ? void 0 : yAxis.data]);
103
+ }, [ticks, yScale, requestedTickCount, tickInterval, yAxis == null ? void 0 : yAxis.data, layout]);
103
104
  const isBandScale = useMemo(() => {
104
105
  if (!yScale) return false;
105
106
  return isCategoricalScale(yScale);
@@ -1,5 +1,6 @@
1
1
  import React, { memo, useMemo } from 'react';
2
2
  import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
3
+ import { useCartesianChartContext } from '../ChartProvider';
3
4
  import { getBarPath } from '../utils';
4
5
  import { DefaultBar } from './DefaultBar';
5
6
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -21,7 +22,7 @@ export const Bar = /*#__PURE__*/memo(_ref => {
21
22
  y,
22
23
  width,
23
24
  height,
24
- originY,
25
+ origin: originProp,
25
26
  dataX,
26
27
  dataY,
27
28
  seriesId,
@@ -37,14 +38,17 @@ export const Bar = /*#__PURE__*/memo(_ref => {
37
38
  transition
38
39
  } = _ref;
39
40
  const theme = useTheme();
41
+ const {
42
+ layout
43
+ } = useCartesianChartContext();
40
44
 
41
45
  // Use theme color as default if no fill is provided
42
46
  const effectiveFill = fill != null ? fill : theme.color.fgPrimary;
43
47
  const borderRadiusPixels = useMemo(() => borderRadius != null ? borderRadius : 0, [borderRadius]);
44
48
  const barPath = useMemo(() => {
45
- return getBarPath(x, y, width, height, borderRadiusPixels, roundTop, roundBottom);
46
- }, [x, y, width, height, borderRadiusPixels, roundTop, roundBottom]);
47
- const effectiveOriginY = originY != null ? originY : y + height;
49
+ return getBarPath(x, y, width, height, borderRadiusPixels, roundTop, roundBottom, layout);
50
+ }, [x, y, width, height, borderRadiusPixels, roundTop, roundBottom, layout]);
51
+ const effectiveOrigin = originProp != null ? originProp : layout === 'horizontal' ? x : y + height;
48
52
  if (!barPath) {
49
53
  return null;
50
54
  }
@@ -58,7 +62,7 @@ export const Bar = /*#__PURE__*/memo(_ref => {
58
62
  fill: effectiveFill,
59
63
  fillOpacity: fillOpacity,
60
64
  height: height,
61
- originY: effectiveOriginY,
65
+ origin: effectiveOrigin,
62
66
  roundBottom: roundBottom,
63
67
  roundTop: roundTop,
64
68
  seriesId: seriesId,
@@ -1,17 +1,17 @@
1
1
  const _excluded = ["series", "stacked", "showXAxis", "showYAxis", "xAxis", "yAxis", "inset", "children", "barPadding", "BarComponent", "fillOpacity", "stroke", "strokeWidth", "borderRadius", "roundBaseline", "BarStackComponent", "stackGap", "barMinSize", "stackMinSize", "transitions", "transition"],
2
- _excluded2 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range"],
2
+ _excluded2 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "id"],
3
3
  _excluded3 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "id"];
4
4
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
5
5
  function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
6
6
  import { forwardRef, memo, useMemo } from 'react';
7
7
  import { XAxis, YAxis } from '../axis';
8
8
  import { CartesianChart } from '../CartesianChart';
9
- import { defaultChartInset, defaultStackId, getChartInset } from '../utils';
9
+ import { defaultStackId } from '../utils';
10
10
  import { BarPlot } from './BarPlot';
11
11
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
13
13
  let {
14
- series,
14
+ series: seriesProp,
15
15
  stacked,
16
16
  showXAxis,
17
17
  showYAxis,
@@ -34,21 +34,19 @@ export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
34
34
  transition
35
35
  } = _ref,
36
36
  chartProps = _objectWithoutPropertiesLoose(_ref, _excluded);
37
- const calculatedInset = useMemo(() => getChartInset(inset, defaultChartInset), [inset]);
38
- const transformedSeries = useMemo(() => {
39
- if (!stacked || !series) return series;
40
- return series.map(s => {
37
+ const series = useMemo(() => {
38
+ if (!stacked || !seriesProp) return seriesProp;
39
+ return seriesProp.map(s => {
41
40
  var _s$stackId;
42
41
  return _extends({}, s, {
43
42
  stackId: (_s$stackId = s.stackId) != null ? _s$stackId : defaultStackId
44
43
  });
45
44
  });
46
- }, [series, stacked]);
47
-
48
- // Unlike other charts with custom props per series, we do not need to pick out
49
- // the props from each series that shouldn't be passed to CartesianChart
50
- const seriesToRender = transformedSeries != null ? transformedSeries : series;
51
- const seriesIds = seriesToRender == null ? void 0 : seriesToRender.map(s => s.id);
45
+ }, [seriesProp, stacked]);
46
+ const seriesIds = useMemo(() => series == null ? void 0 : series.map(s => s.id), [series]);
47
+ const isHorizontal = chartProps.layout === 'horizontal';
48
+ const defaultXScaleType = isHorizontal ? 'linear' : 'band';
49
+ const defaultYScaleType = isHorizontal ? 'band' : 'linear';
52
50
 
53
51
  // Split axis props into config props for Chart and visual props for axis components
54
52
  const _ref2 = xAxis || {},
@@ -58,7 +56,8 @@ export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
58
56
  categoryPadding: xCategoryPadding,
59
57
  domain: xDomain,
60
58
  domainLimit: xDomainLimit,
61
- range: xRange
59
+ range: xRange,
60
+ id: xAxisId
62
61
  } = _ref2,
63
62
  xAxisVisualProps = _objectWithoutPropertiesLoose(_ref2, _excluded2);
64
63
  const _ref3 = yAxis || {},
@@ -72,14 +71,6 @@ export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
72
71
  id: yAxisId
73
72
  } = _ref3,
74
73
  yAxisVisualProps = _objectWithoutPropertiesLoose(_ref3, _excluded3);
75
- const xAxisConfig = {
76
- scaleType: xScaleType != null ? xScaleType : 'band',
77
- data: xData,
78
- categoryPadding: xCategoryPadding,
79
- domain: xDomain,
80
- domainLimit: xDomainLimit,
81
- range: xRange
82
- };
83
74
  const hasNegativeValues = useMemo(() => {
84
75
  if (!series) return false;
85
76
  return series.some(s => {
@@ -87,25 +78,37 @@ export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
87
78
  return (_s$data = s.data) == null ? void 0 : _s$data.some(value => typeof value === 'number' && value < 0 || Array.isArray(value) && value.some(v => typeof v === 'number' && v < 0));
88
79
  });
89
80
  }, [series]);
81
+ const xAxisConfig = useMemo(() => ({
82
+ scaleType: xScaleType != null ? xScaleType : defaultXScaleType,
83
+ data: xData,
84
+ categoryPadding: xCategoryPadding,
85
+ domain: isHorizontal && !hasNegativeValues ? _extends({
86
+ min: 0
87
+ }, xDomain) : xDomain,
88
+ domainLimit: xDomainLimit,
89
+ range: xRange
90
+ }), [xScaleType, defaultXScaleType, xData, xCategoryPadding, isHorizontal, hasNegativeValues, xDomain, xDomainLimit, xRange]);
90
91
 
91
- // Set default min domain to 0 for area chart, but only if there are no negative values
92
- const yAxisConfig = {
93
- scaleType: yScaleType,
92
+ // Set default min domain to 0 for bar chart, but only if there are no negative values.
93
+ const yAxisConfig = useMemo(() => ({
94
+ scaleType: yScaleType != null ? yScaleType : defaultYScaleType,
94
95
  data: yData,
95
96
  categoryPadding: yCategoryPadding,
96
- domain: hasNegativeValues ? yDomain : _extends({
97
+ domain: !isHorizontal && !hasNegativeValues ? _extends({
97
98
  min: 0
98
- }, yDomain),
99
+ }, yDomain) : yDomain,
99
100
  domainLimit: yDomainLimit,
100
101
  range: yRange
101
- };
102
+ }), [yScaleType, defaultYScaleType, yData, yCategoryPadding, isHorizontal, hasNegativeValues, yDomain, yDomainLimit, yRange]);
102
103
  return /*#__PURE__*/_jsxs(CartesianChart, _extends({}, chartProps, {
103
104
  ref: ref,
104
- inset: calculatedInset,
105
- series: seriesToRender,
105
+ inset: inset,
106
+ series: series,
106
107
  xAxis: xAxisConfig,
107
108
  yAxis: yAxisConfig,
108
- children: [showXAxis && /*#__PURE__*/_jsx(XAxis, _extends({}, xAxisVisualProps)), showYAxis && /*#__PURE__*/_jsx(YAxis, _extends({
109
+ children: [showXAxis && /*#__PURE__*/_jsx(XAxis, _extends({
110
+ axisId: xAxisId
111
+ }, xAxisVisualProps)), showYAxis && /*#__PURE__*/_jsx(YAxis, _extends({
109
112
  axisId: yAxisId
110
113
  }, yAxisVisualProps)), /*#__PURE__*/_jsx(BarPlot, {
111
114
  BarComponent: BarComponent,
@@ -1,4 +1,4 @@
1
- import { memo, useId, useMemo } from 'react';
1
+ import { memo, useMemo } from 'react';
2
2
  import { Group, Skia } from '@shopify/react-native-skia';
3
3
  import { useCartesianChartContext } from '../ChartProvider';
4
4
  import { defaultAxisId } from '../utils';
@@ -31,7 +31,6 @@ export const BarPlot = /*#__PURE__*/memo(_ref => {
31
31
  series: allSeries,
32
32
  drawingArea
33
33
  } = useCartesianChartContext();
34
- const clipPathId = useId();
35
34
  const targetSeries = useMemo(() => {
36
35
  // Then filter by seriesIds if provided
37
36
  if (seriesIds !== undefined) {
@@ -42,16 +41,18 @@ export const BarPlot = /*#__PURE__*/memo(_ref => {
42
41
  const stackGroups = useMemo(() => {
43
42
  const groups = new Map();
44
43
 
45
- // Group series into stacks based on stackId + yAxisId combination
44
+ // Group series into stacks based on stackId + axis ID combination
46
45
  targetSeries.forEach(series => {
47
- var _series$yAxisId;
46
+ var _series$xAxisId, _series$yAxisId;
47
+ const xAxisId = (_series$xAxisId = series.xAxisId) != null ? _series$xAxisId : defaultAxisId;
48
48
  const yAxisId = (_series$yAxisId = series.yAxisId) != null ? _series$yAxisId : defaultAxisId;
49
49
  const stackId = series.stackId || "individual-" + series.id;
50
- const stackKey = stackId + ":" + yAxisId;
50
+ const stackKey = stackId + ":" + xAxisId + ":" + yAxisId;
51
51
  if (!groups.has(stackKey)) {
52
52
  groups.set(stackKey, {
53
53
  stackId: stackKey,
54
54
  series: [],
55
+ xAxisId: series.xAxisId,
55
56
  yAxisId: series.yAxisId
56
57
  });
57
58
  }
@@ -98,6 +99,7 @@ export const BarPlot = /*#__PURE__*/memo(_ref => {
98
99
  totalStacks: stackGroups.length,
99
100
  transition: transition,
100
101
  transitions: transitions,
102
+ xAxisId: group.xAxisId,
101
103
  yAxisId: group.yAxisId
102
104
  }, group.stackId))
103
105
  });
@@ -21,10 +21,13 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
21
21
  let {
22
22
  series,
23
23
  categoryIndex,
24
- x,
25
- width,
26
- yScale,
24
+ indexPos,
25
+ thickness,
26
+ indexScale,
27
+ valueScale,
27
28
  rect,
29
+ xAxisId,
30
+ yAxisId,
28
31
  BarComponent: defaultBarComponent,
29
32
  fillOpacity: defaultFillOpacity,
30
33
  stroke: defaultStroke,
@@ -40,24 +43,30 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
40
43
  } = _ref;
41
44
  const theme = useTheme();
42
45
  const {
46
+ layout,
43
47
  getSeriesData,
44
48
  getXAxis,
45
- getXScale
49
+ getYAxis
46
50
  } = useCartesianChartContext();
47
- const xAxis = getXAxis();
48
- const xScale = getXScale();
51
+ const xAxis = getXAxis(xAxisId);
52
+ const yAxis = getYAxis(yAxisId);
53
+ const barsGrowVertically = layout !== 'horizontal';
49
54
  const baseline = useMemo(() => {
50
- var _yScale;
51
- const domain = yScale.domain();
55
+ var _valueScale;
56
+ const domain = valueScale.domain();
52
57
  const [domainMin, domainMax] = domain;
53
58
  const baselineValue = domainMin >= 0 ? domainMin : domainMax <= 0 ? domainMax : 0;
54
- const baseline = (_yScale = yScale(baselineValue)) != null ? _yScale : rect.y + rect.height;
55
- return Math.max(rect.y, Math.min(baseline, rect.y + rect.height));
56
- }, [rect.height, rect.y, yScale]);
59
+ const fallback = barsGrowVertically ? rect.y + rect.height : rect.x;
60
+ const baselinePos = (_valueScale = valueScale(baselineValue)) != null ? _valueScale : fallback;
61
+ if (barsGrowVertically) {
62
+ return Math.max(rect.y, Math.min(baselinePos, rect.y + rect.height));
63
+ }
64
+ return Math.max(rect.x, Math.min(baselinePos, rect.x + rect.width));
65
+ }, [rect, valueScale, barsGrowVertically]);
57
66
  const seriesGradients = useMemo(() => {
58
67
  return series.map(s => {
59
- if (!s.gradient || !xScale || !yScale) return;
60
- const gradientScale = s.gradient.axis === 'x' ? xScale : yScale;
68
+ if (!s.gradient) return;
69
+ const gradientScale = s.gradient.axis === 'x' ? barsGrowVertically ? indexScale : valueScale : barsGrowVertically ? valueScale : indexScale;
61
70
  const serializableScale = convertToSerializableScale(gradientScale);
62
71
  if (!serializableScale) return;
63
72
  const domain = {
@@ -72,14 +81,140 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
72
81
  stops
73
82
  };
74
83
  });
75
- }, [series, xScale, yScale]);
84
+ }, [series, indexScale, valueScale, barsGrowVertically]);
76
85
 
77
86
  // Calculate bars for this specific category
78
87
  const {
79
88
  bars,
80
89
  stackRect
81
90
  } = useMemo(() => {
91
+ const x = indexPos;
92
+ const width = thickness;
93
+ const yScale = valueScale;
82
94
  let allBars = [];
95
+ if (!barsGrowVertically) {
96
+ let minX = Infinity;
97
+ let maxX = -Infinity;
98
+ series.forEach(s => {
99
+ var _yScale, _yScale2;
100
+ const data = getSeriesData(s.id);
101
+ if (!data) return;
102
+ const value = data[categoryIndex];
103
+ if (value === null || value === undefined) return;
104
+ const originalData = s.data;
105
+ const originalValue = originalData == null ? void 0 : originalData[categoryIndex];
106
+ const shouldApplyGap = !Array.isArray(originalValue);
107
+ const [bottom, top] = value.sort((a, b) => a - b);
108
+ const edgeBottom = (_yScale = yScale(bottom)) != null ? _yScale : baseline;
109
+ const edgeTop = (_yScale2 = yScale(top)) != null ? _yScale2 : baseline;
110
+ const length = Math.abs(edgeBottom - edgeTop);
111
+ const barX = Math.min(edgeBottom, edgeTop);
112
+ if (length <= 0) return;
113
+ minX = Math.min(minX, barX);
114
+ maxX = Math.max(maxX, barX + length);
115
+ let barFill = s.color || theme.color.fgPrimary;
116
+ const seriesGradientConfig = seriesGradients.find(g => (g == null ? void 0 : g.seriesId) === s.id);
117
+ if (seriesGradientConfig && originalValue !== null && originalValue !== undefined) {
118
+ var _seriesGradientConfig;
119
+ const axis = (_seriesGradientConfig = seriesGradientConfig.gradient.axis) != null ? _seriesGradientConfig : 'y';
120
+ const evalValue = axis === 'x' ? Array.isArray(originalValue) ? originalValue[1] : originalValue : categoryIndex;
121
+ const evaluatedColor = evaluateGradientAtValue(seriesGradientConfig.stops, evalValue, seriesGradientConfig.scale);
122
+ if (evaluatedColor) {
123
+ barFill = evaluatedColor;
124
+ }
125
+ }
126
+ const roundTop = roundBaseline || Math.abs(edgeTop - baseline) >= EPSILON;
127
+ const roundBottom = roundBaseline || Math.abs(edgeBottom - baseline) >= EPSILON;
128
+ allBars.push({
129
+ seriesId: s.id,
130
+ x: barX,
131
+ y: x,
132
+ width: length,
133
+ height: width,
134
+ dataY: value,
135
+ fill: barFill,
136
+ roundTop,
137
+ roundBottom,
138
+ BarComponent: s.BarComponent,
139
+ shouldApplyGap
140
+ });
141
+ });
142
+ if (stackGap && allBars.length > 1) {
143
+ const barsAboveBaseline = allBars.filter(bar => {
144
+ const [bottom, top] = bar.dataY.sort((a, b) => a - b);
145
+ return bottom >= 0 && top !== bottom && bar.shouldApplyGap;
146
+ });
147
+ const barsBelowBaseline = allBars.filter(bar => {
148
+ const [bottom, top] = bar.dataY.sort((a, b) => a - b);
149
+ return bottom <= 0 && bottom !== top && bar.shouldApplyGap;
150
+ });
151
+ if (barsAboveBaseline.length > 1) {
152
+ const totalGapSpace = stackGap * (barsAboveBaseline.length - 1);
153
+ const totalDataLength = barsAboveBaseline.reduce((sum, bar) => sum + bar.width, 0);
154
+ const lengthReduction = totalGapSpace / totalDataLength;
155
+ const sortedBars = barsAboveBaseline.sort((a, b) => a.x - b.x);
156
+ let currentEdge = baseline;
157
+ sortedBars.forEach((bar, index) => {
158
+ const newLength = bar.width * (1 - lengthReduction);
159
+ const newX = currentEdge;
160
+ currentEdge = newX + newLength + (index < sortedBars.length - 1 ? stackGap : 0);
161
+ const barIndex = allBars.findIndex(b => b.seriesId === bar.seriesId);
162
+ if (barIndex !== -1) {
163
+ allBars[barIndex] = _extends({}, allBars[barIndex], {
164
+ width: newLength,
165
+ x: newX
166
+ });
167
+ }
168
+ });
169
+ }
170
+ if (barsBelowBaseline.length > 1) {
171
+ const totalGapSpace = stackGap * (barsBelowBaseline.length - 1);
172
+ const totalDataLength = barsBelowBaseline.reduce((sum, bar) => sum + bar.width, 0);
173
+ const lengthReduction = totalGapSpace / totalDataLength;
174
+ const sortedBars = barsBelowBaseline.sort((a, b) => b.x - a.x);
175
+ let currentEdge = baseline;
176
+ sortedBars.forEach((bar, index) => {
177
+ const newLength = bar.width * (1 - lengthReduction);
178
+ const newX = currentEdge - newLength;
179
+ currentEdge = newX - (index < sortedBars.length - 1 ? stackGap : 0);
180
+ const barIndex = allBars.findIndex(b => b.seriesId === bar.seriesId);
181
+ if (barIndex !== -1) {
182
+ allBars[barIndex] = _extends({}, allBars[barIndex], {
183
+ width: newLength,
184
+ x: newX
185
+ });
186
+ }
187
+ });
188
+ }
189
+ if (allBars.length > 0) {
190
+ minX = Math.min(...allBars.map(bar => bar.x));
191
+ maxX = Math.max(...allBars.map(bar => bar.x + bar.width));
192
+ }
193
+ }
194
+
195
+ // Horizontal border radius logic: left-to-right sorting.
196
+ const sortedBars = [...allBars].sort((a, b) => a.x - b.x);
197
+ const roundedBars = sortedBars.map((bar, index) => {
198
+ const barBefore = index > 0 ? sortedBars[index - 1] : null;
199
+ const barAfter = index < sortedBars.length - 1 ? sortedBars[index + 1] : null;
200
+ const shouldRoundLower = index === 0 || bar.shouldApplyGap && stackGap || !bar.shouldApplyGap && barAfter && barAfter.x + barAfter.width !== bar.x;
201
+ const shouldRoundHigher = index === sortedBars.length - 1 || bar.shouldApplyGap && stackGap || !bar.shouldApplyGap && barBefore && barBefore.x !== bar.x + bar.width;
202
+ return _extends({}, bar, {
203
+ roundTop: Boolean(bar.roundTop && shouldRoundHigher),
204
+ roundBottom: Boolean(bar.roundBottom && shouldRoundLower)
205
+ });
206
+ });
207
+ const stackBounds = {
208
+ x: minX === Infinity ? baseline : minX,
209
+ y: x,
210
+ width: maxX === -Infinity ? 0 : maxX - minX,
211
+ height: width
212
+ };
213
+ return {
214
+ bars: roundedBars,
215
+ stackRect: stackBounds
216
+ };
217
+ }
83
218
 
84
219
  // Track how many bars we've stacked in each direction for gap calculation
85
220
  let positiveBarCount = 0;
@@ -91,7 +226,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
91
226
 
92
227
  // Process each series in the stack
93
228
  series.forEach(s => {
94
- var _yScale2, _yScale3;
229
+ var _yScale3, _yScale4;
95
230
  const data = getSeriesData(s.id);
96
231
  if (!data) return;
97
232
  const value = data[categoryIndex];
@@ -105,8 +240,8 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
105
240
  const [bottom, top] = value.sort((a, b) => a - b);
106
241
  const isAboveBaseline = bottom >= 0 && top !== bottom;
107
242
  const isBelowBaseline = bottom <= 0 && bottom !== top;
108
- const barBottom = (_yScale2 = yScale(bottom)) != null ? _yScale2 : baseline;
109
- const barTop = (_yScale3 = yScale(top)) != null ? _yScale3 : baseline;
243
+ const barBottom = (_yScale3 = yScale(bottom)) != null ? _yScale3 : baseline;
244
+ const barTop = (_yScale4 = yScale(top)) != null ? _yScale4 : baseline;
110
245
 
111
246
  // Track bar counts for later gap calculations
112
247
  if (shouldApplyGap) {
@@ -136,8 +271,8 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
136
271
  // Evaluate gradient if provided (using precomputed stops)
137
272
  const seriesGradientConfig = seriesGradients.find(g => (g == null ? void 0 : g.seriesId) === s.id);
138
273
  if (seriesGradientConfig) {
139
- var _seriesGradientConfig;
140
- const axis = (_seriesGradientConfig = seriesGradientConfig.gradient.axis) != null ? _seriesGradientConfig : 'y';
274
+ var _seriesGradientConfig2;
275
+ const axis = (_seriesGradientConfig2 = seriesGradientConfig.gradient.axis) != null ? _seriesGradientConfig2 : 'y';
141
276
  // For x-axis gradient, use the categoryIndex
142
277
  // For y-axis gradient, use the actual data value
143
278
  const dataValue = axis === 'x' ? categoryIndex : top;
@@ -243,14 +378,14 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
243
378
  // First, expand bars that need it and track the expansion
244
379
  const expandedBars = allBars.map((bar, index) => {
245
380
  if (bar.height < barMinSize) {
246
- var _yScale4, _yScale5, _yScale6, _yScale7;
381
+ var _yScale5, _yScale6, _yScale7, _yScale8;
247
382
  const heightIncrease = barMinSize - bar.height;
248
383
  const [bottom, top] = bar.dataY.sort((a, b) => a - b);
249
384
 
250
385
  // Determine how to expand the bar
251
386
  let newBottom = bottom;
252
387
  let newTop = top;
253
- const scaleUnit = Math.abs(((_yScale4 = yScale(1)) != null ? _yScale4 : 0) - ((_yScale5 = yScale(0)) != null ? _yScale5 : 0));
388
+ const scaleUnit = Math.abs(((_yScale5 = yScale(1)) != null ? _yScale5 : 0) - ((_yScale6 = yScale(0)) != null ? _yScale6 : 0));
254
389
  if (bottom === 0) {
255
390
  // Expand away from baseline (upward for positive)
256
391
  newTop = top + heightIncrease / scaleUnit;
@@ -265,8 +400,8 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
265
400
  }
266
401
 
267
402
  // Recalculate bar position with new data values
268
- const newBarBottom = (_yScale6 = yScale(newBottom)) != null ? _yScale6 : baseline;
269
- const newBarTop = (_yScale7 = yScale(newTop)) != null ? _yScale7 : baseline;
403
+ const newBarBottom = (_yScale7 = yScale(newBottom)) != null ? _yScale7 : baseline;
404
+ const newBarTop = (_yScale8 = yScale(newTop)) != null ? _yScale8 : baseline;
270
405
  const newHeight = Math.abs(newBarBottom - newBarTop);
271
406
  const newY = Math.min(newBarBottom, newBarTop);
272
407
  return _extends({}, bar, {
@@ -383,7 +518,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
383
518
  // Apply stackMinSize constraints
384
519
  if (stackMinSize) {
385
520
  if (allBars.length === 1 && stackBounds.height < stackMinSize) {
386
- var _yScale8, _yScale9, _yScale0, _yScale1;
521
+ var _yScale9, _yScale0, _yScale1, _yScale10;
387
522
  // For single bars (non-stacked), treat stackMinSize like barMinSize
388
523
 
389
524
  const bar = allBars[0];
@@ -393,7 +528,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
393
528
  // Determine how to expand the bar (same logic as barMinSize)
394
529
  let newBottom = bottom;
395
530
  let newTop = top;
396
- const scaleUnit = Math.abs(((_yScale8 = yScale(1)) != null ? _yScale8 : 0) - ((_yScale9 = yScale(0)) != null ? _yScale9 : 0));
531
+ const scaleUnit = Math.abs(((_yScale9 = yScale(1)) != null ? _yScale9 : 0) - ((_yScale0 = yScale(0)) != null ? _yScale0 : 0));
397
532
  if (bottom === 0) {
398
533
  // Expand away from baseline (upward for positive)
399
534
  newTop = top + heightIncrease / scaleUnit;
@@ -408,8 +543,8 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
408
543
  }
409
544
 
410
545
  // Recalculate bar position with new data values
411
- const newBarBottom = (_yScale0 = yScale(newBottom)) != null ? _yScale0 : baseline;
412
- const newBarTop = (_yScale1 = yScale(newTop)) != null ? _yScale1 : baseline;
546
+ const newBarBottom = (_yScale1 = yScale(newBottom)) != null ? _yScale1 : baseline;
547
+ const newBarTop = (_yScale10 = yScale(newTop)) != null ? _yScale10 : baseline;
413
548
  const newHeight = Math.abs(newBarBottom - newBarTop);
414
549
  const newY = Math.min(newBarBottom, newBarTop);
415
550
  allBars[0] = _extends({}, bar, {
@@ -517,18 +652,19 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
517
652
  bars: allBars,
518
653
  stackRect: stackBounds
519
654
  };
520
- }, [series, x, width, getSeriesData, categoryIndex, roundBaseline, baseline, stackGap, barMinSize, stackMinSize, yScale, seriesGradients, theme.color.fgPrimary]);
521
- const xData = xAxis != null && xAxis.data && Array.isArray(xAxis.data) && typeof xAxis.data[0] === 'number' ? xAxis.data : undefined;
522
- const dataX = xData ? xData[categoryIndex] : categoryIndex;
655
+ }, [series, indexPos, thickness, getSeriesData, categoryIndex, roundBaseline, baseline, stackGap, barMinSize, stackMinSize, valueScale, seriesGradients, theme.color.fgPrimary, barsGrowVertically]);
656
+ const categoryAxis = barsGrowVertically ? xAxis : yAxis;
657
+ const categoryData = categoryAxis != null && categoryAxis.data && Array.isArray(categoryAxis.data) && typeof categoryAxis.data[0] === 'number' ? categoryAxis.data : undefined;
658
+ const categoryValue = categoryData ? categoryData[categoryIndex] : categoryIndex;
523
659
  const barElements = bars.map((bar, index) => /*#__PURE__*/_jsx(Bar, {
524
660
  BarComponent: bar.BarComponent || defaultBarComponent,
525
661
  borderRadius: borderRadius,
526
- dataX: dataX,
527
- dataY: bar.dataY,
662
+ dataX: barsGrowVertically ? categoryValue : bar.dataY,
663
+ dataY: barsGrowVertically ? bar.dataY : categoryValue,
528
664
  fill: bar.fill,
529
665
  fillOpacity: defaultFillOpacity,
530
666
  height: bar.height,
531
- originY: baseline,
667
+ origin: baseline,
532
668
  roundBottom: bar.roundBottom,
533
669
  roundTop: bar.roundTop,
534
670
  seriesId: bar.seriesId,
@@ -541,9 +677,13 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
541
677
  y: bar.y
542
678
  }, bar.seriesId + "-" + categoryIndex + "-" + index));
543
679
 
544
- // Check if the bar should be rounded based on the baseline, with an epsilon to handle floating-point rounding
545
- const stackRoundBottom = roundBaseline || Math.abs(stackRect.y + stackRect.height - baseline) >= EPSILON;
546
- const stackRoundTop = roundBaseline || Math.abs(stackRect.y - baseline) >= EPSILON;
680
+ // Check if the stack should be rounded based on baseline, across both orientations.
681
+ const edge = barsGrowVertically ? stackRect.y : stackRect.x;
682
+ const size = barsGrowVertically ? stackRect.height : stackRect.width;
683
+ const stackRoundLower = roundBaseline || Math.abs(edge - baseline) >= EPSILON;
684
+ const stackRoundHigher = roundBaseline || Math.abs(edge + size - baseline) >= EPSILON;
685
+ const stackRoundTop = barsGrowVertically ? stackRoundLower : stackRoundHigher;
686
+ const stackRoundBottom = barsGrowVertically ? stackRoundHigher : stackRoundLower;
547
687
  return /*#__PURE__*/_jsx(BarStackComponent, {
548
688
  borderRadius: borderRadius,
549
689
  categoryIndex: categoryIndex,