@coinbase/cds-mobile-visualization 3.4.0-beta.2 → 3.4.0-beta.20

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 (211) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dts/chart/CartesianChart.d.ts +92 -34
  3. package/dts/chart/CartesianChart.d.ts.map +1 -1
  4. package/dts/chart/ChartContextBridge.d.ts +28 -0
  5. package/dts/chart/ChartContextBridge.d.ts.map +1 -0
  6. package/dts/chart/ChartProvider.d.ts +3 -0
  7. package/dts/chart/ChartProvider.d.ts.map +1 -1
  8. package/dts/chart/Path.d.ts +97 -32
  9. package/dts/chart/Path.d.ts.map +1 -1
  10. package/dts/chart/PeriodSelector.d.ts +6 -13
  11. package/dts/chart/PeriodSelector.d.ts.map +1 -1
  12. package/dts/chart/area/Area.d.ts +39 -28
  13. package/dts/chart/area/Area.d.ts.map +1 -1
  14. package/dts/chart/area/AreaChart.d.ts +51 -10
  15. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  16. package/dts/chart/area/DottedArea.d.ts +21 -2
  17. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  18. package/dts/chart/area/GradientArea.d.ts +19 -13
  19. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  20. package/dts/chart/area/SolidArea.d.ts +17 -2
  21. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  22. package/dts/chart/axis/Axis.d.ts +86 -118
  23. package/dts/chart/axis/Axis.d.ts.map +1 -1
  24. package/dts/chart/axis/DefaultAxisTickLabel.d.ts +8 -0
  25. package/dts/chart/axis/DefaultAxisTickLabel.d.ts.map +1 -0
  26. package/dts/chart/axis/XAxis.d.ts +1 -1
  27. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  28. package/dts/chart/axis/YAxis.d.ts +2 -2
  29. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  30. package/dts/chart/axis/index.d.ts +1 -0
  31. package/dts/chart/axis/index.d.ts.map +1 -1
  32. package/dts/chart/bar/Bar.d.ts +49 -12
  33. package/dts/chart/bar/Bar.d.ts.map +1 -1
  34. package/dts/chart/bar/BarChart.d.ts +40 -19
  35. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  36. package/dts/chart/bar/BarPlot.d.ts +3 -1
  37. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  38. package/dts/chart/bar/BarStack.d.ts +41 -46
  39. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  40. package/dts/chart/bar/BarStackGroup.d.ts +2 -0
  41. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  42. package/dts/chart/bar/DefaultBar.d.ts +1 -1
  43. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  44. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  45. package/dts/chart/gradient/Gradient.d.ts +25 -0
  46. package/dts/chart/gradient/Gradient.d.ts.map +1 -0
  47. package/dts/chart/gradient/index.d.ts +2 -0
  48. package/dts/chart/gradient/index.d.ts.map +1 -0
  49. package/dts/chart/index.d.ts +4 -1
  50. package/dts/chart/index.d.ts.map +1 -1
  51. package/dts/chart/legend/DefaultLegendEntry.d.ts +5 -0
  52. package/dts/chart/legend/DefaultLegendEntry.d.ts.map +1 -0
  53. package/dts/chart/legend/DefaultLegendShape.d.ts +5 -0
  54. package/dts/chart/legend/DefaultLegendShape.d.ts.map +1 -0
  55. package/dts/chart/legend/Legend.d.ts +168 -0
  56. package/dts/chart/legend/Legend.d.ts.map +1 -0
  57. package/dts/chart/legend/index.d.ts +4 -0
  58. package/dts/chart/legend/index.d.ts.map +1 -0
  59. package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
  60. package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
  61. package/dts/chart/line/DottedLine.d.ts +13 -5
  62. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  63. package/dts/chart/line/Line.d.ts +61 -27
  64. package/dts/chart/line/Line.d.ts.map +1 -1
  65. package/dts/chart/line/LineChart.d.ts +43 -9
  66. package/dts/chart/line/LineChart.d.ts.map +1 -1
  67. package/dts/chart/line/ReferenceLine.d.ts +68 -20
  68. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  69. package/dts/chart/line/SolidLine.d.ts +8 -5
  70. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  71. package/dts/chart/line/index.d.ts +1 -1
  72. package/dts/chart/line/index.d.ts.map +1 -1
  73. package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
  74. package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
  75. package/dts/chart/point/Point.d.ts +136 -0
  76. package/dts/chart/point/Point.d.ts.map +1 -0
  77. package/dts/chart/point/index.d.ts +3 -0
  78. package/dts/chart/point/index.d.ts.map +1 -0
  79. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +38 -0
  80. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
  81. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
  82. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
  83. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +11 -0
  84. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
  85. package/dts/chart/scrubber/Scrubber.d.ts +230 -42
  86. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  87. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +54 -0
  88. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
  89. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +46 -0
  90. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
  91. package/dts/chart/scrubber/ScrubberProvider.d.ts +6 -3
  92. package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
  93. package/dts/chart/scrubber/index.d.ts +3 -0
  94. package/dts/chart/scrubber/index.d.ts.map +1 -1
  95. package/dts/chart/text/ChartText.d.ts +151 -77
  96. package/dts/chart/text/ChartText.d.ts.map +1 -1
  97. package/dts/chart/text/{SmartChartTextGroup.d.ts → ChartTextGroup.d.ts} +9 -3
  98. package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
  99. package/dts/chart/text/index.d.ts +1 -1
  100. package/dts/chart/text/index.d.ts.map +1 -1
  101. package/dts/chart/utils/axis.d.ts +25 -1
  102. package/dts/chart/utils/axis.d.ts.map +1 -1
  103. package/dts/chart/utils/bar.d.ts +34 -0
  104. package/dts/chart/utils/bar.d.ts.map +1 -1
  105. package/dts/chart/utils/chart.d.ts +52 -7
  106. package/dts/chart/utils/chart.d.ts.map +1 -1
  107. package/dts/chart/utils/context.d.ts +28 -7
  108. package/dts/chart/utils/context.d.ts.map +1 -1
  109. package/dts/chart/utils/gradient.d.ts +117 -0
  110. package/dts/chart/utils/gradient.d.ts.map +1 -0
  111. package/dts/chart/utils/index.d.ts +3 -0
  112. package/dts/chart/utils/index.d.ts.map +1 -1
  113. package/dts/chart/utils/path.d.ts +59 -0
  114. package/dts/chart/utils/path.d.ts.map +1 -1
  115. package/dts/chart/utils/point.d.ts +71 -7
  116. package/dts/chart/utils/point.d.ts.map +1 -1
  117. package/dts/chart/utils/scale.d.ts +102 -0
  118. package/dts/chart/utils/scale.d.ts.map +1 -1
  119. package/dts/chart/utils/scrubber.d.ts +40 -0
  120. package/dts/chart/utils/scrubber.d.ts.map +1 -0
  121. package/dts/chart/utils/transition.d.ts +178 -0
  122. package/dts/chart/utils/transition.d.ts.map +1 -0
  123. package/esm/chart/CartesianChart.js +199 -75
  124. package/esm/chart/ChartContextBridge.js +159 -0
  125. package/esm/chart/ChartProvider.js +2 -2
  126. package/esm/chart/Path.js +200 -114
  127. package/esm/chart/PeriodSelector.js +7 -3
  128. package/esm/chart/__stories__/CartesianChart.stories.js +307 -134
  129. package/esm/chart/__stories__/ChartTransitions.stories.js +629 -0
  130. package/esm/chart/__stories__/PeriodSelector.stories.js +201 -75
  131. package/esm/chart/area/Area.js +27 -35
  132. package/esm/chart/area/AreaChart.js +17 -12
  133. package/esm/chart/area/DottedArea.js +64 -108
  134. package/esm/chart/area/GradientArea.js +37 -91
  135. package/esm/chart/area/SolidArea.js +24 -8
  136. package/esm/chart/area/__stories__/AreaChart.stories.js +1 -1
  137. package/esm/chart/axis/Axis.js +5 -39
  138. package/esm/chart/axis/DefaultAxisTickLabel.js +11 -0
  139. package/esm/chart/axis/XAxis.js +148 -66
  140. package/esm/chart/axis/YAxis.js +149 -65
  141. package/esm/chart/axis/__stories__/Axis.stories.js +259 -1
  142. package/esm/chart/axis/index.js +1 -0
  143. package/esm/chart/bar/Bar.js +7 -1
  144. package/esm/chart/bar/BarChart.js +17 -37
  145. package/esm/chart/bar/BarPlot.js +43 -35
  146. package/esm/chart/bar/BarStack.js +84 -37
  147. package/esm/chart/bar/BarStackGroup.js +7 -17
  148. package/esm/chart/bar/DefaultBar.js +29 -51
  149. package/esm/chart/bar/DefaultBarStack.js +34 -58
  150. package/esm/chart/bar/__stories__/BarChart.stories.js +948 -88
  151. package/esm/chart/gradient/Gradient.js +53 -0
  152. package/esm/chart/gradient/index.js +1 -0
  153. package/esm/chart/index.js +4 -1
  154. package/esm/chart/legend/DefaultLegendEntry.js +42 -0
  155. package/esm/chart/legend/DefaultLegendShape.js +64 -0
  156. package/esm/chart/legend/Legend.js +59 -0
  157. package/esm/chart/legend/__stories__/Legend.stories.js +574 -0
  158. package/esm/chart/legend/index.js +3 -0
  159. package/esm/chart/line/DefaultReferenceLineLabel.js +66 -0
  160. package/esm/chart/line/DottedLine.js +31 -14
  161. package/esm/chart/line/Line.js +96 -68
  162. package/esm/chart/line/LineChart.js +21 -14
  163. package/esm/chart/line/ReferenceLine.js +80 -63
  164. package/esm/chart/line/SolidLine.js +27 -10
  165. package/esm/chart/line/__stories__/LineChart.stories.js +1748 -2048
  166. package/esm/chart/line/__stories__/ReferenceLine.stories.js +177 -28
  167. package/esm/chart/line/index.js +1 -1
  168. package/esm/chart/point/DefaultPointLabel.js +39 -0
  169. package/esm/chart/point/Point.js +186 -0
  170. package/esm/chart/point/index.js +2 -0
  171. package/esm/chart/scrubber/DefaultScrubberBeacon.js +180 -0
  172. package/esm/chart/scrubber/DefaultScrubberBeaconLabel.js +43 -0
  173. package/esm/chart/scrubber/DefaultScrubberLabel.js +28 -0
  174. package/esm/chart/scrubber/Scrubber.js +130 -144
  175. package/esm/chart/scrubber/ScrubberBeaconGroup.js +165 -0
  176. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +208 -0
  177. package/esm/chart/scrubber/ScrubberProvider.js +46 -54
  178. package/esm/chart/scrubber/__stories__/Scrubber.stories.js +760 -0
  179. package/esm/chart/scrubber/index.js +3 -1
  180. package/esm/chart/text/ChartText.js +242 -174
  181. package/esm/chart/text/{SmartChartTextGroup.js → ChartTextGroup.js} +6 -5
  182. package/esm/chart/text/index.js +1 -1
  183. package/esm/chart/utils/axis.js +47 -31
  184. package/esm/chart/utils/bar.js +43 -0
  185. package/esm/chart/utils/chart.js +57 -3
  186. package/esm/chart/utils/gradient.js +305 -0
  187. package/esm/chart/utils/index.js +3 -0
  188. package/esm/chart/utils/path.js +84 -8
  189. package/esm/chart/utils/point.js +171 -17
  190. package/esm/chart/utils/scale.js +242 -2
  191. package/esm/chart/utils/scrubber.js +146 -0
  192. package/esm/chart/utils/transition.js +220 -0
  193. package/esm/sparkline/__figma__/Sparkline.figma.js +1 -1
  194. package/esm/sparkline/__stories__/Sparkline.stories.js +11 -7
  195. package/esm/sparkline/__stories__/SparklineGradient.stories.js +7 -4
  196. package/esm/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.js +1 -1
  197. package/esm/sparkline/sparkline-interactive/__stories__/SparklineInteractive.stories.js +51 -26
  198. package/esm/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.js +1 -1
  199. package/esm/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.js +17 -9
  200. package/package.json +15 -10
  201. package/dts/chart/Point.d.ts +0 -103
  202. package/dts/chart/Point.d.ts.map +0 -1
  203. package/dts/chart/line/GradientLine.d.ts +0 -45
  204. package/dts/chart/line/GradientLine.d.ts.map +0 -1
  205. package/dts/chart/scrubber/ScrubberBeacon.d.ts +0 -75
  206. package/dts/chart/scrubber/ScrubberBeacon.d.ts.map +0 -1
  207. package/dts/chart/text/SmartChartTextGroup.d.ts.map +0 -1
  208. package/esm/chart/Point.js +0 -111
  209. package/esm/chart/__stories__/Chart.stories.js +0 -79
  210. package/esm/chart/line/GradientLine.js +0 -62
  211. package/esm/chart/scrubber/ScrubberBeacon.js +0 -199
@@ -1,16 +1,68 @@
1
+ const _excluded = ["accentColor", "yellowColor"];
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
+ 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
+ import { memo, useCallback, useMemo } from 'react';
5
+ import { useDerivedValue, withTiming } from 'react-native-reanimated';
6
+ import { sparklineInteractiveData } from '@coinbase/cds-common/internal/visualizations/SparklineInteractiveData';
1
7
  import { useTheme } from '@coinbase/cds-mobile';
2
8
  import { Example, ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
9
+ import { useCartesianChartContext } from '../../ChartProvider';
10
+ import { Scrubber } from '../../scrubber';
11
+ import { getPointOnSerializableScale, useScrubberContext } from '../../utils';
12
+ import { DefaultReferenceLineLabel } from '../DefaultReferenceLineLabel';
13
+ import { DottedLine } from '../DottedLine';
3
14
  import { LineChart } from '../LineChart';
4
15
  import { ReferenceLine } from '../ReferenceLine';
5
16
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
+ const LiquidationLabelMobile = /*#__PURE__*/memo(_ref => {
18
+ let {
19
+ accentColor,
20
+ yellowColor
21
+ } = _ref,
22
+ props = _objectWithoutPropertiesLoose(_ref, _excluded);
23
+ return /*#__PURE__*/_jsx(DefaultReferenceLineLabel, _extends({}, props, {
24
+ background: accentColor,
25
+ borderRadius: 100,
26
+ color: "rgb(" + yellowColor + ")",
27
+ horizontalAlignment: "left",
28
+ inset: {
29
+ top: 4,
30
+ bottom: 4,
31
+ left: 8,
32
+ right: 8
33
+ }
34
+ }));
35
+ });
6
36
  const ReferenceLineStories = () => {
7
37
  const theme = useTheme();
38
+ const liquidationLabelComponent = useCallback(props => /*#__PURE__*/_jsx(LiquidationLabelMobile, _extends({}, props, {
39
+ accentColor: theme.color.accentSubtleYellow,
40
+ yellowColor: theme.spectrum.yellow70
41
+ })), [theme.color.accentSubtleYellow, theme.spectrum.yellow70]);
8
42
  return /*#__PURE__*/_jsxs(ExampleScreen, {
9
43
  children: [/*#__PURE__*/_jsx(Example, {
10
- title: "Basic",
44
+ title: "Simple Reference Line",
45
+ children: /*#__PURE__*/_jsx(LineChart, {
46
+ showArea: true,
47
+ height: 250,
48
+ series: [{
49
+ id: 'prices',
50
+ data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
51
+ color: theme.color.fgPositive
52
+ }],
53
+ children: /*#__PURE__*/_jsx(ReferenceLine, {
54
+ LineComponent: props => /*#__PURE__*/_jsx(DottedLine, _extends({}, props, {
55
+ dashIntervals: [0, 16],
56
+ strokeWidth: 3
57
+ })),
58
+ dataY: 10,
59
+ stroke: theme.color.fg
60
+ })
61
+ })
62
+ }), /*#__PURE__*/_jsx(Example, {
63
+ title: "With Labels",
11
64
  children: /*#__PURE__*/_jsxs(LineChart, {
12
65
  showArea: true,
13
- curve: "monotone",
14
66
  height: 250,
15
67
  inset: 0,
16
68
  series: [{
@@ -20,26 +72,46 @@ const ReferenceLineStories = () => {
20
72
  children: [/*#__PURE__*/_jsx(ReferenceLine, {
21
73
  dataX: 4,
22
74
  label: "Vertical Reference Line",
23
- labelProps: {
24
- horizontalAlignment: 'left',
25
- dx: 6,
26
- inset: 0
27
- }
75
+ labelHorizontalAlignment: "left"
28
76
  }), /*#__PURE__*/_jsx(ReferenceLine, {
29
77
  dataY: 70,
30
78
  label: "Horizontal Reference Line",
31
- labelProps: {
32
- verticalAlignment: 'bottom',
33
- dy: -6,
34
- horizontalAlignment: 'right',
35
- inset: 0
36
- }
79
+ labelHorizontalAlignment: "right",
80
+ labelVerticalAlignment: "bottom"
37
81
  })]
38
82
  })
39
83
  }), /*#__PURE__*/_jsx(Example, {
40
- title: "With Custom Label",
84
+ title: "Label Customization",
85
+ children: /*#__PURE__*/_jsxs(LineChart, {
86
+ showArea: true,
87
+ height: 250,
88
+ series: [{
89
+ id: 'prices',
90
+ data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58]
91
+ }],
92
+ children: [/*#__PURE__*/_jsx(ReferenceLine, {
93
+ dataY: 75,
94
+ label: "Top Right",
95
+ labelDx: -8,
96
+ labelDy: -8,
97
+ labelFont: "label1",
98
+ labelHorizontalAlignment: "right",
99
+ labelPosition: "right",
100
+ labelVerticalAlignment: "bottom"
101
+ }), /*#__PURE__*/_jsx(ReferenceLine, {
102
+ dataX: 7,
103
+ label: "Bottom Left",
104
+ labelDx: 8,
105
+ labelDy: 8,
106
+ labelFont: "label1",
107
+ labelHorizontalAlignment: "left",
108
+ labelPosition: "top",
109
+ labelVerticalAlignment: "top"
110
+ })]
111
+ })
112
+ }), /*#__PURE__*/_jsx(Example, {
113
+ title: "With Custom Label Component",
41
114
  children: /*#__PURE__*/_jsx(LineChart, {
42
- curve: "monotone",
43
115
  height: 250,
44
116
  inset: {
45
117
  right: 32,
@@ -52,26 +124,103 @@ const ReferenceLineStories = () => {
52
124
  data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58]
53
125
  }],
54
126
  children: /*#__PURE__*/_jsx(ReferenceLine, {
127
+ LabelComponent: liquidationLabelComponent,
55
128
  dataY: 25,
56
129
  label: "Liquidation",
57
130
  labelPosition: "left",
58
- labelProps: {
59
- dx: 4,
60
- borderRadius: 100,
61
- inset: {
62
- top: 4,
63
- bottom: 4,
64
- left: 8,
65
- right: 8
66
- },
67
- horizontalAlignment: 'left',
68
- color: "rgb(" + theme.spectrum.yellow70 + ")",
69
- background: theme.color.accentSubtleYellow
70
- },
71
131
  stroke: theme.color.bgWarning
72
132
  })
73
133
  })
134
+ }), /*#__PURE__*/_jsx(Example, {
135
+ title: "Start Price Reference Line",
136
+ children: /*#__PURE__*/_jsx(StartPriceReferenceLine, {})
74
137
  })]
75
138
  });
76
139
  };
140
+ const FADE_ZONE = 128;
141
+ const StartPriceLabel = /*#__PURE__*/memo(props => {
142
+ const theme = useTheme();
143
+ const {
144
+ scrubberPosition
145
+ } = useScrubberContext();
146
+ const {
147
+ getXSerializableScale,
148
+ drawingArea
149
+ } = useCartesianChartContext();
150
+ const xScale = useMemo(() => getXSerializableScale(), [getXSerializableScale]);
151
+ const opacity = useDerivedValue(() => {
152
+ if (scrubberPosition.value === undefined) return withTiming(0, {
153
+ duration: 250
154
+ });
155
+ if (!xScale) return withTiming(1, {
156
+ duration: 250
157
+ });
158
+ const scrubX = getPointOnSerializableScale(scrubberPosition.value, xScale);
159
+ const rightEdge = drawingArea.x + drawingArea.width;
160
+ const target = rightEdge - scrubX >= FADE_ZONE ? 1 : 0;
161
+ return withTiming(target, {
162
+ duration: 250
163
+ });
164
+ }, [scrubberPosition, xScale, drawingArea]);
165
+ return /*#__PURE__*/_jsx(DefaultReferenceLineLabel, _extends({}, props, {
166
+ background: theme.color.bgSecondary,
167
+ borderRadius: 12.5,
168
+ color: theme.color.fg,
169
+ font: "label1",
170
+ inset: {
171
+ top: 4,
172
+ bottom: 4,
173
+ left: 8,
174
+ right: 8
175
+ },
176
+ opacity: opacity
177
+ }));
178
+ });
179
+ function StartPriceReferenceLine() {
180
+ const theme = useTheme();
181
+ const hourData = useMemo(() => sparklineInteractiveData.hour, []);
182
+ const startPrice = hourData[0].value;
183
+ const endPrice = hourData[hourData.length - 1].value;
184
+ const isPositive = endPrice >= startPrice;
185
+ const seriesColor = isPositive ? theme.color.fgPositive : theme.color.fgNegative;
186
+ return /*#__PURE__*/_jsxs(LineChart, {
187
+ enableScrubbing: true,
188
+ showArea: true,
189
+ areaType: "dotted",
190
+ height: 300,
191
+ inset: 0,
192
+ series: [{
193
+ id: 'hourly-prices',
194
+ data: hourData.map(d => d.value),
195
+ color: seriesColor
196
+ }],
197
+ xAxis: {
198
+ range: _ref2 => {
199
+ let {
200
+ min,
201
+ max
202
+ } = _ref2;
203
+ return {
204
+ min,
205
+ max: max - 24
206
+ };
207
+ }
208
+ },
209
+ children: [/*#__PURE__*/_jsx(Scrubber, {}), /*#__PURE__*/_jsx(ReferenceLine, {
210
+ LabelComponent: StartPriceLabel,
211
+ LineComponent: props => /*#__PURE__*/_jsx(DottedLine, _extends({}, props, {
212
+ dashIntervals: [0, 16],
213
+ strokeWidth: 3
214
+ })),
215
+ dataY: startPrice,
216
+ label: startPrice.toLocaleString('en-US', {
217
+ minimumFractionDigits: 2,
218
+ maximumFractionDigits: 2
219
+ }),
220
+ labelDx: -12,
221
+ labelHorizontalAlignment: "right",
222
+ stroke: theme.color.fgMuted
223
+ })]
224
+ });
225
+ }
77
226
  export default ReferenceLineStories;
@@ -1,6 +1,6 @@
1
1
  // codegen:start {preset: barrel, include: ./*.tsx, exclude: ./__stories__/*.tsx}
2
+ export * from './DefaultReferenceLineLabel';
2
3
  export * from './DottedLine';
3
- export * from './GradientLine';
4
4
  export * from './Line';
5
5
  export * from './LineChart';
6
6
  export * from './ReferenceLine';
@@ -0,0 +1,39 @@
1
+ const _excluded = ["x", "y", "position", "offset", "children"];
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
+ 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
+ import { memo, useMemo } from 'react';
5
+ import { ChartText } from '../text';
6
+ import { getAlignmentFromPosition, getLabelCoordinates } from '../utils/point';
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ /**
9
+ * DefaultPointLabel is the default label component for point labels.
10
+ * It renders text at the specified position relative to the point.
11
+ */
12
+ export const DefaultPointLabel = /*#__PURE__*/memo(_ref => {
13
+ let {
14
+ x,
15
+ y,
16
+ position = 'center',
17
+ offset,
18
+ children
19
+ } = _ref,
20
+ props = _objectWithoutPropertiesLoose(_ref, _excluded);
21
+ const {
22
+ horizontalAlignment,
23
+ verticalAlignment
24
+ } = useMemo(() => getAlignmentFromPosition(position), [position]);
25
+ const labelCoordinates = useMemo(() => {
26
+ if (offset === undefined) return {
27
+ x,
28
+ y
29
+ };
30
+ return getLabelCoordinates(x, y, position, offset);
31
+ }, [x, y, position, offset]);
32
+ return /*#__PURE__*/_jsx(ChartText, _extends({}, props, {
33
+ horizontalAlignment: horizontalAlignment,
34
+ verticalAlignment: verticalAlignment,
35
+ x: labelCoordinates.x,
36
+ y: labelCoordinates.y,
37
+ children: children
38
+ }));
39
+ });
@@ -0,0 +1,186 @@
1
+ import { memo, useEffect, useMemo } from 'react';
2
+ import { cancelAnimation, useDerivedValue, useSharedValue } from 'react-native-reanimated';
3
+ import { usePreviousValue } from '@coinbase/cds-common/hooks/usePreviousValue';
4
+ import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
5
+ import { Circle, Group, interpolateColors } from '@shopify/react-native-skia';
6
+ import { useCartesianChartContext } from '../ChartProvider';
7
+ import { projectPoint } from '../utils';
8
+ import { buildTransition, defaultAccessoryEnterTransition, defaultTransition, getTransition } from '../utils/transition';
9
+ import { DefaultPointLabel } from './DefaultPointLabel';
10
+
11
+ /**
12
+ * Props for point label components.
13
+ */
14
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
15
+ export const Point = /*#__PURE__*/memo(_ref => {
16
+ let {
17
+ dataX,
18
+ dataY,
19
+ yAxisId,
20
+ fill: fillProp,
21
+ radius = 5,
22
+ opacity,
23
+ stroke: strokeProp,
24
+ strokeWidth = 2,
25
+ label,
26
+ LabelComponent = DefaultPointLabel,
27
+ labelPosition = 'center',
28
+ labelOffset,
29
+ labelFont,
30
+ transitions,
31
+ transition,
32
+ animate: animateProp
33
+ } = _ref;
34
+ const theme = useTheme();
35
+ const stroke = strokeProp != null ? strokeProp : theme.color.bg;
36
+ const fill = fillProp != null ? fillProp : theme.color.fgPrimary;
37
+ const {
38
+ getXScale,
39
+ getYScale,
40
+ animate: animationEnabled,
41
+ drawingArea
42
+ } = useCartesianChartContext();
43
+ const animate = animateProp != null ? animateProp : animationEnabled;
44
+ const xScale = getXScale();
45
+ const yScale = getYScale(yAxisId);
46
+ const shouldAnimate = animate != null ? animate : false;
47
+ const updateTransition = useMemo(() => getTransition((transitions == null ? void 0 : transitions.update) !== undefined ? transitions.update : transition, animate, defaultTransition), [animate, transitions == null ? void 0 : transitions.update, transition]);
48
+ const enterTransition = useMemo(() => getTransition(transitions == null ? void 0 : transitions.enter, animate, defaultAccessoryEnterTransition), [animate, transitions == null ? void 0 : transitions.enter]);
49
+
50
+ // Calculate pixel coordinates from data coordinates
51
+ const pixelCoordinate = useMemo(() => {
52
+ if (!xScale || !yScale) {
53
+ return undefined;
54
+ }
55
+ return projectPoint({
56
+ x: dataX,
57
+ y: dataY,
58
+ xScale,
59
+ yScale
60
+ });
61
+ }, [xScale, yScale, dataX, dataY]);
62
+ const previousPixelCoordinate = usePreviousValue(pixelCoordinate);
63
+ const previousFill = usePreviousValue(fill);
64
+
65
+ // Animated values for position
66
+ const animatedX = useSharedValue(0);
67
+ const animatedY = useSharedValue(0);
68
+ const enterOpacity = useSharedValue(shouldAnimate ? 0 : 1);
69
+ const colorProgress = useSharedValue(1);
70
+ const isReady = !!xScale && !!yScale;
71
+ useEffect(() => {
72
+ if (!shouldAnimate || !isReady) return;
73
+ enterOpacity.value = buildTransition(1, enterTransition);
74
+ }, [shouldAnimate, isReady, enterTransition, enterOpacity]);
75
+
76
+ // Update position when coordinates change
77
+ useEffect(() => {
78
+ if (!pixelCoordinate) {
79
+ return;
80
+ }
81
+ if (shouldAnimate && previousPixelCoordinate) {
82
+ animatedX.value = buildTransition(pixelCoordinate.x, updateTransition);
83
+ animatedY.value = buildTransition(pixelCoordinate.y, updateTransition);
84
+ } else {
85
+ cancelAnimation(animatedX);
86
+ cancelAnimation(animatedY);
87
+ animatedX.value = pixelCoordinate.x;
88
+ animatedY.value = pixelCoordinate.y;
89
+ }
90
+ }, [pixelCoordinate, shouldAnimate, previousPixelCoordinate, animatedX, animatedY, updateTransition]);
91
+
92
+ // Update color when fill changes
93
+ useEffect(() => {
94
+ if (shouldAnimate && previousFill && previousFill !== fill) {
95
+ colorProgress.value = 0;
96
+ colorProgress.value = buildTransition(1, updateTransition);
97
+ } else {
98
+ cancelAnimation(colorProgress);
99
+ colorProgress.value = 1;
100
+ }
101
+ }, [fill, shouldAnimate, previousFill, colorProgress, updateTransition]);
102
+
103
+ // Create animated point for circles
104
+ const animatedPoint = useDerivedValue(() => {
105
+ return {
106
+ x: animatedX.value,
107
+ y: animatedY.value
108
+ };
109
+ }, [animatedX, animatedY]);
110
+
111
+ // Interpolate between previous and current fill color
112
+ const animatedFillColor = useDerivedValue(() => {
113
+ if (!previousFill || previousFill === fill) {
114
+ return fill;
115
+ }
116
+ return interpolateColors(colorProgress.value, [0, 1], [previousFill, fill]);
117
+ }, [colorProgress, previousFill, fill]);
118
+ const isWithinDrawingArea = useMemo(() => {
119
+ if (!pixelCoordinate) return false;
120
+ return pixelCoordinate.x >= drawingArea.x && pixelCoordinate.x <= drawingArea.x + drawingArea.width && pixelCoordinate.y >= drawingArea.y && pixelCoordinate.y <= drawingArea.y + drawingArea.height;
121
+ }, [pixelCoordinate, drawingArea]);
122
+ const effectiveOpacity = useDerivedValue(() => {
123
+ const baseOpacity = opacity != null ? opacity : 1;
124
+ return isWithinDrawingArea ? baseOpacity * enterOpacity.value : 0;
125
+ }, [isWithinDrawingArea, opacity, enterOpacity]);
126
+ const offset = useMemo(() => labelOffset != null ? labelOffset : radius * 2, [labelOffset, radius]);
127
+ if (!pixelCoordinate) {
128
+ return null;
129
+ }
130
+ if (!shouldAnimate) {
131
+ const isWithinBounds = pixelCoordinate.x >= drawingArea.x && pixelCoordinate.x <= drawingArea.x + drawingArea.width && pixelCoordinate.y >= drawingArea.y && pixelCoordinate.y <= drawingArea.y + drawingArea.height;
132
+ const staticOpacity = isWithinBounds ? opacity != null ? opacity : 1 : 0;
133
+ return /*#__PURE__*/_jsxs(_Fragment, {
134
+ children: [/*#__PURE__*/_jsxs(Group, {
135
+ opacity: staticOpacity,
136
+ children: [strokeWidth > 0 && /*#__PURE__*/_jsx(Circle, {
137
+ c: {
138
+ x: pixelCoordinate.x,
139
+ y: pixelCoordinate.y
140
+ },
141
+ color: stroke,
142
+ r: radius + strokeWidth / 2
143
+ }), /*#__PURE__*/_jsx(Circle, {
144
+ c: {
145
+ x: pixelCoordinate.x,
146
+ y: pixelCoordinate.y
147
+ },
148
+ color: fill,
149
+ r: radius - strokeWidth / 2
150
+ })]
151
+ }), label && /*#__PURE__*/_jsx(LabelComponent, {
152
+ dataX: dataX,
153
+ dataY: dataY,
154
+ fill: fill,
155
+ font: labelFont,
156
+ offset: offset,
157
+ position: labelPosition,
158
+ x: pixelCoordinate.x,
159
+ y: pixelCoordinate.y,
160
+ children: label
161
+ })]
162
+ });
163
+ }
164
+ return /*#__PURE__*/_jsxs(Group, {
165
+ opacity: effectiveOpacity,
166
+ children: [strokeWidth > 0 && /*#__PURE__*/_jsx(Circle, {
167
+ c: animatedPoint,
168
+ color: stroke,
169
+ r: radius + strokeWidth / 2
170
+ }), /*#__PURE__*/_jsx(Circle, {
171
+ c: animatedPoint,
172
+ color: animatedFillColor,
173
+ r: radius - strokeWidth / 2
174
+ }), label && /*#__PURE__*/_jsx(LabelComponent, {
175
+ dataX: dataX,
176
+ dataY: dataY,
177
+ fill: fill,
178
+ font: labelFont,
179
+ offset: offset,
180
+ position: labelPosition,
181
+ x: pixelCoordinate.x,
182
+ y: pixelCoordinate.y,
183
+ children: label
184
+ })]
185
+ });
186
+ });
@@ -0,0 +1,2 @@
1
+ export * from './DefaultPointLabel';
2
+ export * from './Point';
@@ -0,0 +1,180 @@
1
+ import { forwardRef, memo, useEffect, useImperativeHandle, useMemo } from 'react';
2
+ import { cancelAnimation, Easing, useAnimatedReaction, useDerivedValue, useSharedValue, withDelay, withRepeat, withSequence, withTiming } from 'react-native-reanimated';
3
+ import { useTheme } from '@coinbase/cds-mobile';
4
+ import { Circle, Group } from '@shopify/react-native-skia';
5
+ import { useCartesianChartContext } from '../ChartProvider';
6
+ import { unwrapAnimatedValue } from '../utils';
7
+ import { projectPointWithSerializableScale } from '../utils/point';
8
+ import { buildTransition, defaultTransition, getTransition } from '../utils/transition';
9
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
+ const defaultRadius = 5;
11
+ const defaultStrokeWidth = 2;
12
+ const pulseOpacityStart = 0.5;
13
+ const pulseOpacityEnd = 0;
14
+ const pulseRadiusStartMultiplier = 2;
15
+ const pulseRadiusEndMultiplier = 3;
16
+ const defaultPulseTransition = {
17
+ type: 'timing',
18
+ duration: 1600,
19
+ easing: Easing.bezier(0.0, 0.0, 0.0, 1.0)
20
+ };
21
+ const defaultPulseRepeatDelay = 400;
22
+ export const DefaultScrubberBeacon = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
23
+ let {
24
+ seriesId,
25
+ color: colorProp,
26
+ dataX,
27
+ dataY,
28
+ isIdle,
29
+ idlePulse,
30
+ animate = true,
31
+ transitions,
32
+ opacity: opacityProp = 1,
33
+ radius = defaultRadius,
34
+ stroke,
35
+ strokeWidth = defaultStrokeWidth
36
+ } = _ref;
37
+ const theme = useTheme();
38
+ const {
39
+ getSeries,
40
+ getXSerializableScale,
41
+ getYSerializableScale,
42
+ drawingArea
43
+ } = useCartesianChartContext();
44
+ const targetSeries = useMemo(() => getSeries(seriesId), [getSeries, seriesId]);
45
+ const xScale = useMemo(() => getXSerializableScale(), [getXSerializableScale]);
46
+ const yScale = useMemo(() => getYSerializableScale(targetSeries == null ? void 0 : targetSeries.yAxisId), [getYSerializableScale, targetSeries == null ? void 0 : targetSeries.yAxisId]);
47
+ const color = useMemo(() => {
48
+ var _ref2;
49
+ return (_ref2 = colorProp != null ? colorProp : targetSeries == null ? void 0 : targetSeries.color) != null ? _ref2 : theme.color.fgPrimary;
50
+ }, [colorProp, targetSeries == null ? void 0 : targetSeries.color, theme.color.fgPrimary]);
51
+ const updateTransition = useMemo(() => getTransition(transitions == null ? void 0 : transitions.update, animate, defaultTransition), [transitions == null ? void 0 : transitions.update, animate]);
52
+ const pulseTransition = useMemo(() => {
53
+ var _transitions$pulse;
54
+ return (_transitions$pulse = transitions == null ? void 0 : transitions.pulse) != null ? _transitions$pulse : defaultPulseTransition;
55
+ }, [transitions == null ? void 0 : transitions.pulse]);
56
+ const pulseRepeatDelay = useMemo(() => {
57
+ var _transitions$pulseRep;
58
+ return (_transitions$pulseRep = transitions == null ? void 0 : transitions.pulseRepeatDelay) != null ? _transitions$pulseRep : defaultPulseRepeatDelay;
59
+ }, [transitions == null ? void 0 : transitions.pulseRepeatDelay]);
60
+ const pulseRadiusStart = radius * pulseRadiusStartMultiplier;
61
+ const pulseRadiusEnd = radius * pulseRadiusEndMultiplier;
62
+ const pulseOpacity = useSharedValue(0);
63
+ const pulseRadius = useSharedValue(pulseRadiusStart);
64
+
65
+ // Convert idlePulse prop to SharedValue so useAnimatedReaction can detect changes.
66
+ // In the new React Native architecture, regular JS props are captured by value in worklets
67
+ // and won't update when the prop changes.
68
+ const idlePulseShared = useSharedValue(idlePulse != null ? idlePulse : false);
69
+ useEffect(() => {
70
+ idlePulseShared.value = idlePulse != null ? idlePulse : false;
71
+ }, [idlePulse, idlePulseShared]);
72
+ const animatedX = useSharedValue(0);
73
+ const animatedY = useSharedValue(0);
74
+
75
+ // Calculate the target point position - project data to pixels
76
+ const targetPoint = useDerivedValue(() => {
77
+ if (!xScale || !yScale) return {
78
+ x: 0,
79
+ y: 0
80
+ };
81
+ return projectPointWithSerializableScale({
82
+ x: unwrapAnimatedValue(dataX),
83
+ y: unwrapAnimatedValue(dataY),
84
+ xScale,
85
+ yScale
86
+ });
87
+ }, [dataX, dataY, xScale, yScale]);
88
+ useAnimatedReaction(() => {
89
+ return {
90
+ point: targetPoint.value,
91
+ isIdle: unwrapAnimatedValue(isIdle)
92
+ };
93
+ }, (current, previous) => {
94
+ // When animation is disabled, on initial render, or when we are starting,
95
+ // continuing, or finishing scrubbing we should immediately transition
96
+ if (!animate || previous === null || !previous.isIdle || !current.isIdle) {
97
+ animatedX.value = current.point.x;
98
+ animatedY.value = current.point.y;
99
+ return;
100
+ }
101
+ animatedX.value = buildTransition(current.point.x, updateTransition);
102
+ animatedY.value = buildTransition(current.point.y, updateTransition);
103
+ }, [animate, updateTransition]);
104
+
105
+ // Create animated point using the animated values
106
+ const animatedPoint = useDerivedValue(() => {
107
+ return {
108
+ x: animatedX.value,
109
+ y: animatedY.value
110
+ };
111
+ }, [animatedX, animatedY]);
112
+ useImperativeHandle(ref, () => ({
113
+ pulse: () => {
114
+ // Only trigger manual pulse when idlePulse is not enabled
115
+ if (!idlePulseShared.value) {
116
+ cancelAnimation(pulseOpacity);
117
+ cancelAnimation(pulseRadius);
118
+
119
+ // Manual pulse without delay
120
+ pulseOpacity.value = pulseOpacityStart;
121
+ pulseRadius.value = pulseRadiusStart;
122
+ pulseOpacity.value = buildTransition(pulseOpacityEnd, pulseTransition);
123
+ pulseRadius.value = buildTransition(pulseRadiusEnd, pulseTransition);
124
+ }
125
+ }
126
+ }), [idlePulseShared, pulseOpacity, pulseRadius, pulseTransition, pulseRadiusStart, pulseRadiusEnd]);
127
+
128
+ // Watch idlePulse changes and control continuous pulse
129
+ useAnimatedReaction(() => idlePulseShared.value, current => {
130
+ if (current) {
131
+ // Start continuous pulse when idlePulse is enabled
132
+ pulseOpacity.value = pulseOpacityStart;
133
+ pulseRadius.value = pulseRadiusStart;
134
+ pulseOpacity.value = withRepeat(withSequence(buildTransition(pulseOpacityEnd, pulseTransition), withDelay(pulseRepeatDelay, withTiming(pulseOpacityStart, {
135
+ duration: 0
136
+ }))), -1,
137
+ // infinite loop
138
+ false);
139
+ pulseRadius.value = withRepeat(withSequence(buildTransition(pulseRadiusEnd, pulseTransition), withDelay(pulseRepeatDelay, withTiming(pulseRadiusStart, {
140
+ duration: 0
141
+ }))), -1,
142
+ // infinite loop
143
+ false);
144
+ } else {
145
+ // Stop pulse when idlePulse is disabled
146
+ cancelAnimation(pulseOpacity);
147
+ cancelAnimation(pulseRadius);
148
+ pulseOpacity.value = pulseOpacityEnd;
149
+ pulseRadius.value = pulseRadiusStart;
150
+ }
151
+ }, [pulseTransition, pulseRepeatDelay, pulseRadiusStart, pulseRadiusEnd]);
152
+ const pulseVisibility = useDerivedValue(() => {
153
+ // Never pulse when scrubbing
154
+ if (!unwrapAnimatedValue(isIdle)) return 0;
155
+ return pulseOpacity.value;
156
+ }, [isIdle, pulseOpacity]);
157
+ const beaconOpacity = useDerivedValue(() => {
158
+ const point = targetPoint.value;
159
+ const isWithinDrawingArea = point.x >= drawingArea.x && point.x <= drawingArea.x + drawingArea.width && point.y >= drawingArea.y && point.y <= drawingArea.y + drawingArea.height;
160
+ const userOpacity = unwrapAnimatedValue(opacityProp);
161
+ return isWithinDrawingArea ? userOpacity : 0;
162
+ }, [targetPoint, drawingArea, opacityProp]);
163
+ return /*#__PURE__*/_jsxs(Group, {
164
+ opacity: beaconOpacity,
165
+ children: [/*#__PURE__*/_jsx(Circle, {
166
+ c: animatedPoint,
167
+ color: color,
168
+ opacity: pulseVisibility,
169
+ r: pulseRadius
170
+ }), /*#__PURE__*/_jsx(Circle, {
171
+ c: animatedPoint,
172
+ color: stroke != null ? stroke : theme.color.bg,
173
+ r: radius + strokeWidth / 2
174
+ }), /*#__PURE__*/_jsx(Circle, {
175
+ c: animatedPoint,
176
+ color: color,
177
+ r: radius - strokeWidth / 2
178
+ })]
179
+ });
180
+ }));