@coinbase/cds-mobile-visualization 3.7.0 → 3.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,18 @@ All notable changes to this project will be documented in this file.
8
8
 
9
9
  <!-- template-start -->
10
10
 
11
+ ## 3.8.1 (5/18/2026 PST)
12
+
13
+ #### 🐞 Fixes
14
+
15
+ - Fix(CDS-2065): keep Gradient colors in sync with positions on stop count changes. [[#3455](https://github.com/coinbase/cds/pull/3455)]
16
+
17
+ ## 3.8.0 (5/8/2026 PST)
18
+
19
+ #### 🚀 Updates
20
+
21
+ - Feat: support active color on Tabs. [[#669](https://github.com/coinbase/cds/pull/669)]
22
+
11
23
  ## 3.7.0 (4/20/2026 PST)
12
24
 
13
25
  #### 🚀 Updates
@@ -1 +1 @@
1
- {"version":3,"file":"PeriodSelector.d.ts","sourceRoot":"","sources":["../../src/chart/PeriodSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAC;AACzD,OAAO,EAAc,IAAI,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAGhE,OAAO,EAEL,KAAK,kBAAkB,EAEvB,KAAK,wBAAwB,EAC9B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAQ,KAAK,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAG3E,eAAO,MAAM,6BAA6B,GAAI,wDAK3C,wBAAwB,wDAmD1B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG;IAClD;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,KAAK,CAAC,EAAE,GAAG,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AAStD,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;IAxBvB;;;OAGG;YACK,MAAM;IACd;;OAEG;cACO,OAAO;IACjB;;OAEG;YACK,GAAG;+BAyCZ,CAAC;AASF,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAErD;;;GAGG;AACH,eAAO,MAAM,cAAc;;YA9Hd,CAAC;WAGZ,CAAC;uBAGE,CAAC;;8BAkJL,CAAC"}
1
+ {"version":3,"file":"PeriodSelector.d.ts","sourceRoot":"","sources":["../../src/chart/PeriodSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAC;AACzD,OAAO,EAAc,IAAI,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAGhE,OAAO,EAEL,KAAK,kBAAkB,EAEvB,KAAK,wBAAwB,EAC9B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAQ,KAAK,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAG3E,eAAO,MAAM,6BAA6B,GAAI,wDAK3C,wBAAwB,wDAmD1B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG;IAClD;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,KAAK,CAAC,EAAE,GAAG,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AAStD,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;IAxBvB;;;OAGG;YACK,MAAM;IACd;;OAEG;cACO,OAAO;IACjB;;OAEG;YACK,GAAG;+BAyCZ,CAAC;AAQF,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAErD;;;GAGG;AACH,eAAO,MAAM,cAAc;;YA7Hd,CAAC;WAGZ,CAAC;uBAGE,CAAC;;8BAmJL,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Gradient.d.ts","sourceRoot":"","sources":["../../../src/chart/gradient/Gradient.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAyD,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAClG,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,QAAQ,EAAE,kBAAkB,CAAC;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAAG;IAC9C;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,qDA6IpB,CAAC"}
1
+ {"version":3,"file":"Gradient.d.ts","sourceRoot":"","sources":["../../../src/chart/gradient/Gradient.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAyD,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAClG,OAAO,EAIL,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,QAAQ,EAAE,kBAAkB,CAAC;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,iBAAiB,GAAG;IAC9C;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,qDAyJpB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  const _excluded = ["color", "label", "font", "hideDot", "style"],
2
- _excluded2 = ["background", "activeBackground", "width", "justifyContent", "TabComponent", "TabsActiveIndicatorComponent"];
2
+ _excluded2 = ["background", "activeBackground", "activeColor", "width", "justifyContent", "TabComponent", "TabsActiveIndicatorComponent"];
3
3
  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); }
4
4
  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; }
5
5
  import React, { forwardRef, memo, useMemo } from 'react';
@@ -107,11 +107,8 @@ export const LiveTabLabel = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref2, re
107
107
  }))]
108
108
  });
109
109
  }));
110
-
111
- // Custom tab component with primary color for active state
112
110
  const PeriodSelectorTab = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((props, ref) => /*#__PURE__*/_jsx(SegmentedTab, _extends({
113
111
  ref: ref,
114
- activeColor: "fgPrimary",
115
112
  font: "label1"
116
113
  }, props))));
117
114
  /**
@@ -122,6 +119,7 @@ export const PeriodSelector = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref3,
122
119
  let {
123
120
  background = 'transparent',
124
121
  activeBackground = 'bgPrimaryWash',
122
+ activeColor = 'fgPrimary',
125
123
  width = '100%',
126
124
  justifyContent = 'space-between',
127
125
  TabComponent = PeriodSelectorTab,
@@ -133,6 +131,7 @@ export const PeriodSelector = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref3,
133
131
  TabComponent: TabComponent,
134
132
  TabsActiveIndicatorComponent: TabsActiveIndicatorComponent,
135
133
  activeBackground: activeBackground,
134
+ activeColor: activeColor,
136
135
  background: background,
137
136
  justifyContent: justifyContent,
138
137
  width: width
@@ -1,9 +1,10 @@
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
- import { memo, useCallback } from 'react';
2
+ import { memo, useCallback, useState } from 'react';
3
3
  import { candles as btcCandles } from '@coinbase/cds-common/internal/data/candles';
4
+ import { Button } from '@coinbase/cds-mobile/buttons/Button';
4
5
  import { Example, ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
5
6
  import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
6
- import { VStack } from '@coinbase/cds-mobile/layout';
7
+ import { Box, VStack } from '@coinbase/cds-mobile/layout';
7
8
  import { DefaultReferenceLineLabel, DottedLine, ReferenceLine } from '../../line';
8
9
  import { Scrubber } from '../../scrubber/Scrubber';
9
10
  import { AreaChart } from '..';
@@ -211,6 +212,44 @@ const AxisBaselineThresholdExample = () => {
211
212
  })]
212
213
  });
213
214
  };
215
+
216
+ // Regression repro for CDS-2065. The default baseline is 0; alternating between
217
+ // an all-positive dataset (2 gradient stops) and a negative-crossing dataset
218
+ // (3 gradient stops) causes the Gradient component to swap stop counts between
219
+ // renders. Before the fix this crashed with `ReanimatedError: Positions array
220
+ // must have the same size as colors array`.
221
+ const positiveOnlyData = [10, 25, 15, 40, 30, 50, 35];
222
+ const crossesBaselineData = [-20, 25, -15, 40, -30, 50, 35];
223
+ const BaselineCrossingExample = () => {
224
+ const [crosses, setCrosses] = useState(false);
225
+ const data = crosses ? crossesBaselineData : positiveOnlyData;
226
+ return /*#__PURE__*/_jsxs(VStack, {
227
+ gap: 2,
228
+ children: [/*#__PURE__*/_jsx(AreaChart, {
229
+ showLines: true,
230
+ showYAxis: true,
231
+ accessibilityLabel: "Area chart toggling across the y-axis baseline",
232
+ height: 220,
233
+ series: [{
234
+ id: 'flow',
235
+ data
236
+ }],
237
+ type: "gradient",
238
+ yAxis: {
239
+ baseline: 0,
240
+ showGrid: true
241
+ }
242
+ }), /*#__PURE__*/_jsx(Box, {
243
+ paddingX: 2,
244
+ children: /*#__PURE__*/_jsx(Button, {
245
+ compact: true,
246
+ onPress: () => setCrosses(c => !c),
247
+ variant: "secondary",
248
+ children: "Toggle baseline crossing"
249
+ })
250
+ })]
251
+ });
252
+ };
214
253
  const AreaChartStories = () => {
215
254
  return /*#__PURE__*/_jsxs(ExampleScreen, {
216
255
  children: [/*#__PURE__*/_jsx(Example, {
@@ -238,6 +277,9 @@ const AreaChartStories = () => {
238
277
  },
239
278
  children: /*#__PURE__*/_jsx(Scrubber, {})
240
279
  })
280
+ }), /*#__PURE__*/_jsx(Example, {
281
+ title: "Baseline Crossing (CDS-2065 repro)",
282
+ children: /*#__PURE__*/_jsx(BaselineCrossingExample, {})
241
283
  }), /*#__PURE__*/_jsx(Example, {
242
284
  title: "Axis Baseline",
243
285
  children: /*#__PURE__*/_jsx(AreaChart, {
@@ -64,7 +64,7 @@ export const Gradient = /*#__PURE__*/memo(_ref => {
64
64
  };
65
65
 
66
66
  // Extract colors and positions for LinearGradient.
67
- const colors = useMemo(() => (stops != null ? stops : []).map(stop => {
67
+ const targetColors = useMemo(() => (stops != null ? stops : []).map(stop => {
68
68
  var _stop$opacity;
69
69
  return getColorWithOpacity(stop.color, (_stop$opacity = stop.opacity) != null ? _stop$opacity : 1);
70
70
  }), [stops]);
@@ -73,6 +73,11 @@ export const Gradient = /*#__PURE__*/memo(_ref => {
73
73
  const startY = useSharedValue(targetStart.y);
74
74
  const endX = useSharedValue(targetEnd.x);
75
75
  const endY = useSharedValue(targetEnd.y);
76
+
77
+ // colors lives on the worklet so it flips in lockstep with positions; updating
78
+ // it via useMemo on the JS thread races the worklet-driven positions and Skia
79
+ // can paint mismatched array sizes when the stop count changes.
80
+ const currentColors = useSharedValue(targetColors);
76
81
  const fromPositions = useSharedValue(targetPositions);
77
82
  const toPositions = useSharedValue(targetPositions);
78
83
  const positionsProgress = useSharedValue(1);
@@ -88,6 +93,7 @@ export const Gradient = /*#__PURE__*/memo(_ref => {
88
93
  startY.value = targetStart.y;
89
94
  endX.value = targetEnd.x;
90
95
  endY.value = targetEnd.y;
96
+ currentColors.value = targetColors;
91
97
  fromPositions.value = [...targetPositions];
92
98
  toPositions.value = [...targetPositions];
93
99
  positionsProgress.value = 1;
@@ -99,16 +105,18 @@ export const Gradient = /*#__PURE__*/memo(_ref => {
99
105
  endY.value = buildTransition(targetEnd.y, transition);
100
106
  const canAnimatePositions = toPositions.value.length === targetPositions.length;
101
107
  if (canAnimatePositions) {
108
+ currentColors.value = targetColors;
102
109
  fromPositions.value = [...toPositions.value];
103
110
  toPositions.value = [...targetPositions];
104
111
  positionsProgress.value = 0;
105
112
  positionsProgress.value = buildTransition(1, transition);
106
113
  } else {
114
+ currentColors.value = targetColors;
107
115
  fromPositions.value = [...targetPositions];
108
116
  toPositions.value = [...targetPositions];
109
117
  positionsProgress.value = 1;
110
118
  }
111
- }, [transition, targetStart.x, targetStart.y, targetEnd.x, targetEnd.y, targetPositions, startX, startY, endX, endY, fromPositions, toPositions, positionsProgress, shouldRender]);
119
+ }, [transition, targetStart.x, targetStart.y, targetEnd.x, targetEnd.y, targetColors, targetPositions, startX, startY, endX, endY, currentColors, fromPositions, toPositions, positionsProgress, shouldRender]);
112
120
  const start = useDerivedValue(() => {
113
121
  return {
114
122
  x: startX.value,
@@ -121,6 +129,7 @@ export const Gradient = /*#__PURE__*/memo(_ref => {
121
129
  y: endY.value
122
130
  };
123
131
  }, [endX, endY]);
132
+ const colors = useDerivedValue(() => currentColors.value, [currentColors]);
124
133
  const positions = useDerivedValue(() => {
125
134
  const from = fromPositions.value;
126
135
  const to = toPositions.value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coinbase/cds-mobile-visualization",
3
- "version": "3.7.0",
3
+ "version": "3.8.1",
4
4
  "description": "Coinbase Design System - Mobile Visualization Native",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,9 +36,9 @@
36
36
  "CHANGELOG"
37
37
  ],
38
38
  "peerDependencies": {
39
- "@coinbase/cds-common": "^8.66.0",
39
+ "@coinbase/cds-common": "^8.75.1",
40
40
  "@coinbase/cds-lottie-files": "^3.3.4",
41
- "@coinbase/cds-mobile": "^8.66.0",
41
+ "@coinbase/cds-mobile": "^8.75.1",
42
42
  "@coinbase/cds-utils": "^2.3.5",
43
43
  "@shopify/react-native-skia": "^1.12.4 || ^2.0.0",
44
44
  "react": "^18.3.1",
@@ -57,9 +57,9 @@
57
57
  "@babel/preset-env": "^7.28.0",
58
58
  "@babel/preset-react": "^7.27.1",
59
59
  "@babel/preset-typescript": "^7.27.1",
60
- "@coinbase/cds-common": "^8.66.0",
60
+ "@coinbase/cds-common": "^8.75.1",
61
61
  "@coinbase/cds-lottie-files": "^3.3.4",
62
- "@coinbase/cds-mobile": "^8.66.0",
62
+ "@coinbase/cds-mobile": "^8.75.1",
63
63
  "@coinbase/cds-utils": "^2.3.5",
64
64
  "@shopify/react-native-skia": "1.12.4",
65
65
  "@types/react": "^18.3.12",