@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
@@ -0,0 +1,833 @@
1
+ const _excluded = ["x", "y", "width", "height", "borderRadius", "roundTop", "roundBottom", "d", "fill", "fillOpacity", "dataX", "origin", "dataY", "seriesId", "minSize"];
2
+ 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; }
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
+ import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
5
+ import { assets } from '@coinbase/cds-common/internal/data/assets';
6
+ import { IconButton } from '@coinbase/cds-mobile/buttons';
7
+ import { Switch } from '@coinbase/cds-mobile/controls';
8
+ import { ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
9
+ import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
10
+ import { Box, HStack, VStack } from '@coinbase/cds-mobile/layout';
11
+ import { RollingNumber } from '@coinbase/cds-mobile/numbers';
12
+ import { Text } from '@coinbase/cds-mobile/typography';
13
+ import { Group, Path as SkiaPath, Skia } from '@shopify/react-native-skia';
14
+ import { useCartesianChartContext } from '../../ChartProvider';
15
+ import { DefaultLegendEntry, DefaultLegendShape, Legend } from '../../legend';
16
+ import { Path } from '../../Path';
17
+ import { getBarPath } from '../../utils';
18
+ import { getDottedAreaPath } from '../../utils/path';
19
+ import { DefaultBar } from '../DefaultBar';
20
+ import { PercentageBarChart } from '../PercentageBarChart';
21
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
22
+ const DOTTED_BAR_PATTERN_SIZE = 4;
23
+ const DOTTED_BAR_DOT_SIZE = 1;
24
+ const DOTTED_BAR_OUTLINE_STROKE_WIDTH = 2;
25
+ const DottedBarComponent = /*#__PURE__*/memo(function DottedBarComponent(props) {
26
+ const {
27
+ x,
28
+ y,
29
+ width,
30
+ height,
31
+ fill,
32
+ d
33
+ } = props;
34
+ const dottedPath = useMemo(() => getDottedAreaPath({
35
+ x,
36
+ y,
37
+ width,
38
+ height
39
+ }, DOTTED_BAR_PATTERN_SIZE, DOTTED_BAR_DOT_SIZE), [x, y, width, height]);
40
+ const barClipPath = useMemo(() => {
41
+ var _Skia$Path$MakeFromSV;
42
+ return d ? (_Skia$Path$MakeFromSV = Skia.Path.MakeFromSVGString(d)) != null ? _Skia$Path$MakeFromSV : undefined : undefined;
43
+ }, [d]);
44
+ const dotsSkiaPath = useMemo(() => {
45
+ var _Skia$Path$MakeFromSV2;
46
+ return dottedPath ? (_Skia$Path$MakeFromSV2 = Skia.Path.MakeFromSVGString(dottedPath)) != null ? _Skia$Path$MakeFromSV2 : undefined : undefined;
47
+ }, [dottedPath]);
48
+ return /*#__PURE__*/_jsxs(_Fragment, {
49
+ children: [/*#__PURE__*/_jsx(Group, {
50
+ clip: barClipPath,
51
+ children: dotsSkiaPath && fill ? /*#__PURE__*/_jsx(SkiaPath, {
52
+ color: fill,
53
+ path: dotsSkiaPath,
54
+ style: "fill"
55
+ }) : null
56
+ }), /*#__PURE__*/_jsx(DefaultBar, _extends({}, props, {
57
+ fill: undefined,
58
+ stroke: fill,
59
+ strokeWidth: DOTTED_BAR_OUTLINE_STROKE_WIDTH
60
+ }))]
61
+ });
62
+ });
63
+
64
+ /**
65
+ * Builds an SVG path for a horizontal bar segment with a pill cap on one end
66
+ * and a slanted straight edge on the other. The two segments' inner edges
67
+ * are parallel, producing a parallelogram-shaped gap between them.
68
+ */
69
+ function getSlantedHorizontalBarPath(x, y, width, height, borderRadius, pillLeft, pillRight, slantDx) {
70
+ if (width <= 0 || height <= 0) return undefined;
71
+ if (pillLeft === pillRight) return undefined;
72
+ const r = Math.min(borderRadius, height / 2, width / 2);
73
+ const s = Math.min(Math.max(0, slantDx), width - r * 2);
74
+ const x0 = x;
75
+ const x1 = x + width;
76
+ const y0 = y;
77
+ const y1 = y + height;
78
+
79
+ // Pill left, slanted right
80
+ if (pillLeft && !pillRight) {
81
+ return ["M " + (x0 + r) + " " + y0, "L " + x1 + " " + y0, "L " + (x1 - s) + " " + y1, "L " + (x0 + r) + " " + y1, "A " + r + " " + r + " 0 0 1 " + x0 + " " + (y1 - r), "L " + x0 + " " + (y0 + r), "A " + r + " " + r + " 0 0 1 " + (x0 + r) + " " + y0, 'Z'].join(' ');
82
+ }
83
+
84
+ // Slanted left, pill right
85
+ if (!pillLeft && pillRight) {
86
+ return ["M " + (x0 + s) + " " + y0, "L " + (x1 - r) + " " + y0, "A " + r + " " + r + " 0 0 1 " + x1 + " " + (y0 + r), "L " + x1 + " " + (y1 - r), "A " + r + " " + r + " 0 0 1 " + (x1 - r) + " " + y1, "L " + x0 + " " + y1, 'Z'].join(' ');
87
+ }
88
+ return undefined;
89
+ }
90
+ const SLANT_DX = 8;
91
+ const BASELINE_THRESHOLD = 1;
92
+ const SlantedStackBar = /*#__PURE__*/memo(function SlantedStackBar(props) {
93
+ const {
94
+ layout
95
+ } = useCartesianChartContext();
96
+ const {
97
+ x,
98
+ y,
99
+ width,
100
+ height,
101
+ borderRadius = 4,
102
+ roundTop,
103
+ roundBottom,
104
+ d: defaultD,
105
+ fill,
106
+ fillOpacity,
107
+ dataX
108
+ } = props,
109
+ rest = _objectWithoutPropertiesLoose(props, _excluded);
110
+ const d = useMemo(() => {
111
+ var _ref, _getSlantedHorizontal;
112
+ if (layout !== 'horizontal') {
113
+ return defaultD != null ? defaultD : getBarPath(x, y, width, height, borderRadius, !!roundTop, !!roundBottom, layout);
114
+ }
115
+ const isLeftmost = Array.isArray(dataX) && Math.abs(dataX[0]) < BASELINE_THRESHOLD;
116
+ return (_ref = (_getSlantedHorizontal = getSlantedHorizontalBarPath(x, y, width, height, borderRadius, isLeftmost, !isLeftmost, SLANT_DX)) != null ? _getSlantedHorizontal : defaultD) != null ? _ref : getBarPath(x, y, width, height, borderRadius, !!roundTop, !!roundBottom, layout);
117
+ }, [layout, defaultD, dataX, x, y, width, height, borderRadius, roundTop, roundBottom]);
118
+ if (!d) return null;
119
+ return /*#__PURE__*/_jsx(Path, _extends({}, rest, {
120
+ animate: true,
121
+ clipPath: null,
122
+ d: d,
123
+ fill: fill,
124
+ fillOpacity: fillOpacity,
125
+ transitions: props.transitions
126
+ }));
127
+ });
128
+ const Basics = () => {
129
+ const theme = useTheme();
130
+ return /*#__PURE__*/_jsx(PercentageBarChart, {
131
+ height: 16,
132
+ series: [{
133
+ id: 'a',
134
+ data: 70,
135
+ label: 'Segment A',
136
+ color: theme.color.fgPositive
137
+ }, {
138
+ id: 'b',
139
+ data: 45,
140
+ label: 'Segment B',
141
+ color: theme.color.fgNegative
142
+ }]
143
+ });
144
+ };
145
+ const StackGap = () => {
146
+ const theme = useTheme();
147
+ return /*#__PURE__*/_jsx(PercentageBarChart, {
148
+ height: 20,
149
+ series: [{
150
+ id: 'a',
151
+ data: 40,
152
+ label: 'A',
153
+ color: theme.color.fgPositive
154
+ }, {
155
+ id: 'b',
156
+ data: 35,
157
+ label: 'B',
158
+ color: theme.color.fgWarning
159
+ }, {
160
+ id: 'c',
161
+ data: 20,
162
+ label: 'C',
163
+ color: theme.color.accentBoldPurple
164
+ }],
165
+ stackGap: 6
166
+ });
167
+ };
168
+ const BorderRadius = () => {
169
+ const theme = useTheme();
170
+ return /*#__PURE__*/_jsx(PercentageBarChart, {
171
+ borderRadius: 1000,
172
+ height: 28,
173
+ series: [{
174
+ id: 'a',
175
+ data: 45,
176
+ color: "rgb(" + theme.spectrum.purple30 + ")",
177
+ label: 'A'
178
+ }, {
179
+ id: 'b',
180
+ data: 30,
181
+ color: "rgb(" + theme.spectrum.blue30 + ")",
182
+ label: 'B'
183
+ }, {
184
+ id: 'c',
185
+ data: 20,
186
+ color: "rgb(" + theme.spectrum.teal30 + ")",
187
+ label: 'C'
188
+ }],
189
+ stackGap: 2
190
+ });
191
+ };
192
+ const DataExample = () => {
193
+ const theme = useTheme();
194
+ return /*#__PURE__*/_jsx(PercentageBarChart, {
195
+ showXAxis: true,
196
+ showYAxis: true,
197
+ barMinSize: 12,
198
+ borderRadius: 8,
199
+ height: 100,
200
+ series: [{
201
+ id: 'a',
202
+ data: [40, null, 20],
203
+ label: 'A',
204
+ color: theme.color.fgPositive
205
+ }, {
206
+ id: 'b',
207
+ data: [-10, 60, 30],
208
+ label: 'B',
209
+ color: theme.color.fgWarning
210
+ }, {
211
+ id: 'c',
212
+ data: [null, 50],
213
+ label: 'C',
214
+ color: theme.color.fgMuted
215
+ }, {
216
+ id: 'd',
217
+ data: 45,
218
+ label: 'D',
219
+ color: theme.color.fgNegative
220
+ }],
221
+ stackGap: 2,
222
+ xAxis: {
223
+ showTickMarks: true
224
+ },
225
+ yAxis: {
226
+ data: ['Q1', 'Q2', 'Q3'],
227
+ position: 'left',
228
+ categoryPadding: 0.45
229
+ }
230
+ });
231
+ };
232
+ const BarStackSpacing = () => {
233
+ const theme = useTheme();
234
+ return /*#__PURE__*/_jsx(PercentageBarChart, {
235
+ legend: true,
236
+ showXAxis: true,
237
+ showYAxis: true,
238
+ barMinSize: 18,
239
+ borderRadius: 24,
240
+ height: 240,
241
+ series: [{
242
+ id: 'a',
243
+ data: [55, 40, 35],
244
+ label: 'A',
245
+ color: theme.color.fgWarning
246
+ }, {
247
+ id: 'b',
248
+ data: [30, 45, 25],
249
+ label: 'B',
250
+ color: theme.color.accentBoldPurple
251
+ }, {
252
+ id: 'c',
253
+ data: [15, 15, 40],
254
+ label: 'C',
255
+ color: theme.color.fgMuted
256
+ }],
257
+ stackGap: 4,
258
+ xAxis: {
259
+ showTickMarks: true
260
+ },
261
+ yAxis: {
262
+ data: ['Q1', 'Q2', 'Q3'],
263
+ position: 'left',
264
+ categoryPadding: 0.7
265
+ }
266
+ });
267
+ };
268
+ const MinimumBarSize = () => {
269
+ const theme = useTheme();
270
+ return /*#__PURE__*/_jsx(PercentageBarChart, {
271
+ barMinSize: 16,
272
+ height: 16,
273
+ series: [{
274
+ id: 'a',
275
+ data: 99,
276
+ label: 'Segment A',
277
+ color: theme.color.fgPositive
278
+ }, {
279
+ id: 'b',
280
+ data: 0.001,
281
+ label: 'Segment B',
282
+ color: theme.color.fgNegative
283
+ }],
284
+ stackGap: 2
285
+ });
286
+ };
287
+ const TaxesStyleConfirmedVsNeedReview = () => {
288
+ const theme = useTheme();
289
+ const series = [{
290
+ id: 'confirmed',
291
+ data: 28,
292
+ label: 'Confirmed',
293
+ color: theme.color.fgPositive
294
+ }, {
295
+ id: 'needs-review',
296
+ data: 2,
297
+ label: 'Needs review',
298
+ color: theme.color.fgWarning
299
+ }];
300
+ return /*#__PURE__*/_jsxs(VStack, {
301
+ gap: 2,
302
+ paddingX: 2,
303
+ children: [/*#__PURE__*/_jsxs(VStack, {
304
+ gap: 0.5,
305
+ children: [/*#__PURE__*/_jsx(Text, {
306
+ color: "fgMuted",
307
+ font: "label2",
308
+ children: "Estimated gain"
309
+ }), /*#__PURE__*/_jsx(Text, {
310
+ font: "title2",
311
+ children: "+$30,000"
312
+ })]
313
+ }), /*#__PURE__*/_jsx(PercentageBarChart, {
314
+ height: 24,
315
+ series: series,
316
+ stackGap: 4
317
+ }), /*#__PURE__*/_jsxs(VStack, {
318
+ children: [/*#__PURE__*/_jsxs(HStack, {
319
+ alignItems: "center",
320
+ gap: 1,
321
+ justifyContent: "space-between",
322
+ children: [/*#__PURE__*/_jsxs(HStack, {
323
+ alignItems: "center",
324
+ gap: 1,
325
+ children: [/*#__PURE__*/_jsx(DefaultLegendShape, {
326
+ color: theme.color.fgPositive,
327
+ shape: "squircle"
328
+ }), /*#__PURE__*/_jsx(Text, {
329
+ font: "label1",
330
+ children: "Confirmed"
331
+ })]
332
+ }), /*#__PURE__*/_jsxs(HStack, {
333
+ alignItems: "center",
334
+ gap: 1,
335
+ children: [/*#__PURE__*/_jsx(Text, {
336
+ font: "body",
337
+ children: "+$28,000"
338
+ }), /*#__PURE__*/_jsx(IconButton, {
339
+ compact: true,
340
+ transparent: true,
341
+ accessibilityLabel: "Confirmed details",
342
+ name: "caretRight",
343
+ onPress: () => {},
344
+ variant: "foregroundMuted"
345
+ })]
346
+ })]
347
+ }), /*#__PURE__*/_jsxs(HStack, {
348
+ alignItems: "center",
349
+ gap: 1,
350
+ justifyContent: "space-between",
351
+ children: [/*#__PURE__*/_jsxs(HStack, {
352
+ alignItems: "center",
353
+ gap: 1,
354
+ children: [/*#__PURE__*/_jsx(DefaultLegendShape, {
355
+ color: theme.color.fgWarning,
356
+ shape: "squircle"
357
+ }), /*#__PURE__*/_jsx(Text, {
358
+ font: "label1",
359
+ children: "Needs review"
360
+ })]
361
+ }), /*#__PURE__*/_jsxs(HStack, {
362
+ alignItems: "center",
363
+ gap: 1,
364
+ children: [/*#__PURE__*/_jsxs(VStack, {
365
+ alignItems: "flex-end",
366
+ gap: 0,
367
+ children: [/*#__PURE__*/_jsx(Text, {
368
+ font: "body",
369
+ children: "Up to $2,000"
370
+ }), /*#__PURE__*/_jsx(Text, {
371
+ color: "fgMuted",
372
+ font: "body",
373
+ children: "11 transfers"
374
+ })]
375
+ }), /*#__PURE__*/_jsx(IconButton, {
376
+ compact: true,
377
+ transparent: true,
378
+ accessibilityLabel: "Needs review details",
379
+ name: "caretRight",
380
+ onPress: () => {},
381
+ variant: "foregroundMuted"
382
+ })]
383
+ })]
384
+ })]
385
+ })]
386
+ });
387
+ };
388
+ const DottedBarFirstSeriesOnly = () => {
389
+ const theme = useTheme();
390
+ const dottedBarSeries = useMemo(() => [{
391
+ id: 'segment-a',
392
+ data: 60,
393
+ label: 'Segment A',
394
+ color: "rgb(" + theme.spectrum.teal60 + ")",
395
+ BarComponent: DottedBarComponent
396
+ }, {
397
+ id: 'segment-b',
398
+ data: 30,
399
+ label: 'Segment B',
400
+ color: "rgb(" + theme.spectrum.chartreuse50 + ")"
401
+ }, {
402
+ id: 'segment-c',
403
+ data: 10,
404
+ label: 'Segment C',
405
+ color: "rgb(" + theme.spectrum.indigo40 + ")"
406
+ }], [theme]);
407
+ return /*#__PURE__*/_jsx(PercentageBarChart, {
408
+ height: 24,
409
+ series: dottedBarSeries,
410
+ stackGap: 4
411
+ });
412
+ };
413
+ const DottedBarChartLevel = () => {
414
+ const theme = useTheme();
415
+ return /*#__PURE__*/_jsx(PercentageBarChart, {
416
+ BarComponent: DottedBarComponent,
417
+ height: 24,
418
+ series: [{
419
+ id: 'segment-a',
420
+ data: 60,
421
+ label: 'Segment A',
422
+ color: "rgb(" + theme.spectrum.teal60 + ")"
423
+ }, {
424
+ id: 'segment-b',
425
+ data: 30,
426
+ label: 'Segment B',
427
+ color: "rgb(" + theme.spectrum.chartreuse50 + ")"
428
+ }, {
429
+ id: 'segment-c',
430
+ data: 10,
431
+ label: 'Segment C',
432
+ color: "rgb(" + theme.spectrum.indigo40 + ")"
433
+ }],
434
+ stackGap: 4
435
+ });
436
+ };
437
+ function randomShares() {
438
+ const raw = [Math.random() + 0.1, Math.random() + 0.1, Math.random() + 0.1];
439
+ const sum = raw[0] + raw[1] + raw[2];
440
+ return raw.map(v => Math.max(1, Math.round(v / sum * 100)));
441
+ }
442
+ function generateAnimationData() {
443
+ return [randomShares(), randomShares(), randomShares()];
444
+ }
445
+ const Animations = () => {
446
+ const theme = useTheme();
447
+ const [animate, setAnimate] = useState(true);
448
+ const [data, setData] = useState(generateAnimationData);
449
+ useEffect(() => {
450
+ const id = setInterval(() => setData(generateAnimationData()), 800);
451
+ return () => clearInterval(id);
452
+ }, []);
453
+ const series = useMemo(() => [{
454
+ id: 'btc',
455
+ data: data.map(q => q[0]),
456
+ label: 'BTC',
457
+ color: assets.btc.color
458
+ }, {
459
+ id: 'eth',
460
+ data: data.map(q => q[1]),
461
+ label: 'ETH',
462
+ color: assets.eth.color
463
+ }, {
464
+ id: 'other',
465
+ data: data.map(q => q[2]),
466
+ label: 'Other',
467
+ color: theme.color.fgMuted
468
+ }], [data, theme]);
469
+ return /*#__PURE__*/_jsxs(VStack, {
470
+ gap: 2,
471
+ children: [/*#__PURE__*/_jsx(HStack, {
472
+ alignItems: "center",
473
+ gap: 1,
474
+ justifyContent: "flex-end",
475
+ children: /*#__PURE__*/_jsx(Switch, {
476
+ checked: animate,
477
+ onChange: () => setAnimate(v => !v),
478
+ children: "Animate"
479
+ })
480
+ }), /*#__PURE__*/_jsx(PercentageBarChart, {
481
+ legend: true,
482
+ showXAxis: true,
483
+ showYAxis: true,
484
+ animate: animate,
485
+ barMinSize: 14,
486
+ borderRadius: 48,
487
+ height: 220,
488
+ inset: {
489
+ left: 24,
490
+ right: 0,
491
+ top: 0,
492
+ bottom: 0
493
+ },
494
+ legendPosition: "top",
495
+ series: series,
496
+ stackGap: 2,
497
+ transitions: {
498
+ enter: {
499
+ type: 'timing',
500
+ duration: 400,
501
+ staggerDelay: 0.2
502
+ },
503
+ update: {
504
+ type: 'timing',
505
+ duration: 300
506
+ }
507
+ },
508
+ xAxis: {
509
+ showTickMarks: true,
510
+ tickLabelFormatter: value => value + "%"
511
+ },
512
+ yAxis: {
513
+ categoryPadding: 0.75,
514
+ data: ['Q1 2025', 'Q2 2025', 'Q3 2025'],
515
+ position: 'left',
516
+ requestedTickCount: 5,
517
+ showTickMarks: true
518
+ }
519
+ })]
520
+ });
521
+ };
522
+
523
+ /** Fake "projected value" copy: scales with live % so subtitles stay in sync with the bar. */
524
+ const liveFeedSubtitleBase = 100;
525
+ const liveFeedYesDollarsPerPercentPoint = (182 - liveFeedSubtitleBase) / 50;
526
+ const liveFeedNoDollarsPerPercentPoint = (222 - liveFeedSubtitleBase) / 50;
527
+ function getLiveFeedProjectedValue(seriesId, percentage) {
528
+ const inverseShare = 100 - percentage;
529
+ if (seriesId === 'yes') {
530
+ return Math.round(liveFeedSubtitleBase + inverseShare * liveFeedYesDollarsPerPercentPoint);
531
+ }
532
+ if (seriesId === 'no') {
533
+ return Math.round(liveFeedSubtitleBase + inverseShare * liveFeedNoDollarsPerPercentPoint);
534
+ }
535
+ return undefined;
536
+ }
537
+ const liveFeedCurrencyFormat = {
538
+ style: 'currency',
539
+ currency: 'USD',
540
+ maximumFractionDigits: 0
541
+ };
542
+ const LiveFeedCTALegendEntry = /*#__PURE__*/memo(function LiveFeedCTALegendEntry(_ref2) {
543
+ var _, _seriesData$data;
544
+ let {
545
+ seriesId,
546
+ label,
547
+ color
548
+ } = _ref2;
549
+ const {
550
+ series: contextSeries
551
+ } = useCartesianChartContext();
552
+ const seriesData = contextSeries.find(s => s.id === seriesId);
553
+ const percentage = (_ = seriesData == null || (_seriesData$data = seriesData.data) == null ? void 0 : _seriesData$data[0]) != null ? _ : 0;
554
+ const projectedValue = getLiveFeedProjectedValue(seriesId, percentage);
555
+ return /*#__PURE__*/_jsx(Box, {
556
+ paddingX: 2,
557
+ paddingY: 1,
558
+ style: {
559
+ backgroundColor: color,
560
+ borderRadius: 200
561
+ },
562
+ children: /*#__PURE__*/_jsxs(VStack, {
563
+ alignItems: "center",
564
+ gap: 0.25,
565
+ children: [/*#__PURE__*/_jsxs(HStack, {
566
+ alignItems: "center",
567
+ gap: 0.5,
568
+ children: [/*#__PURE__*/_jsxs(Text, {
569
+ color: "fgInverse",
570
+ font: "label1",
571
+ children: [label, " ", '· ']
572
+ }), /*#__PURE__*/_jsx(RollingNumber, {
573
+ color: "fgInverse",
574
+ font: "label1",
575
+ format: {
576
+ style: 'percent',
577
+ maximumFractionDigits: 0
578
+ },
579
+ value: percentage / 100
580
+ })]
581
+ }), projectedValue != null && /*#__PURE__*/_jsxs(HStack, {
582
+ alignItems: "center",
583
+ gap: 0.5,
584
+ children: [/*#__PURE__*/_jsxs(Text, {
585
+ color: "fgInverse",
586
+ font: "legal",
587
+ children: ["$", liveFeedSubtitleBase, " \u2192"]
588
+ }), /*#__PURE__*/_jsx(RollingNumber, {
589
+ color: "fgInverse",
590
+ font: "legal",
591
+ format: liveFeedCurrencyFormat,
592
+ value: projectedValue
593
+ })]
594
+ })]
595
+ })
596
+ });
597
+ });
598
+ const LiveUpdatingData = () => {
599
+ const theme = useTheme();
600
+ const [tick, setTick] = useState(0);
601
+ const yesValue = 50 + Math.sin(tick * 0.05) * 49;
602
+ const noValue = 50 - Math.sin(tick * 0.05) * 49;
603
+ const series = [{
604
+ id: 'yes',
605
+ data: yesValue,
606
+ label: 'Yes',
607
+ color: theme.color.fgPositive
608
+ }, {
609
+ id: 'no',
610
+ data: noValue,
611
+ label: 'No',
612
+ color: theme.color.fgNegative
613
+ }];
614
+ useEffect(() => {
615
+ const id = setInterval(() => setTick(t => t + 4), 1000);
616
+ return () => clearInterval(id);
617
+ }, []);
618
+ return /*#__PURE__*/_jsx(PercentageBarChart, {
619
+ barMinSize: 16,
620
+ borderRadius: 1000,
621
+ height: 78,
622
+ legend: /*#__PURE__*/_jsx(Legend, {
623
+ EntryComponent: LiveFeedCTALegendEntry,
624
+ justifyContent: "space-evenly",
625
+ paddingTop: 1
626
+ }),
627
+ legendPosition: "bottom",
628
+ series: series,
629
+ stackGap: 2
630
+ });
631
+ };
632
+ const VerticalMix = () => {
633
+ const theme = useTheme();
634
+ const series = [{
635
+ id: 'btc',
636
+ data: [55, 52, 48, 45, 50, 58, 62, 57, 53, 49, 44, 46],
637
+ label: 'BTC',
638
+ color: assets.btc.color
639
+ }, {
640
+ id: 'eth',
641
+ data: [30, 33, 35, 38, 32, 27, 25, 29, 34, 37, 40, 38],
642
+ label: 'ETH',
643
+ color: assets.eth.color
644
+ }, {
645
+ id: 'other',
646
+ data: [15, 15, 17, 17, 18, 15, 13, 14, 13, 14, 16, 16],
647
+ label: 'Other',
648
+ color: theme.color.fgMuted
649
+ }];
650
+ return /*#__PURE__*/_jsx(PercentageBarChart, {
651
+ legend: true,
652
+ showXAxis: true,
653
+ showYAxis: true,
654
+ barMinSize: 28,
655
+ borderRadius: 48,
656
+ height: 240,
657
+ layout: "vertical",
658
+ legendPosition: "top",
659
+ series: series,
660
+ stackGap: 1,
661
+ xAxis: {
662
+ categoryPadding: 0.5,
663
+ data: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
664
+ position: 'bottom',
665
+ showTickMarks: true
666
+ }
667
+ });
668
+ };
669
+ const BuyVsSellLegend = /*#__PURE__*/memo(function BuyVsSellLegend(_ref3) {
670
+ let {
671
+ series
672
+ } = _ref3;
673
+ const [buy, sell] = series;
674
+ return /*#__PURE__*/_jsxs(HStack, {
675
+ gap: 1,
676
+ justifyContent: "space-between",
677
+ children: [/*#__PURE__*/_jsx(DefaultLegendEntry, {
678
+ color: buy.color,
679
+ label: /*#__PURE__*/_jsxs(Text, {
680
+ color: "fgMuted",
681
+ font: "legal",
682
+ children: [buy.data, "% bought"]
683
+ }),
684
+ seriesId: buy.id,
685
+ shape: buy.legendShape
686
+ }), /*#__PURE__*/_jsx(DefaultLegendEntry, {
687
+ color: sell.color,
688
+ label: /*#__PURE__*/_jsxs(Text, {
689
+ color: "fgMuted",
690
+ font: "legal",
691
+ children: [sell.data, "% sold"]
692
+ }),
693
+ seriesId: sell.id,
694
+ shape: sell.legendShape
695
+ })]
696
+ });
697
+ });
698
+ const BuyVsSell = () => {
699
+ const theme = useTheme();
700
+ const buySellSeries = useMemo(() => [{
701
+ id: 'buy',
702
+ data: 76,
703
+ color: theme.color.fgPositive,
704
+ legendShape: 'circle'
705
+ }, {
706
+ id: 'sell',
707
+ data: 24,
708
+ color: theme.color.fgNegative,
709
+ legendShape: 'square'
710
+ }], [theme]);
711
+ return /*#__PURE__*/_jsxs(VStack, {
712
+ gap: 1.5,
713
+ children: [/*#__PURE__*/_jsx(PercentageBarChart, {
714
+ barMinSize: 8,
715
+ borderRadius: 24,
716
+ height: 8,
717
+ series: buySellSeries,
718
+ stackGap: 4
719
+ }), /*#__PURE__*/_jsx(BuyVsSellLegend, {
720
+ series: buySellSeries
721
+ })]
722
+ });
723
+ };
724
+ const SlantedStackGap = () => {
725
+ const theme = useTheme();
726
+ return /*#__PURE__*/_jsx(PercentageBarChart, {
727
+ BarComponent: SlantedStackBar,
728
+ animate: false,
729
+ barMinSize: 12,
730
+ borderRadius: 24,
731
+ height: 12,
732
+ series: [{
733
+ id: 'team-a',
734
+ data: 40,
735
+ color: "rgb(" + theme.spectrum.teal60 + ")"
736
+ }, {
737
+ id: 'team-b',
738
+ data: 61,
739
+ color: theme.color.accentBoldBlue
740
+ }]
741
+ });
742
+ };
743
+ function ExampleNavigator() {
744
+ const [currentIndex, setCurrentIndex] = useState(0);
745
+ const examples = useMemo(() => [{
746
+ title: 'Basics',
747
+ component: /*#__PURE__*/_jsx(Basics, {})
748
+ }, {
749
+ title: 'Stack Gap',
750
+ component: /*#__PURE__*/_jsx(StackGap, {})
751
+ }, {
752
+ title: 'Border Radius',
753
+ component: /*#__PURE__*/_jsx(BorderRadius, {})
754
+ }, {
755
+ title: 'Sparse Data',
756
+ component: /*#__PURE__*/_jsx(DataExample, {})
757
+ }, {
758
+ title: 'Bar Stack Spacing',
759
+ component: /*#__PURE__*/_jsx(BarStackSpacing, {})
760
+ }, {
761
+ title: 'Minimum Bar Size',
762
+ component: /*#__PURE__*/_jsx(MinimumBarSize, {})
763
+ }, {
764
+ title: 'Taxes style',
765
+ component: /*#__PURE__*/_jsx(TaxesStyleConfirmedVsNeedReview, {})
766
+ }, {
767
+ title: 'Slanted stack gap',
768
+ component: /*#__PURE__*/_jsx(SlantedStackGap, {})
769
+ }, {
770
+ title: 'Dotted bar (first series only)',
771
+ component: /*#__PURE__*/_jsx(DottedBarFirstSeriesOnly, {})
772
+ }, {
773
+ title: 'Dotted bar (chart-level)',
774
+ component: /*#__PURE__*/_jsx(DottedBarChartLevel, {})
775
+ }, {
776
+ title: 'Animations',
777
+ component: /*#__PURE__*/_jsx(Animations, {})
778
+ }, {
779
+ title: 'Live-updating Data',
780
+ component: /*#__PURE__*/_jsx(LiveUpdatingData, {})
781
+ }, {
782
+ title: 'Vertical Mix',
783
+ component: /*#__PURE__*/_jsx(VerticalMix, {})
784
+ }, {
785
+ title: 'Buy vs Sell',
786
+ component: /*#__PURE__*/_jsx(BuyVsSell, {})
787
+ }], []);
788
+ const currentExample = examples[currentIndex];
789
+ const handlePrevious = useCallback(() => {
790
+ setCurrentIndex(prev => (prev - 1 + examples.length) % examples.length);
791
+ }, [examples.length]);
792
+ const handleNext = useCallback(() => {
793
+ setCurrentIndex(prev => (prev + 1 + examples.length) % examples.length);
794
+ }, [examples.length]);
795
+ return /*#__PURE__*/_jsx(ExampleScreen, {
796
+ paddingX: 0,
797
+ children: /*#__PURE__*/_jsxs(VStack, {
798
+ gap: 4,
799
+ children: [/*#__PURE__*/_jsxs(HStack, {
800
+ alignItems: "center",
801
+ justifyContent: "space-between",
802
+ padding: 2,
803
+ children: [/*#__PURE__*/_jsx(IconButton, {
804
+ accessibilityHint: "Navigate to previous example",
805
+ accessibilityLabel: "Previous",
806
+ name: "arrowLeft",
807
+ onPress: handlePrevious,
808
+ variant: "secondary"
809
+ }), /*#__PURE__*/_jsxs(VStack, {
810
+ alignItems: "center",
811
+ children: [/*#__PURE__*/_jsx(Text, {
812
+ font: "title3",
813
+ children: currentExample.title
814
+ }), /*#__PURE__*/_jsxs(Text, {
815
+ color: "fgMuted",
816
+ font: "label1",
817
+ children: [currentIndex + 1, " / ", examples.length]
818
+ })]
819
+ }), /*#__PURE__*/_jsx(IconButton, {
820
+ accessibilityHint: "Navigate to next example",
821
+ accessibilityLabel: "Next",
822
+ name: "arrowRight",
823
+ onPress: handleNext,
824
+ variant: "secondary"
825
+ })]
826
+ }), /*#__PURE__*/_jsx(Box, {
827
+ padding: 1,
828
+ children: currentExample.component
829
+ })]
830
+ })
831
+ });
832
+ }
833
+ export default ExampleNavigator;