@coinbase/cds-mobile-visualization 3.4.0-beta.9 → 3.6.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.
Files changed (174) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/dts/chart/CartesianChart.d.ts +92 -7
  3. package/dts/chart/CartesianChart.d.ts.map +1 -1
  4. package/dts/chart/ChartContextBridge.d.ts.map +1 -1
  5. package/dts/chart/ChartProvider.d.ts +3 -0
  6. package/dts/chart/ChartProvider.d.ts.map +1 -1
  7. package/dts/chart/Path.d.ts +43 -13
  8. package/dts/chart/Path.d.ts.map +1 -1
  9. package/dts/chart/PeriodSelector.d.ts +20 -5
  10. package/dts/chart/PeriodSelector.d.ts.map +1 -1
  11. package/dts/chart/area/Area.d.ts +14 -11
  12. package/dts/chart/area/Area.d.ts.map +1 -1
  13. package/dts/chart/area/AreaChart.d.ts +33 -9
  14. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  15. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  16. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  17. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  18. package/dts/chart/axis/Axis.d.ts +22 -42
  19. package/dts/chart/axis/Axis.d.ts.map +1 -1
  20. package/dts/chart/axis/XAxis.d.ts +6 -0
  21. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  22. package/dts/chart/axis/YAxis.d.ts +1 -0
  23. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  24. package/dts/chart/bar/Bar.d.ts +59 -51
  25. package/dts/chart/bar/Bar.d.ts.map +1 -1
  26. package/dts/chart/bar/BarChart.d.ts +56 -11
  27. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  28. package/dts/chart/bar/BarPlot.d.ts +2 -1
  29. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  30. package/dts/chart/bar/BarStack.d.ts +45 -20
  31. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  32. package/dts/chart/bar/BarStackGroup.d.ts +2 -1
  33. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  34. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  35. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  36. package/dts/chart/bar/PercentageBarChart.d.ts +106 -0
  37. package/dts/chart/bar/PercentageBarChart.d.ts.map +1 -0
  38. package/dts/chart/bar/index.d.ts +1 -0
  39. package/dts/chart/bar/index.d.ts.map +1 -1
  40. package/dts/chart/gradient/Gradient.d.ts +5 -0
  41. package/dts/chart/gradient/Gradient.d.ts.map +1 -1
  42. package/dts/chart/index.d.ts +1 -0
  43. package/dts/chart/index.d.ts.map +1 -1
  44. package/dts/chart/legend/DefaultLegendEntry.d.ts +5 -0
  45. package/dts/chart/legend/DefaultLegendEntry.d.ts.map +1 -0
  46. package/dts/chart/legend/DefaultLegendShape.d.ts +5 -0
  47. package/dts/chart/legend/DefaultLegendShape.d.ts.map +1 -0
  48. package/dts/chart/legend/Legend.d.ts +168 -0
  49. package/dts/chart/legend/Legend.d.ts.map +1 -0
  50. package/dts/chart/legend/index.d.ts +4 -0
  51. package/dts/chart/legend/index.d.ts.map +1 -0
  52. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  53. package/dts/chart/line/Line.d.ts +23 -19
  54. package/dts/chart/line/Line.d.ts.map +1 -1
  55. package/dts/chart/line/LineChart.d.ts +26 -9
  56. package/dts/chart/line/LineChart.d.ts.map +1 -1
  57. package/dts/chart/line/ReferenceLine.d.ts +1 -0
  58. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  59. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  60. package/dts/chart/point/Point.d.ts +26 -2
  61. package/dts/chart/point/Point.d.ts.map +1 -1
  62. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +32 -2
  63. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -1
  64. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +2 -1
  65. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -1
  66. package/dts/chart/scrubber/Scrubber.d.ts +86 -17
  67. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  68. package/dts/chart/scrubber/ScrubberAccessibilityView.d.ts +12 -0
  69. package/dts/chart/scrubber/ScrubberAccessibilityView.d.ts.map +1 -0
  70. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +10 -0
  71. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -1
  72. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +16 -1
  73. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -1
  74. package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
  75. package/dts/chart/utils/axis.d.ts +45 -10
  76. package/dts/chart/utils/axis.d.ts.map +1 -1
  77. package/dts/chart/utils/bar.d.ts +195 -0
  78. package/dts/chart/utils/bar.d.ts.map +1 -1
  79. package/dts/chart/utils/chart.d.ts +32 -0
  80. package/dts/chart/utils/chart.d.ts.map +1 -1
  81. package/dts/chart/utils/context.d.ts +21 -6
  82. package/dts/chart/utils/context.d.ts.map +1 -1
  83. package/dts/chart/utils/gradient.d.ts +3 -1
  84. package/dts/chart/utils/gradient.d.ts.map +1 -1
  85. package/dts/chart/utils/path.d.ts +26 -0
  86. package/dts/chart/utils/path.d.ts.map +1 -1
  87. package/dts/chart/utils/point.d.ts +24 -12
  88. package/dts/chart/utils/point.d.ts.map +1 -1
  89. package/dts/chart/utils/scale.d.ts +11 -0
  90. package/dts/chart/utils/scale.d.ts.map +1 -1
  91. package/dts/chart/utils/scrubber.d.ts +2 -1
  92. package/dts/chart/utils/scrubber.d.ts.map +1 -1
  93. package/dts/chart/utils/transition.d.ts +63 -22
  94. package/dts/chart/utils/transition.d.ts.map +1 -1
  95. package/dts/sparkline/Sparkline.d.ts +2 -1
  96. package/dts/sparkline/Sparkline.d.ts.map +1 -1
  97. package/dts/sparkline/SparklineArea.d.ts +2 -1
  98. package/dts/sparkline/SparklineArea.d.ts.map +1 -1
  99. package/dts/sparkline/SparklineGradient.d.ts +2 -1
  100. package/dts/sparkline/SparklineGradient.d.ts.map +1 -1
  101. package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts +2 -1
  102. package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts.map +1 -1
  103. package/esm/chart/CartesianChart.js +176 -82
  104. package/esm/chart/ChartContextBridge.js +14 -3
  105. package/esm/chart/ChartProvider.js +2 -2
  106. package/esm/chart/Path.js +68 -31
  107. package/esm/chart/PeriodSelector.js +5 -1
  108. package/esm/chart/__stories__/CartesianChart.stories.js +16 -80
  109. package/esm/chart/__stories__/ChartAccessibility.stories.js +721 -0
  110. package/esm/chart/__stories__/ChartTransitions.stories.js +625 -0
  111. package/esm/chart/__stories__/PeriodSelector.stories.js +99 -1
  112. package/esm/chart/area/Area.js +21 -9
  113. package/esm/chart/area/AreaChart.js +18 -13
  114. package/esm/chart/area/DottedArea.js +28 -18
  115. package/esm/chart/area/GradientArea.js +14 -7
  116. package/esm/chart/area/SolidArea.js +6 -2
  117. package/esm/chart/area/__stories__/AreaChart.stories.js +47 -5
  118. package/esm/chart/axis/Axis.js +5 -41
  119. package/esm/chart/axis/XAxis.js +116 -47
  120. package/esm/chart/axis/YAxis.js +105 -26
  121. package/esm/chart/axis/__stories__/Axis.stories.js +324 -48
  122. package/esm/chart/bar/Bar.js +17 -15
  123. package/esm/chart/bar/BarChart.js +38 -33
  124. package/esm/chart/bar/BarPlot.js +40 -45
  125. package/esm/chart/bar/BarStack.js +92 -475
  126. package/esm/chart/bar/BarStackGroup.js +37 -27
  127. package/esm/chart/bar/DefaultBar.js +41 -18
  128. package/esm/chart/bar/DefaultBarStack.js +25 -9
  129. package/esm/chart/bar/PercentageBarChart.js +99 -0
  130. package/esm/chart/bar/__stories__/BarChart.stories.js +721 -54
  131. package/esm/chart/bar/__stories__/PercentageBarChart.stories.js +833 -0
  132. package/esm/chart/bar/index.js +1 -0
  133. package/esm/chart/gradient/Gradient.js +2 -1
  134. package/esm/chart/index.js +1 -0
  135. package/esm/chart/legend/DefaultLegendEntry.js +42 -0
  136. package/esm/chart/legend/DefaultLegendShape.js +64 -0
  137. package/esm/chart/legend/Legend.js +59 -0
  138. package/esm/chart/legend/__stories__/Legend.stories.js +574 -0
  139. package/esm/chart/legend/index.js +3 -0
  140. package/esm/chart/line/DottedLine.js +6 -2
  141. package/esm/chart/line/Line.js +42 -38
  142. package/esm/chart/line/LineChart.js +36 -12
  143. package/esm/chart/line/SolidLine.js +6 -2
  144. package/esm/chart/line/__stories__/LineChart.stories.js +236 -590
  145. package/esm/chart/line/__stories__/ReferenceLine.stories.js +95 -1
  146. package/esm/chart/point/Point.js +35 -36
  147. package/esm/chart/scrubber/DefaultScrubberBeacon.js +41 -38
  148. package/esm/chart/scrubber/DefaultScrubberLabel.js +26 -10
  149. package/esm/chart/scrubber/Scrubber.js +67 -35
  150. package/esm/chart/scrubber/ScrubberAccessibilityView.js +177 -0
  151. package/esm/chart/scrubber/ScrubberBeaconGroup.js +30 -22
  152. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +35 -8
  153. package/esm/chart/scrubber/ScrubberProvider.js +29 -24
  154. package/esm/chart/scrubber/__stories__/Scrubber.stories.js +946 -0
  155. package/esm/chart/utils/axis.js +88 -44
  156. package/esm/chart/utils/bar.js +829 -0
  157. package/esm/chart/utils/chart.js +34 -7
  158. package/esm/chart/utils/context.js +7 -0
  159. package/esm/chart/utils/gradient.js +8 -4
  160. package/esm/chart/utils/path.js +91 -61
  161. package/esm/chart/utils/point.js +92 -39
  162. package/esm/chart/utils/scale.js +13 -2
  163. package/esm/chart/utils/scrubber.js +12 -5
  164. package/esm/chart/utils/transition.js +116 -60
  165. package/esm/sparkline/Sparkline.js +2 -1
  166. package/esm/sparkline/SparklineArea.js +2 -1
  167. package/esm/sparkline/SparklineGradient.js +2 -1
  168. package/esm/sparkline/__figma__/Sparkline.figma.js +1 -1
  169. package/esm/sparkline/sparkline-interactive/SparklineInteractive.js +2 -1
  170. package/esm/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.js +1 -1
  171. package/esm/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.js +1 -1
  172. package/esm/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.js +2 -0
  173. package/package.json +5 -6
  174. package/esm/chart/__stories__/Chart.stories.js +0 -77
@@ -6,6 +6,17 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
6
6
  */
7
7
 
8
8
  import * as React from 'react';
9
+ import { ThemeContext } from '@coinbase/cds-mobile/system/ThemeProvider';
10
+ import { ScrubberContext } from './utils/context';
11
+ import { CartesianChartContext } from './ChartProvider';
12
+
13
+ /**
14
+ * Whitelist of contexts that should be bridged to the Skia canvas.
15
+ * Only these contexts will be made available inside the chart's Skia tree.
16
+ * This improves performance by avoiding the overhead of rendering every bridged context.
17
+ */
18
+ import { jsx as _jsx } from "react/jsx-runtime";
19
+ const BRIDGED_CONTEXTS = [ThemeContext, CartesianChartContext, ScrubberContext];
9
20
 
10
21
  /**
11
22
  * Represents a react-internal tree node.
@@ -14,7 +25,7 @@ import * as React from 'react';
14
25
  /**
15
26
  * Represents a tree node selector for traversal.
16
27
  */
17
- import { jsx as _jsx } from "react/jsx-runtime";
28
+
18
29
  /**
19
30
  * Traverses up or down a React tree, return `true` to stop and select a node.
20
31
  */
@@ -104,7 +115,7 @@ function useTreeNode() {
104
115
  return treeNode;
105
116
  }
106
117
  /**
107
- * Returns a map of all contexts and their values.
118
+ * Returns a map of whitelisted contexts and their values.
108
119
  */
109
120
  function useContextMap() {
110
121
  const treeNode = useTreeNode();
@@ -118,7 +129,7 @@ function useContextMap() {
118
129
  // https://github.com/facebook/react/pull/28226
119
130
  const enableRenderableContext = node.type._context === undefined && node.type.Provider === node.type;
120
131
  const context = enableRenderableContext ? node.type : node.type._context;
121
- if (context && context !== TreeNodeContext && !contextMap.has(context)) {
132
+ if (context && context !== TreeNodeContext && BRIDGED_CONTEXTS.includes(context) && !contextMap.has(context)) {
122
133
  // eslint-disable-next-line react-hooks/rules-of-hooks
123
134
  contextMap.set(context, React.useContext(wrapContext(context)));
124
135
  }
@@ -1,9 +1,9 @@
1
1
  import { createContext, useContext } from 'react';
2
- const CartesianChartContext = /*#__PURE__*/createContext(undefined);
2
+ export const CartesianChartContext = /*#__PURE__*/createContext(undefined);
3
3
  export const useCartesianChartContext = () => {
4
4
  const context = useContext(CartesianChartContext);
5
5
  if (!context) {
6
- throw new Error('useCartesianChartContext must be used within a CartesianChart component. See http://cds.coinbase.com/components/graphs/CartesianChart.');
6
+ throw new Error('useCartesianChartContext must be used within a CartesianChart component. See https://cds.coinbase.com/components/charts/CartesianChart.');
7
7
  }
8
8
  return context;
9
9
  };
package/esm/chart/Path.js CHANGED
@@ -1,11 +1,12 @@
1
- const _excluded = ["d", "initialPath", "fill", "fillOpacity", "stroke", "strokeOpacity", "strokeWidth", "strokeCap", "strokeJoin", "children", "transition"],
2
- _excluded2 = ["animate", "clipRect", "clipPath", "clipOffset", "d", "initialPath", "fill", "fillOpacity", "stroke", "strokeOpacity", "strokeWidth", "strokeCap", "strokeJoin", "children", "transition"];
1
+ const _excluded = ["d", "initialPath", "fill", "fillOpacity", "stroke", "strokeOpacity", "strokeWidth", "strokeCap", "strokeJoin", "children", "transitions"],
2
+ _excluded2 = ["animate", "clipRect", "clipPath", "clipOffset", "d", "initialPath", "fill", "fillOpacity", "stroke", "strokeOpacity", "strokeWidth", "strokeCap", "strokeJoin", "children", "transitions", "transition"];
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
- import { memo, useEffect, useMemo } from 'react';
6
- import { useDerivedValue, useSharedValue, withTiming } from 'react-native-reanimated';
5
+ import { memo, useEffect, useMemo, useRef } from 'react';
6
+ import { useDerivedValue, useSharedValue } from 'react-native-reanimated';
7
7
  import { Group, Path as SkiaPath, Skia, usePathInterpolation } from '@shopify/react-native-skia';
8
- import { usePathTransition } from './utils/transition';
8
+ import { defaultPathEnterTransition } from './utils/path';
9
+ import { buildTransition, defaultTransition, getTransition, usePathTransition } from './utils/transition';
9
10
  import { useCartesianChartContext } from './ChartProvider';
10
11
  import { unwrapAnimatedValue } from './utils';
11
12
 
@@ -26,17 +27,14 @@ const AnimatedPath = /*#__PURE__*/memo(_ref => {
26
27
  strokeCap,
27
28
  strokeJoin,
28
29
  children,
29
- transition
30
+ transitions
30
31
  } = _ref,
31
32
  pathProps = _objectWithoutPropertiesLoose(_ref, _excluded);
32
33
  const isDAnimated = typeof d !== 'string';
33
-
34
- // When d is animated, usePathTransition handles static path transitions.
35
- // For animated d values, we skip usePathTransition and use useDerivedValue directly.
36
34
  const animatedPath = usePathTransition({
37
35
  currentPath: isDAnimated ? '' : d,
38
36
  initialPath,
39
- transition
37
+ transitions
40
38
  });
41
39
  const isFilled = fill !== undefined && fill !== 'none';
42
40
  const isStroked = stroke !== undefined && stroke !== 'none';
@@ -84,28 +82,56 @@ export const Path = /*#__PURE__*/memo(props => {
84
82
  strokeCap,
85
83
  strokeJoin,
86
84
  children,
85
+ transitions,
87
86
  transition
88
87
  } = props,
89
88
  pathProps = _objectWithoutPropertiesLoose(props, _excluded2);
90
89
  const context = useCartesianChartContext();
91
90
  const rect = clipRect != null ? clipRect : context.drawingArea;
92
91
  const animate = animateProp != null ? animateProp : context.animate;
92
+ const isReady = !!context.getXScale();
93
+ const enterTransition = useMemo(() => getTransition(transitions == null ? void 0 : transitions.enter, animate, defaultPathEnterTransition), [animate, transitions == null ? void 0 : transitions.enter]);
94
+ const updateTransition = useMemo(() => getTransition((transitions == null ? void 0 : transitions.update) !== undefined ? transitions.update : transition, animate, defaultTransition), [animate, transitions == null ? void 0 : transitions.update, transition]);
95
+ const enterOpacityTransition = useMemo(() => {
96
+ if (!animate) return null;
97
+ return transitions == null ? void 0 : transitions.enterOpacity;
98
+ }, [animate, transitions == null ? void 0 : transitions.enterOpacity]);
99
+ const animateEnterOpacity = Boolean(enterOpacityTransition);
100
+ const enterOpacity = useSharedValue(animateEnterOpacity ? 0 : 1);
101
+ const hasAnimatedEnterOpacity = useRef(false);
102
+ useEffect(() => {
103
+ if (hasAnimatedEnterOpacity.current) {
104
+ return;
105
+ }
106
+ if (!animateEnterOpacity) {
107
+ hasAnimatedEnterOpacity.current = true;
108
+ enterOpacity.value = 1;
109
+ return;
110
+ }
111
+ if (!isReady) {
112
+ return;
113
+ }
114
+ if (enterOpacityTransition === undefined || enterOpacityTransition === null) {
115
+ enterOpacity.value = 1;
116
+ hasAnimatedEnterOpacity.current = true;
117
+ return;
118
+ }
119
+ hasAnimatedEnterOpacity.current = true;
120
+ enterOpacity.value = buildTransition(1, enterOpacityTransition);
121
+ }, [animateEnterOpacity, isReady, enterOpacityTransition, enterOpacity]);
122
+ const animateClip = animate && enterTransition !== null;
93
123
 
94
124
  // The clip offset provides extra padding to prevent path from being cut off
95
125
  // Area charts typically use offset=0 for exact clipping, while lines use offset=2 for breathing room
96
126
  const totalOffset = clipOffset * 2; // Applied on both sides
97
127
 
98
128
  // Animation progress for clip path reveal
99
- const clipProgress = useSharedValue(animate ? 0 : 1);
100
-
101
- // Trigger clip path animation when component mounts and animate is true
129
+ const clipProgress = useSharedValue(animateClip ? 0 : 1);
102
130
  useEffect(() => {
103
- if (animate) {
104
- clipProgress.value = withTiming(1, {
105
- duration: pathEnterTransitionDuration
106
- });
131
+ if (animateClip && isReady) {
132
+ clipProgress.value = buildTransition(1, enterTransition);
107
133
  }
108
- }, [animate, clipProgress]);
134
+ }, [animateClip, isReady, clipProgress, enterTransition]);
109
135
 
110
136
  // Create initial and target clip paths for animation
111
137
  const {
@@ -116,32 +142,35 @@ export const Path = /*#__PURE__*/memo(props => {
116
142
  initialClipPath: null,
117
143
  targetClipPath: null
118
144
  };
145
+ const categoryAxisIsX = context.layout !== 'horizontal';
146
+ const fullWidth = rect.width + totalOffset;
147
+ const fullHeight = rect.height + totalOffset;
119
148
 
120
- // Initial clip path (width = 0)
149
+ // Initial clip path starts collapsed on the category axis.
121
150
  const initial = Skia.Path.Make();
122
151
  initial.addRect({
123
152
  x: rect.x - clipOffset,
124
153
  y: rect.y - clipOffset,
125
- width: 0,
126
- height: rect.height + totalOffset
154
+ width: categoryAxisIsX ? 0 : fullWidth,
155
+ height: categoryAxisIsX ? fullHeight : 0
127
156
  });
128
157
 
129
- // Target clip path (full width)
158
+ // Target clip path is fully expanded.
130
159
  const target = Skia.Path.Make();
131
160
  target.addRect({
132
161
  x: rect.x - clipOffset,
133
162
  y: rect.y - clipOffset,
134
- width: rect.width + totalOffset,
135
- height: rect.height + totalOffset
163
+ width: fullWidth,
164
+ height: fullHeight
136
165
  });
137
166
  return {
138
167
  initialClipPath: initial,
139
168
  targetClipPath: target
140
169
  };
141
- }, [rect, clipOffset, totalOffset]);
170
+ }, [rect, clipOffset, totalOffset, context.layout]);
142
171
 
143
172
  // Use usePathInterpolation for animated clip path
144
- const animatedClipPath = usePathInterpolation(clipProgress, [0, 1], animate && initialClipPath && targetClipPath ? [initialClipPath, targetClipPath] : targetClipPath ? [targetClipPath, targetClipPath] : [Skia.Path.Make(), Skia.Path.Make()]);
173
+ const animatedClipPath = usePathInterpolation(clipProgress, [0, 1], animateClip && initialClipPath && targetClipPath ? [initialClipPath, targetClipPath] : targetClipPath ? [targetClipPath, targetClipPath] : [Skia.Path.Make(), Skia.Path.Make()]);
145
174
 
146
175
  // Resolve the final clip path:
147
176
  // 1. If clipPath prop was explicitly provided, use it (even if null = no clipping)
@@ -154,13 +183,13 @@ export const Path = /*#__PURE__*/memo(props => {
154
183
  }
155
184
 
156
185
  // If not animating or paths are null, return target clip path
157
- if (!animate || !targetClipPath) {
186
+ if (!animateClip || !targetClipPath) {
158
187
  return targetClipPath;
159
188
  }
160
189
 
161
190
  // Return undefined here since we'll use animatedClipPath directly
162
191
  return undefined;
163
- }, [clipPathProp, animate, targetClipPath]);
192
+ }, [clipPathProp, animateClip, targetClipPath]);
164
193
 
165
194
  // Convert SVG path string to SkPath for static rendering
166
195
  const staticPath = useDerivedValue(() => {
@@ -200,19 +229,27 @@ export const Path = /*#__PURE__*/memo(props => {
200
229
  strokeJoin: strokeJoin,
201
230
  strokeOpacity: strokeOpacity,
202
231
  strokeWidth: strokeWidth,
203
- transition: transition,
232
+ transitions: {
233
+ enter: enterTransition,
234
+ enterOpacity: enterOpacityTransition,
235
+ update: updateTransition
236
+ },
204
237
  children: children
205
238
  });
206
239
 
207
240
  // Determine which clip path to use
208
- const finalClipPath = animate && resolvedClipPath === undefined ? animatedClipPath : resolvedClipPath;
241
+ const finalClipPath = animateClip && resolvedClipPath === undefined ? animatedClipPath : resolvedClipPath;
209
242
 
210
243
  // If finalClipPath is null, render without clipping
211
244
  if (finalClipPath === null) {
212
- return content;
245
+ return /*#__PURE__*/_jsx(Group, {
246
+ opacity: animateEnterOpacity ? enterOpacity : undefined,
247
+ children: content
248
+ });
213
249
  }
214
250
  return /*#__PURE__*/_jsx(Group, {
215
251
  clip: finalClipPath,
252
+ opacity: animateEnterOpacity ? enterOpacity : undefined,
216
253
  children: content
217
254
  });
218
255
  });
@@ -24,7 +24,8 @@ export const PeriodSelectorActiveIndicator = _ref => {
24
24
  const {
25
25
  width,
26
26
  height,
27
- x
27
+ x,
28
+ y
28
29
  } = activeTabRect;
29
30
 
30
31
  // Get the target background color
@@ -38,6 +39,7 @@ export const PeriodSelectorActiveIndicator = _ref => {
38
39
  // Combined animated value for position, size, and color
39
40
  const newAnimatedValues = {
40
41
  x,
42
+ y,
41
43
  width,
42
44
  backgroundColor: targetColor
43
45
  };
@@ -51,6 +53,8 @@ export const PeriodSelectorActiveIndicator = _ref => {
51
53
  const animatedStyles = useAnimatedStyle(() => ({
52
54
  transform: [{
53
55
  translateX: animatedValues.value.x
56
+ }, {
57
+ translateY: animatedValues.value.y
54
58
  }],
55
59
  width: animatedValues.value.width,
56
60
  backgroundColor: animatedValues.value.backgroundColor
@@ -7,7 +7,7 @@ import { Example, ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScr
7
7
  import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
8
8
  import { Box, HStack, VStack } from '@coinbase/cds-mobile/layout';
9
9
  import { Text } from '@coinbase/cds-mobile/typography';
10
- import { Circle, Group, Skia } from '@shopify/react-native-skia';
10
+ import { Circle, Group } from '@shopify/react-native-skia';
11
11
  import { Area } from '../area/Area';
12
12
  import { XAxis, YAxis } from '../axis';
13
13
  import { BarPlot } from '../bar/BarPlot';
@@ -155,7 +155,7 @@ const EarningsHistory = () => {
155
155
  backgroundColor: theme.color.bgPositive
156
156
  }
157
157
  });
158
- const LegendItem = /*#__PURE__*/memo(_ref2 => {
158
+ const LegendEntry = /*#__PURE__*/memo(_ref2 => {
159
159
  let {
160
160
  opacity = 1,
161
161
  label
@@ -217,10 +217,10 @@ const EarningsHistory = () => {
217
217
  }), /*#__PURE__*/_jsxs(HStack, {
218
218
  gap: 2,
219
219
  justifyContent: "flex-end",
220
- children: [/*#__PURE__*/_jsx(LegendItem, {
220
+ children: [/*#__PURE__*/_jsx(LegendEntry, {
221
221
  label: "Estimated EPS",
222
222
  opacity: 0.5
223
- }), /*#__PURE__*/_jsx(LegendItem, {
223
+ }), /*#__PURE__*/_jsx(LegendEntry, {
224
224
  label: "Actual EPS"
225
225
  })]
226
226
  })]
@@ -250,11 +250,21 @@ const PriceWithVolumeChart = /*#__PURE__*/memo(_ref3 => {
250
250
  day: 'numeric'
251
251
  });
252
252
  }, []);
253
+ const formatVolume = useCallback(volume => {
254
+ return (volume / 1000).toFixed(2) + "K";
255
+ }, []);
253
256
  const scrubberLabel = useCallback(dataIndex => {
254
257
  return formatDate(btcDates[dataIndex]);
255
258
  }, [formatDate]);
259
+ const chartAccessibilityLabel = useMemo(() => {
260
+ const lastIndex = btcPrices.length - 1;
261
+ return "Bitcoin chart. Current date " + formatDate(btcDates[lastIndex]) + ". Current price " + formatPriceInThousands(btcPrices[lastIndex]) + ". Current volume " + formatVolume(btcVolumes[lastIndex]) + ".";
262
+ }, [formatDate, formatPriceInThousands, formatVolume]);
263
+ const getScrubberAccessibilityLabel = useCallback(dataIndex => "Bitcoin on " + formatDate(btcDates[dataIndex]) + ". Price " + formatPriceInThousands(btcPrices[dataIndex]) + ". Volume " + formatVolume(btcVolumes[dataIndex]) + ".", [formatDate, formatPriceInThousands, formatVolume]);
256
264
  return /*#__PURE__*/_jsxs(CartesianChart, {
257
265
  enableScrubbing: true,
266
+ accessibilityLabel: chartAccessibilityLabel,
267
+ getScrubberAccessibilityLabel: getScrubberAccessibilityLabel,
258
268
  height: defaultChartHeight,
259
269
  onScrubberPositionChange: onScrubberPositionChange,
260
270
  series: [{
@@ -485,80 +495,6 @@ function TradingTrends() {
485
495
  })]
486
496
  });
487
497
  }
488
- const UVGradient = {
489
- axis: 'y',
490
- stops: [{
491
- offset: 0,
492
- color: 'green'
493
- }, {
494
- offset: 3,
495
- color: 'yellow'
496
- }, {
497
- offset: 5,
498
- color: 'orange'
499
- }, {
500
- offset: 8,
501
- color: 'red'
502
- }, {
503
- offset: 10,
504
- color: 'purple'
505
- }]
506
- };
507
- const PreviousData = /*#__PURE__*/memo(_ref0 => {
508
- let {
509
- children,
510
- currentHour,
511
- clipOffset = 0
512
- } = _ref0;
513
- // we will clip the data to the current hour
514
- const {
515
- drawingArea,
516
- getXScale
517
- } = useCartesianChartContext();
518
- const xScale = getXScale();
519
- const currentHourX = xScale == null ? void 0 : xScale(currentHour);
520
- const clipPath = useMemo(() => {
521
- if (!xScale || currentHourX === undefined) return null;
522
-
523
- // Create a rectangle from top-left of drawing area to currentHourX on the right
524
- // Apply clipOffset to left, top, and bottom edges only (NOT to currentHourX)
525
- const pathString = "M " + (drawingArea.x - clipOffset) + " " + (drawingArea.y - clipOffset) + " L " + currentHourX + " " + (drawingArea.y - clipOffset) + " L " + currentHourX + " " + (drawingArea.y + drawingArea.height + clipOffset) + " L " + (drawingArea.x - clipOffset) + " " + (drawingArea.y + drawingArea.height + clipOffset) + " Z";
526
- return Skia.Path.MakeFromSVGString(pathString);
527
- }, [xScale, currentHourX, drawingArea, clipOffset]);
528
- if (!clipPath) return null;
529
- return /*#__PURE__*/_jsx(Group, {
530
- clip: clipPath,
531
- opacity: 0.75,
532
- children: children
533
- });
534
- });
535
- const FutureData = /*#__PURE__*/memo(_ref1 => {
536
- let {
537
- children,
538
- currentHour,
539
- clipOffset = 0
540
- } = _ref1;
541
- // we will clip the data from the current hour to the right edge
542
- const {
543
- drawingArea,
544
- getXScale
545
- } = useCartesianChartContext();
546
- const xScale = getXScale();
547
- const currentHourX = xScale == null ? void 0 : xScale(currentHour);
548
- const clipPath = useMemo(() => {
549
- if (!xScale || currentHourX === undefined) return null;
550
-
551
- // Create a rectangle from currentHourX to right edge of drawing area
552
- // Apply clipOffset to top, bottom, and right, but NOT left (currentHourX)
553
- const pathString = "M " + currentHourX + " " + (drawingArea.y - clipOffset) + " L " + (drawingArea.x + drawingArea.width + clipOffset) + " " + (drawingArea.y - clipOffset) + " L " + (drawingArea.x + drawingArea.width + clipOffset) + " " + (drawingArea.y + drawingArea.height + clipOffset) + " L " + currentHourX + " " + (drawingArea.y + drawingArea.height + clipOffset) + " Z";
554
- return Skia.Path.MakeFromSVGString(pathString);
555
- }, [xScale, currentHourX, drawingArea, clipOffset]);
556
- if (!clipPath) return null;
557
- return /*#__PURE__*/_jsx(Group, {
558
- clip: clipPath,
559
- children: children
560
- });
561
- });
562
498
  const ScatterplotWithCustomLabels = /*#__PURE__*/memo(() => {
563
499
  const theme = useTheme();
564
500
  const dataPoints = useMemo(() => [{
@@ -642,13 +578,13 @@ const ScatterplotWithCustomLabels = /*#__PURE__*/memo(() => {
642
578
  const yMax = Math.max(...yValues);
643
579
 
644
580
  // Custom label component that places labels to the top-right
645
- const TopRightPointLabel = useCallback(_ref10 => {
581
+ const TopRightPointLabel = useCallback(_ref0 => {
646
582
  let {
647
583
  x,
648
584
  y,
649
585
  offset = 0,
650
586
  children
651
- } = _ref10;
587
+ } = _ref0;
652
588
  return /*#__PURE__*/_jsx(ChartText, {
653
589
  font: "label1",
654
590
  fontWeight: 600,