@coinbase/cds-mobile-visualization 3.6.1 → 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 (53) hide show
  1. package/CHANGELOG.md +12 -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/BarPlot.d.ts.map +1 -1
  12. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  13. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  14. package/dts/chart/gradient/Gradient.d.ts +14 -3
  15. package/dts/chart/gradient/Gradient.d.ts.map +1 -1
  16. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  17. package/dts/chart/line/Line.d.ts +3 -0
  18. package/dts/chart/line/Line.d.ts.map +1 -1
  19. package/dts/chart/line/LineChart.d.ts.map +1 -1
  20. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  21. package/dts/chart/utils/axis.d.ts +18 -8
  22. package/dts/chart/utils/axis.d.ts.map +1 -1
  23. package/dts/chart/utils/bar.d.ts +17 -7
  24. package/dts/chart/utils/bar.d.ts.map +1 -1
  25. package/dts/chart/utils/chart.d.ts +9 -0
  26. package/dts/chart/utils/chart.d.ts.map +1 -1
  27. package/dts/chart/utils/context.d.ts +3 -3
  28. package/dts/chart/utils/context.d.ts.map +1 -1
  29. package/dts/chart/utils/gradient.d.ts +14 -4
  30. package/dts/chart/utils/gradient.d.ts.map +1 -1
  31. package/esm/chart/CartesianChart.js +6 -4
  32. package/esm/chart/__stories__/ChartTransitions.stories.js +68 -0
  33. package/esm/chart/area/Area.js +0 -2
  34. package/esm/chart/area/AreaChart.js +15 -19
  35. package/esm/chart/area/DottedArea.js +6 -4
  36. package/esm/chart/area/GradientArea.js +6 -4
  37. package/esm/chart/area/SolidArea.js +3 -0
  38. package/esm/chart/area/__stories__/AreaChart.stories.js +189 -3
  39. package/esm/chart/bar/BarChart.js +14 -22
  40. package/esm/chart/bar/BarPlot.js +11 -2
  41. package/esm/chart/bar/BarStack.js +15 -10
  42. package/esm/chart/bar/DefaultBarStack.js +9 -4
  43. package/esm/chart/bar/__stories__/BarChart.stories.js +84 -2
  44. package/esm/chart/gradient/Gradient.js +119 -26
  45. package/esm/chart/line/DottedLine.js +3 -0
  46. package/esm/chart/line/Line.js +1 -3
  47. package/esm/chart/line/LineChart.js +8 -4
  48. package/esm/chart/line/SolidLine.js +3 -0
  49. package/esm/chart/utils/axis.js +32 -4
  50. package/esm/chart/utils/bar.js +129 -76
  51. package/esm/chart/utils/chart.js +53 -21
  52. package/esm/chart/utils/gradient.js +15 -5
  53. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
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
2
  import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { useTheme } from '@coinbase/cds-mobile';
3
4
  import { Button } from '@coinbase/cds-mobile/buttons/Button';
4
5
  import { IconButton } from '@coinbase/cds-mobile/buttons/IconButton';
5
6
  import { ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
@@ -299,6 +300,69 @@ function AreaExample(_ref5) {
299
300
  })]
300
301
  });
301
302
  }
303
+ function SessionBaselineAreaTransitionsExample() {
304
+ const theme = useTheme();
305
+ const [resetKey, setResetKey] = useState(0);
306
+ const [data, setData] = useState(generateInitialData());
307
+ const handleReset = useCallback(() => {
308
+ setData(generateInitialData());
309
+ setResetKey(k => k + 1);
310
+ }, []);
311
+ useEffect(() => {
312
+ const intervalId = setInterval(() => {
313
+ setData(d => [...d.slice(1), generateNextValue(d[d.length - 1])]);
314
+ }, updateInterval);
315
+ return () => clearInterval(intervalId);
316
+ }, []);
317
+ const baseline = data[0];
318
+ return /*#__PURE__*/_jsxs(VStack, {
319
+ gap: 2,
320
+ children: [/*#__PURE__*/_jsx(AreaChart, {
321
+ enableScrubbing: true,
322
+ showLines: true,
323
+ height: 200,
324
+ inset: {
325
+ top: 16,
326
+ bottom: 16,
327
+ left: 16,
328
+ right: 16
329
+ },
330
+ series: [{
331
+ id: 'values',
332
+ data,
333
+ type: 'gradient',
334
+ gradient: {
335
+ stops: [{
336
+ offset: baseline,
337
+ color: theme.color.fgNegative
338
+ }, {
339
+ offset: baseline,
340
+ color: theme.color.fgPositive
341
+ }]
342
+ }
343
+ }],
344
+ yAxis: {
345
+ baseline: baseline,
346
+ domain: {
347
+ min: 0,
348
+ max: 100
349
+ }
350
+ },
351
+ children: /*#__PURE__*/_jsx(Scrubber, {
352
+ hideOverlay: true,
353
+ idlePulse: true
354
+ })
355
+ }, resetKey), /*#__PURE__*/_jsx(Box, {
356
+ paddingX: 2,
357
+ children: /*#__PURE__*/_jsx(Button, {
358
+ compact: true,
359
+ onPress: handleReset,
360
+ variant: "secondary",
361
+ children: "Reset"
362
+ })
363
+ })]
364
+ });
365
+ }
302
366
 
303
367
  // --- Bar Chart Components ---
304
368
 
@@ -531,6 +595,10 @@ function ExampleNavigator() {
531
595
  resettable: false,
532
596
  transitions: updateOnly
533
597
  })
598
+ }, {
599
+ category: 'Area',
600
+ title: 'Session baseline',
601
+ component: /*#__PURE__*/_jsx(SessionBaselineAreaTransitionsExample, {})
534
602
  }, {
535
603
  category: 'Bar',
536
604
  title: 'Enter Only',
@@ -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,
@@ -65,12 +65,21 @@ export const BarPlot = /*#__PURE__*/memo(_ref => {
65
65
  from: prev.to,
66
66
  to: nextPath
67
67
  }));
68
- clipProgress.value = 0;
69
- clipProgress.value = buildTransition(1, animate ? clipUpdateTransition : null);
68
+ if (drawingArea.width || !drawingArea.height) {
69
+ clipProgress.value = 1;
70
+ } else {
71
+ clipProgress.value = 0;
72
+ clipProgress.value = buildTransition(1, animate ? clipUpdateTransition : null);
73
+ }
70
74
  // eslint-disable-next-line react-hooks/exhaustive-deps
71
75
  }, [drawingArea, animate, clipUpdateTransition]);
72
76
  const animatedClipPath = usePathInterpolation(clipProgress, [0, 1], [clipPaths.from, clipPaths.to]);
73
77
  if (!drawingArea) return;
78
+
79
+ // Clip path animation for bar is just for chart size changes, not for
80
+ // enter transition. One caveat, bar update transitions are staggered
81
+ // but clip path is not, so some bars could be clipped in rare cases
82
+
74
83
  return /*#__PURE__*/_jsx(Group, {
75
84
  clip: animatedClipPath,
76
85
  children: stackGroups.map((group, stackIndex) => /*#__PURE__*/_jsx(BarStackGroup, {