@coinbase/cds-mobile-visualization 3.6.2 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dts/chart/CartesianChart.d.ts +4 -8
  3. package/dts/chart/CartesianChart.d.ts.map +1 -1
  4. package/dts/chart/area/Area.d.ts +3 -0
  5. package/dts/chart/area/Area.d.ts.map +1 -1
  6. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  7. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  8. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  9. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  10. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  11. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  12. package/dts/chart/gradient/Gradient.d.ts +14 -3
  13. package/dts/chart/gradient/Gradient.d.ts.map +1 -1
  14. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  15. package/dts/chart/line/Line.d.ts +3 -0
  16. package/dts/chart/line/Line.d.ts.map +1 -1
  17. package/dts/chart/line/LineChart.d.ts.map +1 -1
  18. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  19. package/dts/chart/utils/axis.d.ts +18 -8
  20. package/dts/chart/utils/axis.d.ts.map +1 -1
  21. package/dts/chart/utils/bar.d.ts +17 -7
  22. package/dts/chart/utils/bar.d.ts.map +1 -1
  23. package/dts/chart/utils/chart.d.ts +9 -0
  24. package/dts/chart/utils/chart.d.ts.map +1 -1
  25. package/dts/chart/utils/context.d.ts +3 -3
  26. package/dts/chart/utils/context.d.ts.map +1 -1
  27. package/dts/chart/utils/gradient.d.ts +14 -4
  28. package/dts/chart/utils/gradient.d.ts.map +1 -1
  29. package/esm/chart/CartesianChart.js +6 -4
  30. package/esm/chart/__stories__/ChartTransitions.stories.js +68 -0
  31. package/esm/chart/area/Area.js +0 -2
  32. package/esm/chart/area/AreaChart.js +15 -19
  33. package/esm/chart/area/DottedArea.js +6 -4
  34. package/esm/chart/area/GradientArea.js +6 -4
  35. package/esm/chart/area/SolidArea.js +3 -0
  36. package/esm/chart/area/__stories__/AreaChart.stories.js +189 -3
  37. package/esm/chart/bar/BarChart.js +14 -22
  38. package/esm/chart/bar/BarStack.js +15 -10
  39. package/esm/chart/bar/__stories__/BarChart.stories.js +84 -2
  40. package/esm/chart/gradient/Gradient.js +119 -26
  41. package/esm/chart/line/DottedLine.js +3 -0
  42. package/esm/chart/line/Line.js +1 -3
  43. package/esm/chart/line/LineChart.js +8 -4
  44. package/esm/chart/line/SolidLine.js +3 -0
  45. package/esm/chart/utils/axis.js +32 -4
  46. package/esm/chart/utils/bar.js +129 -76
  47. package/esm/chart/utils/chart.js +53 -21
  48. package/esm/chart/utils/gradient.js +15 -5
  49. package/package.json +1 -1
@@ -13,7 +13,6 @@ export const Area = /*#__PURE__*/memo(_ref => {
13
13
  AreaComponent: AreaComponentProp,
14
14
  fill: fillProp,
15
15
  fillOpacity = 1,
16
- baseline,
17
16
  connectNulls,
18
17
  gradient: gradientProp,
19
18
  transitions,
@@ -74,7 +73,6 @@ export const Area = /*#__PURE__*/memo(_ref => {
74
73
  if (!xScale || !yScale || !sourceData || !area) return;
75
74
  return /*#__PURE__*/_jsx(AreaComponent, {
76
75
  animate: animate,
77
- baseline: baseline,
78
76
  d: area,
79
77
  fill: fill,
80
78
  fillOpacity: fillOpacity,
@@ -1,6 +1,6 @@
1
1
  const _excluded = ["series", "stacked", "AreaComponent", "curve", "fillOpacity", "type", "connectNulls", "transition", "transitions", "LineComponent", "strokeWidth", "showXAxis", "showYAxis", "showLines", "lineType", "xAxis", "yAxis", "inset", "children"],
2
- _excluded2 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "id"],
3
- _excluded3 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "id"],
2
+ _excluded2 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "baseline", "id"],
3
+ _excluded3 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "baseline", "id"],
4
4
  _excluded4 = ["id", "data", "label", "color", "xAxisId", "yAxisId", "opacity", "LineComponent", "stackId"],
5
5
  _excluded5 = ["id", "data", "label", "color", "xAxisId", "yAxisId", "fill", "fillOpacity", "stackId", "type"];
6
6
  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); }
@@ -9,7 +9,7 @@ import { forwardRef, memo, useMemo } from 'react';
9
9
  import { XAxis, YAxis } from '../axis';
10
10
  import { CartesianChart } from '../CartesianChart';
11
11
  import { Line } from '../line/Line';
12
- import { defaultStackId } from '../utils';
12
+ import { defaultStackId, withBaselineDomain } from '../utils';
13
13
  import { Area } from './Area';
14
14
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
15
  export const AreaChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
@@ -45,7 +45,8 @@ export const AreaChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =
45
45
  gradient: s.gradient,
46
46
  xAxisId: s.xAxisId,
47
47
  yAxisId: s.yAxisId,
48
- stackId: s.stackId
48
+ stackId: s.stackId,
49
+ legendShape: s.legendShape
49
50
  }));
50
51
  }, [series]);
51
52
  const transformedSeries = useMemo(() => {
@@ -68,6 +69,7 @@ export const AreaChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =
68
69
  domain: xDomain,
69
70
  domainLimit: xDomainLimit,
70
71
  range: xRange,
72
+ baseline: xBaseline,
71
73
  id: xAxisId
72
74
  } = _ref2,
73
75
  xAxisVisualProps = _objectWithoutPropertiesLoose(_ref2, _excluded2);
@@ -79,35 +81,29 @@ export const AreaChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =
79
81
  domain: yDomain,
80
82
  domainLimit: yDomainLimit,
81
83
  range: yRange,
84
+ baseline: yBaseline,
82
85
  id: yAxisId
83
86
  } = _ref3,
84
87
  yAxisVisualProps = _objectWithoutPropertiesLoose(_ref3, _excluded3);
88
+ const isHorizontalLayout = chartProps.layout === 'horizontal';
89
+ const valueAxisBaseline = isHorizontalLayout ? xBaseline : yBaseline;
85
90
  const xAxisConfig = {
86
91
  scaleType: xScaleType,
87
92
  data: xData,
88
93
  categoryPadding: xCategoryPadding,
89
- domain: xDomain,
94
+ domain: isHorizontalLayout ? withBaselineDomain(xDomain, valueAxisBaseline) : xDomain,
90
95
  domainLimit: xDomainLimit,
91
- range: xRange
96
+ range: xRange,
97
+ baseline: xBaseline
92
98
  };
93
- const hasNegativeValues = useMemo(() => {
94
- if (!series) return false;
95
- return series.some(s => {
96
- var _s$data;
97
- 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));
98
- });
99
- }, [series]);
100
-
101
- // Set default min domain to 0 for area chart, but only if there are no negative values
102
99
  const yAxisConfig = {
103
100
  scaleType: yScaleType,
104
101
  data: yData,
105
102
  categoryPadding: yCategoryPadding,
106
- domain: hasNegativeValues ? yDomain : _extends({
107
- min: 0
108
- }, yDomain),
103
+ domain: !isHorizontalLayout ? withBaselineDomain(yDomain, valueAxisBaseline) : yDomain,
109
104
  domainLimit: yDomainLimit,
110
- range: yRange
105
+ range: yRange,
106
+ baseline: yBaseline
111
107
  };
112
108
  return /*#__PURE__*/_jsxs(CartesianChart, _extends({}, chartProps, {
113
109
  ref: ref,
@@ -1,4 +1,4 @@
1
- const _excluded = ["d", "fill", "patternSize", "dotSize", "peakOpacity", "baselineOpacity", "baseline", "xAxisId", "yAxisId", "gradient", "animate", "transitions", "transition"];
1
+ const _excluded = ["d", "fill", "patternSize", "dotSize", "peakOpacity", "baselineOpacity", "xAxisId", "yAxisId", "gradient", "animate", "transitions", "transition"];
2
2
  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); }
3
3
  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; }
4
4
  import { memo, useMemo } from 'react';
@@ -17,6 +17,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
17
17
  * on the fill color and peak/baseline opacities.
18
18
  */
19
19
  export const DottedArea = /*#__PURE__*/memo(_ref => {
20
+ var _transitions$update;
20
21
  let {
21
22
  d,
22
23
  fill: fillProp,
@@ -24,7 +25,6 @@ export const DottedArea = /*#__PURE__*/memo(_ref => {
24
25
  dotSize = 1,
25
26
  peakOpacity = 1,
26
27
  baselineOpacity = 0,
27
- baseline,
28
28
  xAxisId,
29
29
  yAxisId,
30
30
  gradient: gradientProp,
@@ -66,9 +66,9 @@ export const DottedArea = /*#__PURE__*/memo(_ref => {
66
66
  const gradient = useMemo(() => {
67
67
  if (gradientProp) return gradientProp;
68
68
  if (!valueAxisConfig) return;
69
- const baselineValue = getBaseline(valueAxisConfig.domain, baseline);
69
+ const baselineValue = getBaseline(valueAxisConfig.domain, valueAxisConfig.baseline);
70
70
  return createGradient(valueAxisConfig.domain, baselineValue, fill, peakOpacity, baselineOpacity, gradientAxis);
71
- }, [gradientProp, valueAxisConfig, fill, baseline, peakOpacity, baselineOpacity, gradientAxis]);
71
+ }, [gradientProp, valueAxisConfig, fill, peakOpacity, baselineOpacity, gradientAxis]);
72
72
 
73
73
  // Update transition is used for clip path, we skip update animation on Path itself
74
74
  return /*#__PURE__*/_jsx(Group, {
@@ -81,7 +81,9 @@ export const DottedArea = /*#__PURE__*/memo(_ref => {
81
81
  transitions: transitions
82
82
  }, pathProps, {
83
83
  children: gradient && /*#__PURE__*/_jsx(Gradient, {
84
+ animate: shouldAnimate,
84
85
  gradient: gradient,
86
+ transition: (_transitions$update = transitions == null ? void 0 : transitions.update) != null ? _transitions$update : transition,
85
87
  xAxisId: xAxisId,
86
88
  yAxisId: yAxisId
87
89
  })
@@ -1,4 +1,4 @@
1
- const _excluded = ["d", "fill", "fillOpacity", "gradient", "peakOpacity", "baselineOpacity", "baseline", "xAxisId", "yAxisId", "animate", "transitions", "transition"];
1
+ const _excluded = ["d", "fill", "fillOpacity", "gradient", "peakOpacity", "baselineOpacity", "xAxisId", "yAxisId", "animate", "transitions", "transition"];
2
2
  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); }
3
3
  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; }
4
4
  import { memo, useMemo } from 'react';
@@ -14,6 +14,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
14
14
  * on the fill color and peak/baseline opacities.
15
15
  */
16
16
  export const GradientArea = /*#__PURE__*/memo(_ref => {
17
+ var _transitions$update;
17
18
  let {
18
19
  d,
19
20
  fill: fillProp,
@@ -21,7 +22,6 @@ export const GradientArea = /*#__PURE__*/memo(_ref => {
21
22
  gradient: gradientProp,
22
23
  peakOpacity = 0.3,
23
24
  baselineOpacity = 0,
24
- baseline,
25
25
  xAxisId,
26
26
  yAxisId,
27
27
  animate,
@@ -41,9 +41,9 @@ export const GradientArea = /*#__PURE__*/memo(_ref => {
41
41
  const gradient = useMemo(() => {
42
42
  if (gradientProp) return gradientProp;
43
43
  if (!valueAxisConfig) return;
44
- const baselineValue = getBaseline(valueAxisConfig.domain, baseline);
44
+ const baselineValue = getBaseline(valueAxisConfig.domain, valueAxisConfig.baseline);
45
45
  return createGradient(valueAxisConfig.domain, baselineValue, fill, peakOpacity, baselineOpacity, gradientAxis);
46
- }, [gradientProp, valueAxisConfig, fill, baseline, peakOpacity, baselineOpacity, gradientAxis]);
46
+ }, [gradientProp, valueAxisConfig, fill, peakOpacity, baselineOpacity, gradientAxis]);
47
47
  return /*#__PURE__*/_jsx(Path, _extends({
48
48
  animate: animate,
49
49
  d: d,
@@ -53,7 +53,9 @@ export const GradientArea = /*#__PURE__*/memo(_ref => {
53
53
  transitions: transitions
54
54
  }, pathProps, {
55
55
  children: gradient && /*#__PURE__*/_jsx(Gradient, {
56
+ animate: animate,
56
57
  gradient: gradient,
58
+ transition: (_transitions$update = transitions == null ? void 0 : transitions.update) != null ? _transitions$update : transition,
57
59
  xAxisId: xAxisId,
58
60
  yAxisId: yAxisId
59
61
  })
@@ -12,6 +12,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
12
12
  * Otherwise, renders with solid fill.
13
13
  */
14
14
  export const SolidArea = /*#__PURE__*/memo(_ref => {
15
+ var _transitions$update;
15
16
  let {
16
17
  d,
17
18
  fill,
@@ -34,7 +35,9 @@ export const SolidArea = /*#__PURE__*/memo(_ref => {
34
35
  transitions: transitions
35
36
  }, pathProps, {
36
37
  children: gradient && /*#__PURE__*/_jsx(Gradient, {
38
+ animate: animate,
37
39
  gradient: gradient,
40
+ transition: (_transitions$update = transitions == null ? void 0 : transitions.update) != null ? _transitions$update : transition,
38
41
  xAxisId: xAxisId,
39
42
  yAxisId: yAxisId
40
43
  })
@@ -1,11 +1,15 @@
1
- import { useCallback } from 'react';
1
+ 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); }
2
+ import { memo, useCallback } from 'react';
3
+ import { candles as btcCandles } from '@coinbase/cds-common/internal/data/candles';
2
4
  import { Example, ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
3
5
  import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
4
- import { DottedLine } from '../../line';
6
+ import { VStack } from '@coinbase/cds-mobile/layout';
7
+ import { DefaultReferenceLineLabel, DottedLine, ReferenceLine } from '../../line';
5
8
  import { Scrubber } from '../../scrubber/Scrubber';
6
9
  import { AreaChart } from '..';
7
10
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
11
  const basicData = [24, 13, 98, 39, 48, 38, 43];
12
+ const baselineThresholdData = [40, 28, 21, 5, 48, 5, 28, 2, 29, 48, 18, 30, 29, 8].map(value => value + 50);
9
13
  const BasicExample = () => {
10
14
  const getScrubberAccessibilityLabel = useCallback(index => "Point " + (index + 1) + ": " + basicData[index], []);
11
15
  return /*#__PURE__*/_jsx(AreaChart, {
@@ -49,12 +53,164 @@ const StackedExample = () => {
49
53
  data: potentialRewardsData,
50
54
  color: theme.color.fgPositive,
51
55
  type: 'dotted',
52
- LineComponent: DottedLine
56
+ LineComponent: props => /*#__PURE__*/_jsx(DottedLine, _extends({}, props, {
57
+ dashIntervals: [6, 6]
58
+ }))
53
59
  }],
54
60
  type: "dotted",
55
61
  children: /*#__PURE__*/_jsx(Scrubber, {})
56
62
  });
57
63
  };
64
+ const CustomBaselineExample = () => {
65
+ const theme = useTheme();
66
+ const candles = [...btcCandles].reverse().slice(0, 180);
67
+ const prices = candles.map(candle => parseFloat(candle.close));
68
+ const dates = candles.map(candle => new Date(parseInt(candle.start, 10) * 1000));
69
+ const startingPrice = prices[0];
70
+ const formatPrice = useCallback(price => {
71
+ return "$" + price.toLocaleString('en-US', {
72
+ minimumFractionDigits: 2,
73
+ maximumFractionDigits: 2
74
+ });
75
+ }, []);
76
+ const formatPriceInThousands = useCallback(price => {
77
+ return "$" + (price / 1000).toLocaleString('en-US', {
78
+ minimumFractionDigits: 0,
79
+ maximumFractionDigits: 0
80
+ }) + "k";
81
+ }, []);
82
+ const formatDate = useCallback(date => {
83
+ return date.toLocaleDateString('en-US', {
84
+ month: 'short',
85
+ day: 'numeric',
86
+ year: 'numeric'
87
+ });
88
+ }, []);
89
+ const formatLabel = useCallback(dataIndex => formatPrice(prices[dataIndex]) + " " + formatDate(dates[dataIndex]), [dates, formatDate, formatPrice, prices]);
90
+ const PriceLabel = /*#__PURE__*/memo(props => /*#__PURE__*/_jsx(DefaultReferenceLineLabel, _extends({}, props, {
91
+ background: theme.color.bgSecondary,
92
+ borderRadius: 12.5,
93
+ color: theme.color.fg,
94
+ dx: 12,
95
+ font: "label1",
96
+ horizontalAlignment: "left",
97
+ inset: {
98
+ top: 4,
99
+ bottom: 4,
100
+ left: 8,
101
+ right: 8
102
+ }
103
+ })));
104
+ const chartAccessibilityLabel = "Bitcoin area chart with custom baseline. Current price: " + formatPrice(prices[prices.length - 1]) + ". Swipe to navigate.";
105
+ const getScrubberAccessibilityLabel = useCallback(index => formatPrice(prices[index]) + " " + formatDate(dates[index]), [dates, formatDate, formatPrice, prices]);
106
+ return /*#__PURE__*/_jsxs(AreaChart, {
107
+ enableScrubbing: true,
108
+ showLines: true,
109
+ showYAxis: true,
110
+ accessibilityLabel: chartAccessibilityLabel,
111
+ fillOpacity: 0.5,
112
+ getScrubberAccessibilityLabel: getScrubberAccessibilityLabel,
113
+ height: 300,
114
+ series: [{
115
+ id: 'prices',
116
+ data: prices,
117
+ gradient: {
118
+ stops: [{
119
+ offset: startingPrice,
120
+ color: theme.color.fgNegative
121
+ }, {
122
+ offset: startingPrice,
123
+ color: theme.color.fgPositive
124
+ }]
125
+ }
126
+ }],
127
+ yAxis: {
128
+ baseline: startingPrice,
129
+ showGrid: true,
130
+ tickLabelFormatter: formatPriceInThousands,
131
+ domain: {
132
+ min: 70000,
133
+ max: 120000
134
+ },
135
+ ticks: [80000, 100000, 120000]
136
+ },
137
+ children: [/*#__PURE__*/_jsx(Scrubber, {
138
+ labelElevated: true,
139
+ label: formatLabel
140
+ }), /*#__PURE__*/_jsx(ReferenceLine, {
141
+ LabelComponent: PriceLabel,
142
+ LineComponent: props => /*#__PURE__*/_jsx(DottedLine, _extends({}, props, {
143
+ dashIntervals: [0, 16],
144
+ strokeWidth: 3
145
+ })),
146
+ dataY: startingPrice,
147
+ label: formatPrice(startingPrice),
148
+ stroke: theme.color.fg
149
+ })]
150
+ });
151
+ };
152
+ const AxisBaselineThresholdExample = () => {
153
+ const theme = useTheme();
154
+ return /*#__PURE__*/_jsxs(VStack, {
155
+ gap: 2,
156
+ children: [/*#__PURE__*/_jsx(AreaChart, {
157
+ enableScrubbing: true,
158
+ showLines: true,
159
+ showYAxis: true,
160
+ accessibilityLabel: "Area chart with threshold baseline at 30.",
161
+ getScrubberAccessibilityLabel: index => "Point " + (index + 1) + ": " + baselineThresholdData[index],
162
+ height: 220,
163
+ inset: 0,
164
+ series: [{
165
+ id: 'axis-baseline-threshold-vertical',
166
+ data: baselineThresholdData,
167
+ gradient: {
168
+ stops: [{
169
+ offset: 30,
170
+ color: theme.color.fgNegative
171
+ }, {
172
+ offset: 30,
173
+ color: theme.color.fgPositive
174
+ }]
175
+ }
176
+ }],
177
+ type: "dotted",
178
+ yAxis: {
179
+ showGrid: true,
180
+ baseline: 30
181
+ },
182
+ children: /*#__PURE__*/_jsx(Scrubber, {})
183
+ }), /*#__PURE__*/_jsx(AreaChart, {
184
+ enableScrubbing: true,
185
+ showLines: true,
186
+ showXAxis: true,
187
+ accessibilityLabel: "Horizontal area chart with threshold baseline at 30.",
188
+ getScrubberAccessibilityLabel: index => "Point " + (index + 1) + ": " + baselineThresholdData[index],
189
+ height: 220,
190
+ inset: 0,
191
+ layout: "horizontal",
192
+ series: [{
193
+ id: 'axis-baseline-threshold-horizontal',
194
+ data: baselineThresholdData,
195
+ gradient: {
196
+ stops: [{
197
+ offset: 30,
198
+ color: theme.color.fgNegative
199
+ }, {
200
+ offset: 30,
201
+ color: theme.color.fgPositive
202
+ }]
203
+ }
204
+ }],
205
+ type: "dotted",
206
+ xAxis: {
207
+ showGrid: true,
208
+ baseline: 30
209
+ },
210
+ children: /*#__PURE__*/_jsx(Scrubber, {})
211
+ })]
212
+ });
213
+ };
58
214
  const AreaChartStories = () => {
59
215
  return /*#__PURE__*/_jsxs(ExampleScreen, {
60
216
  children: [/*#__PURE__*/_jsx(Example, {
@@ -82,6 +238,36 @@ const AreaChartStories = () => {
82
238
  },
83
239
  children: /*#__PURE__*/_jsx(Scrubber, {})
84
240
  })
241
+ }), /*#__PURE__*/_jsx(Example, {
242
+ title: "Axis Baseline",
243
+ children: /*#__PURE__*/_jsx(AreaChart, {
244
+ enableScrubbing: true,
245
+ showLines: true,
246
+ showYAxis: true,
247
+ accessibilityLabel: "Area chart with custom axis baseline at 100.",
248
+ getScrubberAccessibilityLabel: index => "Point " + (index + 1) + ": " + [112, 97, 121, 103, 129, 118, 94][index],
249
+ height: 220,
250
+ series: [{
251
+ id: 'netFlow',
252
+ data: [112, 97, 121, 103, 129, 118, 94]
253
+ }],
254
+ yAxis: {
255
+ baseline: 100,
256
+ domain: {
257
+ min: 80,
258
+ max: 140
259
+ },
260
+ showGrid: true,
261
+ tickLabelFormatter: value => "" + value
262
+ },
263
+ children: /*#__PURE__*/_jsx(Scrubber, {})
264
+ })
265
+ }), /*#__PURE__*/_jsx(Example, {
266
+ title: "Axis Baseline Threshold",
267
+ children: /*#__PURE__*/_jsx(AxisBaselineThresholdExample, {})
268
+ }), /*#__PURE__*/_jsx(Example, {
269
+ title: "Custom Baseline",
270
+ children: /*#__PURE__*/_jsx(CustomBaselineExample, {})
85
271
  }), /*#__PURE__*/_jsx(Example, {
86
272
  title: "Styles",
87
273
  children: /*#__PURE__*/_jsx(AreaChart, {
@@ -1,12 +1,12 @@
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", "id"],
3
- _excluded3 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "id"];
2
+ _excluded2 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "baseline", "id"],
3
+ _excluded3 = ["scaleType", "data", "categoryPadding", "domain", "domainLimit", "range", "baseline", "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 { defaultStackId } from '../utils';
9
+ import { defaultStackId, withBaselineDomain } 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) => {
@@ -57,6 +57,7 @@ export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
57
57
  domain: xDomain,
58
58
  domainLimit: xDomainLimit,
59
59
  range: xRange,
60
+ baseline: xBaseline,
60
61
  id: xAxisId
61
62
  } = _ref2,
62
63
  xAxisVisualProps = _objectWithoutPropertiesLoose(_ref2, _excluded2);
@@ -68,38 +69,29 @@ export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
68
69
  domain: yDomain,
69
70
  domainLimit: yDomainLimit,
70
71
  range: yRange,
72
+ baseline: yBaseline,
71
73
  id: yAxisId
72
74
  } = _ref3,
73
75
  yAxisVisualProps = _objectWithoutPropertiesLoose(_ref3, _excluded3);
74
- const hasNegativeValues = useMemo(() => {
75
- if (!series) return false;
76
- return series.some(s => {
77
- var _s$data;
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));
79
- });
80
- }, [series]);
76
+ const valueAxisBaseline = isHorizontalLayout ? xBaseline : yBaseline;
81
77
  const xAxisConfig = useMemo(() => ({
82
78
  scaleType: xScaleType != null ? xScaleType : defaultXScaleType,
83
79
  data: xData,
84
80
  categoryPadding: xCategoryPadding,
85
- domain: isHorizontalLayout && !hasNegativeValues ? _extends({
86
- min: 0
87
- }, xDomain) : xDomain,
81
+ domain: isHorizontalLayout ? withBaselineDomain(xDomain, valueAxisBaseline) : xDomain,
88
82
  domainLimit: xDomainLimit,
89
- range: xRange
90
- }), [xScaleType, defaultXScaleType, xData, xCategoryPadding, isHorizontalLayout, hasNegativeValues, xDomain, xDomainLimit, xRange]);
91
-
92
- // Set default min domain to 0 for bar chart, but only if there are no negative values.
83
+ range: xRange,
84
+ baseline: xBaseline
85
+ }), [xScaleType, defaultXScaleType, xData, xCategoryPadding, isHorizontalLayout, xDomain, xDomainLimit, xRange, xBaseline, valueAxisBaseline]);
93
86
  const yAxisConfig = useMemo(() => ({
94
87
  scaleType: yScaleType != null ? yScaleType : defaultYScaleType,
95
88
  data: yData,
96
89
  categoryPadding: yCategoryPadding,
97
- domain: !isHorizontalLayout && !hasNegativeValues ? _extends({
98
- min: 0
99
- }, yDomain) : yDomain,
90
+ domain: !isHorizontalLayout ? withBaselineDomain(yDomain, valueAxisBaseline) : yDomain,
100
91
  domainLimit: yDomainLimit,
101
- range: yRange
102
- }), [yScaleType, defaultYScaleType, yData, yCategoryPadding, isHorizontalLayout, hasNegativeValues, yDomain, yDomainLimit, yRange]);
92
+ range: yRange,
93
+ baseline: yBaseline
94
+ }), [yScaleType, defaultYScaleType, yData, yCategoryPadding, isHorizontalLayout, yDomain, yDomainLimit, yRange, yBaseline, valueAxisBaseline]);
103
95
  return /*#__PURE__*/_jsxs(CartesianChart, _extends({}, chartProps, {
104
96
  ref: ref,
105
97
  inset: inset,
@@ -1,7 +1,7 @@
1
1
  import React, { memo, useMemo } from 'react';
2
2
  import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
3
3
  import { useCartesianChartContext } from '../ChartProvider';
4
- import { EPSILON, getBars, getStackBaseline, getStackOrigin } from '../utils/bar';
4
+ import { EPSILON, getBars, getBaselinePx, getStackOrigin } from '../utils/bar';
5
5
  import { getGradientStops } from '../utils/gradient';
6
6
  import { convertToSerializableScale } from '../utils/scale';
7
7
  import { Bar } from './Bar';
@@ -48,7 +48,11 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
48
48
  } = useCartesianChartContext();
49
49
  const xAxis = getXAxis(xAxisId);
50
50
  const yAxis = getYAxis(yAxisId);
51
- const baseline = useMemo(() => getStackBaseline(valueScale, rect, layout), [rect, valueScale, layout]);
51
+ const baseline = useMemo(() => {
52
+ var _ref2;
53
+ return (_ref2 = layout === 'vertical' ? yAxis : xAxis) == null ? void 0 : _ref2.baseline;
54
+ }, [layout, yAxis, xAxis]);
55
+ const baselinePx = useMemo(() => getBaselinePx(valueScale, rect, layout, baseline), [rect, valueScale, layout, baseline]);
52
56
  const seriesGradients = useMemo(() => {
53
57
  return series.map(s => {
54
58
  if (!s.gradient) return;
@@ -87,6 +91,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
87
91
  roundBaseline,
88
92
  layout,
89
93
  baseline,
94
+ baselinePx,
90
95
  stackGap,
91
96
  barMinSize,
92
97
  stackMinSize,
@@ -96,12 +101,12 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
96
101
  defaultStroke,
97
102
  defaultStrokeWidth,
98
103
  defaultBarComponent
99
- }), [series, seriesData, indexPos, thickness, categoryIndex, categoryValue, roundBaseline, baseline, stackGap, barMinSize, stackMinSize, valueScale, seriesGradients, theme.color.fgPrimary, layout, borderRadius, defaultFillOpacity, defaultStroke, defaultStrokeWidth, defaultBarComponent]);
104
+ }), [series, seriesData, categoryIndex, categoryValue, indexPos, thickness, valueScale, seriesGradients, roundBaseline, layout, baseline, baselinePx, stackGap, barMinSize, stackMinSize, theme.color.fgPrimary, borderRadius, defaultFillOpacity, defaultStroke, defaultStrokeWidth, defaultBarComponent]);
100
105
  const stackRect = useMemo(() => {
101
106
  if (bars.length === 0) {
102
107
  return {
103
- x: layout === 'vertical' ? indexPos : baseline,
104
- y: layout === 'vertical' ? baseline : indexPos,
108
+ x: layout === 'vertical' ? indexPos : baselinePx,
109
+ y: layout === 'vertical' ? baselinePx : indexPos,
105
110
  width: layout === 'vertical' ? thickness : 0,
106
111
  height: layout === 'vertical' ? 0 : thickness
107
112
  };
@@ -116,14 +121,14 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
116
121
  width: maxX - minX,
117
122
  height: maxY - minY
118
123
  };
119
- }, [bars, baseline, indexPos, layout, thickness]);
124
+ }, [bars, baselinePx, indexPos, layout, thickness]);
120
125
  const stackOrigin = useMemo(() => {
121
126
  var _getStackOrigin;
122
127
  return (_getStackOrigin = getStackOrigin(bars.map(b => b.origin), bars.map(b => {
123
128
  var _b$minSize;
124
129
  return (_b$minSize = b.minSize) != null ? _b$minSize : 0;
125
- }))) != null ? _getStackOrigin : baseline;
126
- }, [bars, baseline]);
130
+ }))) != null ? _getStackOrigin : baselinePx;
131
+ }, [bars, baselinePx]);
127
132
  const barElements = bars.map((bar, index) => /*#__PURE__*/_jsx(Bar, {
128
133
  BarComponent: bar.BarComponent,
129
134
  borderRadius: bar.borderRadius,
@@ -147,8 +152,8 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
147
152
  }, bar.seriesId + "-" + categoryIndex + "-" + index));
148
153
  const edge = layout === 'vertical' ? stackRect.y : stackRect.x;
149
154
  const size = layout === 'vertical' ? stackRect.height : stackRect.width;
150
- const stackRoundLower = roundBaseline || Math.abs(edge - baseline) >= EPSILON;
151
- const stackRoundHigher = roundBaseline || Math.abs(edge + size - baseline) >= EPSILON;
155
+ const stackRoundLower = roundBaseline || Math.abs(edge - baselinePx) >= EPSILON;
156
+ const stackRoundHigher = roundBaseline || Math.abs(edge + size - baselinePx) >= EPSILON;
152
157
  const stackRoundTop = layout === 'vertical' ? stackRoundLower : stackRoundHigher;
153
158
  const stackRoundBottom = layout === 'vertical' ? stackRoundHigher : stackRoundLower;
154
159
  return /*#__PURE__*/_jsx(BarStackComponent, {