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

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 +128 -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 +215 -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,1718 +1,1133 @@
1
- const _excluded = ["label"],
1
+ const _excluded = ["dataX", "dataY"],
2
2
  _excluded2 = ["label"],
3
- _excluded3 = ["style"],
4
- _excluded4 = ["dataX"];
5
- function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
3
+ _excluded3 = ["style"];
6
4
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
7
- import { forwardRef, memo, useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef, useState } from 'react';
8
- import { Defs, LinearGradient, Stop } from 'react-native-svg';
9
- import { assets } from '@coinbase/cds-common/internal/data/assets';
10
- import { candles as btcCandles } from '@coinbase/cds-common/internal/data/candles';
5
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
6
+ import { forwardRef, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
+ import { useAnimatedReaction, useDerivedValue, useSharedValue, withDelay, withTiming } from 'react-native-reanimated';
8
+ import { assets, ethBackground } from '@coinbase/cds-common/internal/data/assets';
11
9
  import { prices } from '@coinbase/cds-common/internal/data/prices';
12
10
  import { sparklineInteractiveData } from '@coinbase/cds-common/internal/visualizations/SparklineInteractiveData';
13
11
  import { useTabsContext } from '@coinbase/cds-common/tabs/TabsContext';
12
+ import { NoopFn } from '@coinbase/cds-common/utils/mockUtils';
14
13
  import { useTheme } from '@coinbase/cds-mobile';
15
- import { Button } from '@coinbase/cds-mobile/buttons';
16
- import { Example, ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
14
+ import { DataCard } from '@coinbase/cds-mobile/alpha/data-card/DataCard';
15
+ import { IconButton } from '@coinbase/cds-mobile/buttons';
16
+ import { ListCell } from '@coinbase/cds-mobile/cells';
17
+ import { ExampleScreen } from '@coinbase/cds-mobile/examples/ExampleScreen';
17
18
  import { Box, HStack, VStack } from '@coinbase/cds-mobile/layout';
18
- import { RemoteImage } from '@coinbase/cds-mobile/media';
19
+ import { Avatar, RemoteImage } from '@coinbase/cds-mobile/media';
19
20
  import { SectionHeader } from '@coinbase/cds-mobile/section-header/SectionHeader';
20
- import { SegmentedTabs } from '@coinbase/cds-mobile/tabs';
21
+ import { Pressable } from '@coinbase/cds-mobile/system';
21
22
  import { SegmentedTab } from '@coinbase/cds-mobile/tabs/SegmentedTab';
22
- import { TextLabel1 } from '@coinbase/cds-mobile/typography';
23
- import { Text } from '@coinbase/cds-mobile/typography/Text';
24
- import { Area, DottedArea, GradientArea } from '../../area';
25
- import { XAxis, YAxis } from '../../axis';
23
+ import { Text } from '@coinbase/cds-mobile/typography';
24
+ import { Circle, FontWeight, Group, Skia, TextAlign } from '@shopify/react-native-skia';
25
+ import { Area, DottedArea } from '../../area';
26
+ import { DefaultAxisTickLabel, XAxis, YAxis } from '../../axis';
26
27
  import { CartesianChart } from '../../CartesianChart';
27
28
  import { useCartesianChartContext } from '../../ChartProvider';
28
29
  import { PeriodSelector, PeriodSelectorActiveIndicator } from '../../PeriodSelector';
29
- import { Point } from '../../Point';
30
- import { Scrubber } from '../../scrubber';
31
- import { GradientLine, Line, LineChart, ReferenceLine } from '..';
30
+ import { Point } from '../../point';
31
+ import { DefaultScrubberBeacon, Scrubber } from '../../scrubber';
32
+ import { buildTransition, defaultTransition, projectPointWithSerializableScale, unwrapAnimatedValue, useScrubberContext } from '../../utils';
33
+ import { DottedLine, Line, LineChart, ReferenceLine, SolidLine } from '..';
32
34
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
33
- const defaultChartHeight = 200;
34
- const formatChartDate = (timestamp, timeframe) => {
35
- const date = new Date(timestamp);
36
- switch (timeframe) {
37
- case 'hour':
38
- case '1H':
39
- return date.toLocaleString('en-US', {
40
- hour: 'numeric',
41
- minute: '2-digit',
42
- hour12: true
43
- });
44
- case 'day':
45
- case '1D':
46
- return date.toLocaleString('en-US', {
47
- month: 'short',
48
- day: 'numeric',
49
- hour: 'numeric',
50
- minute: '2-digit',
51
- hour12: true
52
- });
53
- case 'week':
54
- case 'month':
55
- case '1W':
56
- case '1M':
57
- return date.toLocaleDateString('en-US', {
58
- month: 'short',
59
- day: 'numeric',
60
- year: 'numeric'
61
- });
62
- case 'year':
63
- case 'all':
64
- case '1Y':
65
- case 'All':
66
- return date.toLocaleDateString('en-US', {
67
- month: 'short',
68
- year: 'numeric'
69
- });
70
- default:
71
- return date.toLocaleDateString('en-US');
72
- }
73
- };
74
- const calculateTrendData = (scrubberPosition, currentData, currentTimestamps, startPrice, currentPrice, activeTimeframe) => {
75
- if (scrubberPosition !== undefined) {
76
- // When hovering, show trend relative to START of time period (not previous point)
77
- const hoverIndex = scrubberPosition;
78
- const hoverPrice = currentData[hoverIndex];
79
- const hoverPriceChange = hoverPrice - startPrice; // Fixed: relative to start price
80
- const hoverTimestamp = currentTimestamps[hoverIndex];
81
- return {
82
- trendPrice: hoverPrice,
83
- trendPreviousPrice: startPrice,
84
- // Fixed: always use start price
85
- trendDirection: hoverPriceChange > 0 ? 'up' : hoverPriceChange < 0 ? 'down' : 'neutral',
86
- displayDate: formatChartDate(hoverTimestamp, activeTimeframe)
87
- };
88
- } else {
89
- // When not hovering, show current trend relative to start
90
- const latestTimestamp = currentTimestamps[currentTimestamps.length - 1];
91
- const priceChange = currentPrice - startPrice;
92
- return {
93
- trendPrice: currentPrice,
94
- trendPreviousPrice: startPrice,
95
- trendDirection: priceChange > 0 ? 'up' : priceChange < 0 ? 'down' : 'neutral',
96
- displayDate: formatChartDate(latestTimestamp, activeTimeframe)
97
- };
98
- }
99
- };
100
- export const BasicLineChart = () => {
101
- const chartData = [65, 78, 45, 88, 92, 73, 69];
35
+ function MultipleLine() {
36
+ const theme = useTheme();
37
+ const [scrubberPosition, setScrubberPosition] = useState();
38
+ const pages = useMemo(() => ['Page A', 'Page B', 'Page C', 'Page D', 'Page E', 'Page F', 'Page G'], []);
39
+ const pageViews = useMemo(() => [2400, 1398, 9800, 3908, 4800, 3800, 4300], []);
40
+ const uniqueVisitors = useMemo(() => [4000, 3000, 2000, 2780, 1890, 2390, 3490], []);
41
+ const chartAccessibilityLabel = "Website visitors across " + pageViews.length + " pages.";
42
+ const scrubberAccessibilityLabel = useCallback(index => {
43
+ return pages[index] + " has " + pageViews[index] + " views and " + uniqueVisitors[index] + " unique visitors.";
44
+ }, [pages, pageViews, uniqueVisitors]);
45
+ const numberFormatter = useCallback(value => new Intl.NumberFormat('en-US', {
46
+ maximumFractionDigits: 0
47
+ }).format(value), []);
48
+ const accessibilityLabel = useMemo(() => {
49
+ if (scrubberPosition !== undefined) {
50
+ return scrubberAccessibilityLabel(scrubberPosition);
51
+ }
52
+ return chartAccessibilityLabel;
53
+ }, [scrubberPosition, chartAccessibilityLabel, scrubberAccessibilityLabel]);
102
54
  return /*#__PURE__*/_jsx(LineChart, {
103
55
  enableScrubbing: true,
56
+ showArea: true,
57
+ showXAxis: true,
104
58
  showYAxis: true,
105
- height: defaultChartHeight,
106
- renderPoints: () => true,
59
+ accessibilityLabel: accessibilityLabel,
60
+ height: 200,
61
+ onScrubberPositionChange: setScrubberPosition,
107
62
  series: [{
108
- id: 'monthly-growth',
109
- data: chartData,
110
- label: 'Monthly Growth',
111
- color: '#2ca02c'
63
+ id: 'pageViews',
64
+ data: pageViews,
65
+ color: theme.color.accentBoldGreen,
66
+ // Label will render next to scrubber beacon
67
+ label: 'Page Views'
68
+ }, {
69
+ id: 'uniqueVisitors',
70
+ data: uniqueVisitors,
71
+ color: theme.color.accentBoldPurple,
72
+ label: 'Unique Visitors',
73
+ // Default area is gradient
74
+ areaType: 'dotted'
112
75
  }],
76
+ xAxis: {
77
+ // Used on the x-axis to provide context for each index from the series data array
78
+ data: pages
79
+ },
113
80
  yAxis: {
114
- requestedTickCount: 2,
115
- tickLabelFormatter: value => "$" + value,
116
- showGrid: true
81
+ showGrid: true,
82
+ tickLabelFormatter: numberFormatter
117
83
  },
118
84
  children: /*#__PURE__*/_jsx(Scrubber, {})
119
85
  });
120
- };
121
- export const BasicLineChartWithPoints = () => {
122
- const chartData = [65, 78, 45, 88, 92, 73, 69];
123
- return /*#__PURE__*/_jsxs(LineChart, {
86
+ }
87
+ function DataFormat() {
88
+ const [scrubberPosition, setScrubberPosition] = useState();
89
+ const yData = useMemo(() => [2, 5.5, 2, 8.5, 1.5, 5], []);
90
+ const xData = useMemo(() => [1, 2, 3, 5, 8, 10], []);
91
+ const chartAccessibilityLabel = "Chart with custom X and Y data. " + yData.length + " data points";
92
+ const scrubberAccessibilityLabel = useCallback(index => {
93
+ return "Point " + (index + 1) + ": X value " + xData[index] + ", Y value " + yData[index];
94
+ }, [xData, yData]);
95
+ const accessibilityLabel = useMemo(() => {
96
+ if (scrubberPosition !== undefined) {
97
+ return scrubberAccessibilityLabel(scrubberPosition);
98
+ }
99
+ return chartAccessibilityLabel;
100
+ }, [scrubberPosition, chartAccessibilityLabel, scrubberAccessibilityLabel]);
101
+ return /*#__PURE__*/_jsx(LineChart, {
102
+ enableScrubbing: true,
103
+ points: true,
104
+ showArea: true,
105
+ showXAxis: true,
124
106
  showYAxis: true,
125
- height: defaultChartHeight,
126
- renderPoints: () => true,
107
+ accessibilityLabel: accessibilityLabel,
108
+ curve: "natural",
109
+ height: 200,
110
+ inset: {
111
+ top: 16,
112
+ right: 16,
113
+ bottom: 0,
114
+ left: 0
115
+ },
116
+ onScrubberPositionChange: setScrubberPosition,
127
117
  series: [{
128
- id: 'monthly-growth',
129
- data: chartData,
130
- label: 'Monthly Growth',
131
- color: '#2ca02c'
118
+ id: 'line',
119
+ data: yData
132
120
  }],
121
+ xAxis: {
122
+ data: xData,
123
+ showLine: true,
124
+ showTickMarks: true,
125
+ showGrid: true
126
+ },
133
127
  yAxis: {
134
- requestedTickCount: 2,
135
- tickLabelFormatter: value => "$" + value,
128
+ domain: {
129
+ min: 0
130
+ },
131
+ position: 'left',
132
+ showLine: true,
133
+ showTickMarks: true,
136
134
  showGrid: true
137
135
  },
138
- children: [/*#__PURE__*/_jsx(Point, {
139
- dataX: 2,
140
- dataY: 60,
141
- fill: "purple",
142
- label: "hello world im on a point!",
143
- labelProps: {
144
- verticalAlignment: 'bottom',
145
- // why does this go in the opposite direction than what i would expect?
146
- dy: -16
136
+ children: /*#__PURE__*/_jsx(Scrubber, {
137
+ hideOverlay: true
138
+ })
139
+ });
140
+ }
141
+ function LiveUpdates() {
142
+ const scrubberRef = useRef(null);
143
+ const initialData = useMemo(() => {
144
+ return sparklineInteractiveData.hour.map(d => d.value);
145
+ }, []);
146
+ const [priceData, setPriceData] = useState(initialData);
147
+ const lastDataPointTimeRef = useRef(Date.now());
148
+ const updateCountRef = useRef(0);
149
+ const intervalSeconds = 3600 / initialData.length;
150
+ const maxPercentChange = Math.abs(initialData[initialData.length - 1] - initialData[0]) * 0.05;
151
+ useEffect(() => {
152
+ const priceUpdateInterval = setInterval(() => {
153
+ var _scrubberRef$current;
154
+ setPriceData(currentData => {
155
+ const newData = [...currentData];
156
+ const lastPrice = newData[newData.length - 1];
157
+ const priceChange = (Math.random() - 0.5) * maxPercentChange;
158
+ const newPrice = Math.round((lastPrice + priceChange) * 100) / 100;
159
+
160
+ // Check if we should roll over to a new data point
161
+ const currentTime = Date.now();
162
+ const timeSinceLastPoint = (currentTime - lastDataPointTimeRef.current) / 1000;
163
+ if (timeSinceLastPoint >= intervalSeconds) {
164
+ // Time for a new data point - remove first, add new at end
165
+ lastDataPointTimeRef.current = currentTime;
166
+ newData.shift(); // Remove oldest data point
167
+ newData.push(newPrice); // Add new data point
168
+ updateCountRef.current = 0;
169
+ } else {
170
+ // Just update the last data point
171
+ newData[newData.length - 1] = newPrice;
172
+ updateCountRef.current++;
173
+ }
174
+ return newData;
175
+ });
176
+
177
+ // Pulse the scrubber on each update
178
+ (_scrubberRef$current = scrubberRef.current) == null || _scrubberRef$current.pulse();
179
+ }, 2000 + Math.random() * 1000);
180
+ return () => clearInterval(priceUpdateInterval);
181
+ }, [intervalSeconds, maxPercentChange]);
182
+ return /*#__PURE__*/_jsx(LineChart, {
183
+ enableScrubbing: true,
184
+ showArea: true,
185
+ height: 200,
186
+ inset: {
187
+ right: 64
188
+ },
189
+ series: [{
190
+ id: 'btc',
191
+ data: priceData,
192
+ color: assets.btc.color
193
+ }],
194
+ children: /*#__PURE__*/_jsx(Scrubber, {
195
+ ref: scrubberRef
196
+ })
197
+ });
198
+ }
199
+ function MissingData() {
200
+ const theme = useTheme();
201
+ const pages = ['Page A', 'Page B', 'Page C', 'Page D', 'Page E', 'Page F', 'Page G'];
202
+ const pageViews = [2400, 1398, null, 3908, 4800, 3800, 4300];
203
+ const uniqueVisitors = [4000, 3000, null, 2780, 1890, 2390, 3490];
204
+ const numberFormatter = useCallback(value => new Intl.NumberFormat('en-US', {
205
+ maximumFractionDigits: 0
206
+ }).format(value), []);
207
+ return /*#__PURE__*/_jsx(LineChart, {
208
+ enableScrubbing: true,
209
+ points: true,
210
+ showArea: true,
211
+ showXAxis: true,
212
+ showYAxis: true,
213
+ height: 200,
214
+ series: [{
215
+ id: 'pageViews',
216
+ data: pageViews,
217
+ color: theme.color.accentBoldGreen,
218
+ // Label will render next to scrubber beacon
219
+ label: 'Page Views',
220
+ connectNulls: true
221
+ }, {
222
+ id: 'uniqueVisitors',
223
+ data: uniqueVisitors,
224
+ color: theme.color.accentBoldPurple,
225
+ label: 'Unique Visitors'
226
+ }],
227
+ xAxis: {
228
+ // Used on the x-axis to provide context for each index from the series data array
229
+ data: pages
230
+ },
231
+ yAxis: {
232
+ showGrid: true,
233
+ tickLabelFormatter: numberFormatter
234
+ },
235
+ children: /*#__PURE__*/_jsx(Scrubber, {
236
+ overlayOffset: 6
237
+ })
238
+ });
239
+ }
240
+ function Interaction() {
241
+ const [scrubberPosition, setScrubberPosition] = useState();
242
+ return /*#__PURE__*/_jsxs(VStack, {
243
+ gap: 2,
244
+ children: [/*#__PURE__*/_jsx(Text, {
245
+ font: "label1",
246
+ children: scrubberPosition !== undefined ? "Scrubber position: " + scrubberPosition : 'Not scrubbing'
247
+ }), /*#__PURE__*/_jsx(LineChart, {
248
+ enableScrubbing: true,
249
+ showArea: true,
250
+ height: 200,
251
+ onScrubberPositionChange: setScrubberPosition,
252
+ series: [{
253
+ id: 'prices',
254
+ data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58]
255
+ }],
256
+ children: /*#__PURE__*/_jsx(Scrubber, {})
257
+ })]
258
+ });
259
+ }
260
+ function Points() {
261
+ const theme = useTheme();
262
+ const keyMarketShiftIndices = [4, 6, 7, 9, 10];
263
+ const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
264
+ return /*#__PURE__*/_jsxs(CartesianChart, {
265
+ height: 200,
266
+ series: [{
267
+ id: 'prices',
268
+ data: data
269
+ }],
270
+ children: [/*#__PURE__*/_jsx(Area, {
271
+ fill: "rgb(" + theme.spectrum.blue5 + ")",
272
+ seriesId: "prices"
273
+ }), /*#__PURE__*/_jsx(Line, {
274
+ points: _ref => {
275
+ let {
276
+ dataX,
277
+ dataY
278
+ } = _ref,
279
+ props = _objectWithoutPropertiesLoose(_ref, _excluded);
280
+ return keyMarketShiftIndices.includes(dataX) ? _extends({}, props, {
281
+ strokeWidth: 2,
282
+ stroke: theme.color.bg,
283
+ radius: 5,
284
+ onClick: () => alert("You have clicked a key market shift at position " + (dataX + 1) + " with value " + dataY + "!"),
285
+ accessibilityLabel: "Key market shift point at position " + (dataX + 1) + ", value " + dataY + ". Click to view details."
286
+ }) : false;
147
287
  },
148
- onPress: () => console.log('clicked'),
149
- radius: 6,
150
- stroke: "purple",
151
- strokeWidth: 7
152
- }), /*#__PURE__*/_jsx(ReferenceLine, {
153
- dataX: 2,
154
- label: "testing 123",
155
- labelProps: {
156
- color: '#10b981',
157
- inset: 0,
158
- verticalAlignment: 'middle'
159
- }
160
- }), /*#__PURE__*/_jsx(ReferenceLine, {
161
- dataY: 60,
162
- label: "testing 123",
163
- labelProps: {
164
- horizontalAlignment: 'left'
165
- }
166
- }), /*#__PURE__*/_jsx(Point, {
167
- dataX: 5,
168
- dataY: 50,
169
- fill: "orange",
170
- radius: 5
288
+ seriesId: "prices"
171
289
  })]
172
290
  });
173
- };
174
- export const AssetPrice = () => {
175
- const pricePointsPerHour = 12;
176
- const currentHour = 14;
177
- const pricePointsToShow = currentHour * pricePointsPerHour;
178
- const parsedPrices = useMemo(() => prices.slice(0, pricePointsToShow).map(price => parseFloat(price)), [pricePointsToShow]);
179
- const [highlightedItemIndex, setHighlightedItemIndex] = useState(undefined);
180
- const isHovering = useMemo(() => typeof highlightedItemIndex === 'number' && highlightedItemIndex < pricePointsToShow, [highlightedItemIndex, pricePointsToShow]);
181
- const indexToTime = useCallback(index => {
182
- const date = new Date();
183
- date.setHours(0, 0, 0, 0);
184
- date.setMinutes(index * 5);
185
- return date.toLocaleTimeString('en-US', {
186
- hour: 'numeric',
187
- minute: '2-digit',
188
- hour12: true
189
- });
190
- }, []);
191
- const onScrubberPositionChange = useCallback(scrubberPosition => {
192
- setHighlightedItemIndex(scrubberPosition);
193
- }, []);
194
- const highlightedPrice = useMemo(() => {
195
- const price = isHovering && typeof highlightedItemIndex === 'number' ? prices[highlightedItemIndex] : prices[prices.length - 1];
196
- return new Intl.NumberFormat('en-US', {
291
+ }
292
+ function Transitions() {
293
+ const theme = useTheme();
294
+ const dataCount = 20;
295
+ const maxDataOffset = 15000;
296
+ const minStepOffset = 2500;
297
+ const maxStepOffset = 10000;
298
+ const domainLimit = 20000;
299
+ const updateInterval = 500;
300
+ const myTransitionConfig = {
301
+ type: 'spring',
302
+ stiffness: 700,
303
+ damping: 20
304
+ };
305
+ const negativeColor = "rgb(" + theme.spectrum.gray15 + ")";
306
+ const positiveColor = theme.color.fgPositive;
307
+ function generateNextValue(previousValue) {
308
+ const range = maxStepOffset - minStepOffset;
309
+ const offset = Math.random() * range + minStepOffset;
310
+ let direction;
311
+ if (previousValue >= maxDataOffset) {
312
+ direction = -1;
313
+ } else if (previousValue <= -maxDataOffset) {
314
+ direction = 1;
315
+ } else {
316
+ direction = Math.random() < 0.5 ? -1 : 1;
317
+ }
318
+ let newValue = previousValue + offset * direction;
319
+ newValue = Math.max(-maxDataOffset, Math.min(maxDataOffset, newValue));
320
+ return newValue;
321
+ }
322
+ function generateInitialData() {
323
+ const data = [];
324
+ let previousValue = Math.random() * 2 * maxDataOffset - maxDataOffset;
325
+ data.push(previousValue);
326
+ for (let i = 1; i < dataCount; i++) {
327
+ const newValue = generateNextValue(previousValue);
328
+ data.push(newValue);
329
+ previousValue = newValue;
330
+ }
331
+ return data;
332
+ }
333
+ const MyGradient = /*#__PURE__*/memo(props => {
334
+ const areaGradient = {
335
+ stops: _ref2 => {
336
+ let {
337
+ min,
338
+ max
339
+ } = _ref2;
340
+ return [{
341
+ offset: min,
342
+ color: negativeColor,
343
+ opacity: 1
344
+ }, {
345
+ offset: 0,
346
+ color: negativeColor,
347
+ opacity: 0
348
+ }, {
349
+ offset: 0,
350
+ color: positiveColor,
351
+ opacity: 0
352
+ }, {
353
+ offset: max,
354
+ color: positiveColor,
355
+ opacity: 1
356
+ }];
357
+ }
358
+ };
359
+ return /*#__PURE__*/_jsx(DottedArea, _extends({}, props, {
360
+ gradient: areaGradient
361
+ }));
362
+ });
363
+ function CustomTransitionsChart() {
364
+ const [data, setData] = useState(generateInitialData);
365
+ useEffect(() => {
366
+ const intervalId = setInterval(() => {
367
+ setData(currentData => {
368
+ var _currentData;
369
+ const lastValue = (_currentData = currentData[currentData.length - 1]) != null ? _currentData : 0;
370
+ const newValue = generateNextValue(lastValue);
371
+ return [...currentData.slice(1), newValue];
372
+ });
373
+ }, updateInterval);
374
+ return () => clearInterval(intervalId);
375
+ }, []);
376
+ const tickLabelFormatter = useCallback(value => new Intl.NumberFormat('en-US', {
197
377
  style: 'currency',
198
- currency: 'USD'
199
- }).format(parseFloat(price));
200
- }, [highlightedItemIndex, isHovering]);
201
-
202
- // Calculate trend information
203
- const trendInfo = useMemo(() => {
204
- const currentPrice = isHovering && typeof highlightedItemIndex === 'number' ? parseFloat(prices[highlightedItemIndex]) : parseFloat(prices[prices.length - 1]);
205
- const startPrice = parseFloat(prices[0]);
206
- const priceChange = currentPrice - startPrice;
207
- const percentChange = priceChange / startPrice * 100;
208
- const trendDirection = priceChange > 0 ? 'up' : priceChange < 0 ? 'down' : 'neutral';
209
- const formattedPriceChange = new Intl.NumberFormat('en-US', {
378
+ currency: 'USD',
379
+ maximumFractionDigits: 0
380
+ }).format(value), []);
381
+ const valueAtIndexFormatter = useCallback(dataIndex => new Intl.NumberFormat('en-US', {
210
382
  style: 'currency',
211
383
  currency: 'USD'
212
- }).format(Math.abs(priceChange));
213
- const formattedPercentChange = Math.abs(percentChange).toFixed(2) + "%";
214
- return {
215
- direction: trendDirection,
216
- text: formattedPriceChange + " (" + formattedPercentChange + ")"
384
+ }).format(data[dataIndex]), [data]);
385
+ const lineGradient = {
386
+ stops: [{
387
+ offset: 0,
388
+ color: negativeColor
389
+ }, {
390
+ offset: 0,
391
+ color: positiveColor
392
+ }]
217
393
  };
218
- }, [highlightedItemIndex, isHovering]);
219
- return /*#__PURE__*/_jsx(VStack, {
220
- gap: 2,
221
- children: /*#__PURE__*/_jsxs(LineChart, {
222
- showArea: true,
223
- height: defaultChartHeight,
394
+ return /*#__PURE__*/_jsxs(CartesianChart, {
395
+ enableScrubbing: true,
396
+ height: 200,
224
397
  inset: {
225
- top: 4,
226
- bottom: 8,
227
- left: 0,
228
- right: 0
398
+ top: 32,
399
+ bottom: 32,
400
+ left: 16,
401
+ right: 16
229
402
  },
230
- onScrubberPositionChange: onScrubberPositionChange,
231
403
  series: [{
232
- id: 'price',
233
- data: parsedPrices,
234
- color: assets.btc.color
404
+ id: 'prices',
405
+ data: data,
406
+ gradient: lineGradient
235
407
  }],
236
- xAxis: {
408
+ yAxis: {
237
409
  domain: {
238
- min: 0,
239
- max: pricePointsPerHour * 24
410
+ min: -domainLimit,
411
+ max: domainLimit
240
412
  }
241
413
  },
242
- children: [/*#__PURE__*/_jsx(XAxis, {
243
- tickLabelFormatter: index => indexToTime(index).slice(0, -3),
244
- ticks: index => index % (12 * 6) === 0
245
- }), /*#__PURE__*/_jsx(ReferenceLine, {
246
- dataY: parsedPrices[0]
414
+ children: [/*#__PURE__*/_jsx(YAxis, {
415
+ showGrid: true,
416
+ requestedTickCount: 2,
417
+ tickLabelFormatter: tickLabelFormatter
418
+ }), /*#__PURE__*/_jsx(Line, {
419
+ showArea: true,
420
+ AreaComponent: MyGradient,
421
+ seriesId: "prices",
422
+ strokeWidth: 3,
423
+ transition: myTransitionConfig
424
+ }), /*#__PURE__*/_jsx(Scrubber, {
425
+ hideOverlay: true,
426
+ beaconTransitions: {
427
+ update: myTransitionConfig
428
+ },
429
+ label: valueAtIndexFormatter
247
430
  })]
248
- })
249
- });
250
- };
251
- export const LineStyles = () => {
252
- const topChartData = [15, 28, 32, 44, 46, 36, 40, 45, 48, 38];
253
- const upperMiddleChartData = [12, 23, 21, 29, 34, 28, 31, 38, 42, 35];
254
- const lowerMiddleChartData = [8, 15, 14, 25, 20, 18, 22, 28, 24, 30];
255
- const bottomChartData = [4, 8, 11, 15, 16, 14, 16, 10, 12, 14];
256
- return /*#__PURE__*/_jsxs(CartesianChart, {
257
- height: defaultChartHeight,
431
+ });
432
+ }
433
+ return /*#__PURE__*/_jsx(CustomTransitionsChart, {});
434
+ }
435
+ function BasicAccessible() {
436
+ const [scrubberPosition, setScrubberPosition] = useState();
437
+ const data = useMemo(() => [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58], []);
438
+
439
+ // Chart-level accessibility label provides overview
440
+ const chartAccessibilityLabel = useMemo(() => {
441
+ const currentPrice = data[data.length - 1];
442
+ return "Price chart showing trend over " + data.length + " data points. Current value: " + currentPrice + ". Use arrow keys to adjust view";
443
+ }, [data]);
444
+
445
+ // Scrubber-level accessibility label provides specific position info
446
+ const scrubberAccessibilityLabel = useCallback(index => {
447
+ return "Price at position " + (index + 1) + " of " + data.length + ": " + data[index];
448
+ }, [data]);
449
+ const accessibilityLabel = useMemo(() => {
450
+ if (scrubberPosition !== undefined) {
451
+ return scrubberAccessibilityLabel(scrubberPosition);
452
+ }
453
+ return chartAccessibilityLabel;
454
+ }, [scrubberPosition, chartAccessibilityLabel, scrubberAccessibilityLabel]);
455
+ return /*#__PURE__*/_jsx(LineChart, {
456
+ enableScrubbing: true,
457
+ showArea: true,
458
+ showYAxis: true,
459
+ accessibilityLabel: accessibilityLabel,
460
+ height: 200,
461
+ onScrubberPositionChange: setScrubberPosition,
258
462
  series: [{
259
- id: 'top',
260
- data: topChartData
261
- }, {
262
- id: 'upperMiddle',
263
- data: upperMiddleChartData,
264
- color: '#ef4444'
265
- }, {
266
- id: 'lowerMiddle',
267
- data: lowerMiddleChartData,
268
- color: '#f59e0b'
269
- }, {
270
- id: 'bottom',
271
- data: bottomChartData,
272
- color: '#800080'
463
+ id: 'prices',
464
+ data: data
273
465
  }],
274
- children: [/*#__PURE__*/_jsx(Line, {
275
- seriesId: "top"
276
- }), /*#__PURE__*/_jsx(Line, {
277
- seriesId: "upperMiddle",
278
- type: "dotted"
279
- }), /*#__PURE__*/_jsx(Line, {
280
- LineComponent: props => /*#__PURE__*/_jsx(GradientLine, _extends({}, props, {
281
- endColor: "#F7931A",
282
- startColor: "#E3D74D",
283
- strokeWidth: 4
284
- })),
285
- curve: "natural",
286
- seriesId: "lowerMiddle"
287
- }), /*#__PURE__*/_jsx(Line, {
288
- showArea: true,
289
- AreaComponent: DottedArea,
290
- curve: "step",
291
- seriesId: "bottom"
292
- })]
466
+ yAxis: {
467
+ showGrid: true
468
+ },
469
+ children: /*#__PURE__*/_jsx(Scrubber, {})
293
470
  });
294
- };
295
- export const ChartScale = () => {
296
- // Generate exponential growth data that benefits from log scaling
297
- const exponentialData = [1, 2, 4, 8, 15, 30, 65, 140, 280, 580, 1200, 2400, 4800, 9500, 19000, 38000, 75000, 150000];
298
- const scaleTypes = [{
299
- id: 'linear',
300
- label: 'Linear'
301
- }, {
302
- id: 'log',
303
- label: 'Log'
304
- }];
305
- const [selectedScaleType, setSelectedScaleType] = useState(scaleTypes[0]);
471
+ }
472
+ function Gradients() {
473
+ const theme = useTheme();
474
+ const spectrumColors = ['blue', 'green', 'orange', 'yellow', 'gray', 'indigo', 'pink', 'purple', 'red', 'teal', 'chartreuse'];
475
+ const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
476
+ const [currentSpectrumColor, setCurrentSpectrumColor] = useState('pink');
306
477
  return /*#__PURE__*/_jsxs(VStack, {
307
- gap: 3,
308
- children: [/*#__PURE__*/_jsx(VStack, {
309
- alignItems: "flex-end",
310
- gap: 2,
311
- children: /*#__PURE__*/_jsxs(HStack, {
312
- alignItems: "center",
313
- gap: 2,
314
- children: [/*#__PURE__*/_jsx(Text, {
315
- font: "label1",
316
- children: "Scale Type"
317
- }), /*#__PURE__*/_jsx(SegmentedTabs, {
318
- activeTab: selectedScaleType,
319
- onChange: setSelectedScaleType,
320
- tabs: scaleTypes
321
- })]
322
- })
478
+ gap: 2,
479
+ children: [/*#__PURE__*/_jsx(HStack, {
480
+ flexWrap: "wrap",
481
+ gap: 1,
482
+ justifyContent: "flex-end",
483
+ children: spectrumColors.map(color => /*#__PURE__*/_jsx(Pressable, {
484
+ accessibilityLabel: "Select " + color,
485
+ height: 16,
486
+ onPress: () => setCurrentSpectrumColor(color),
487
+ style: {
488
+ backgroundColor: "rgb(" + theme.spectrum[color + "20"] + ")",
489
+ borderColor: "rgb(" + theme.spectrum[color + "50"] + ")",
490
+ borderWidth: 2
491
+ },
492
+ width: 16
493
+ }, color))
323
494
  }), /*#__PURE__*/_jsx(LineChart, {
324
- showArea: true,
495
+ points: true,
325
496
  showYAxis: true,
326
- curve: "natural",
327
- height: defaultChartHeight,
497
+ height: 200,
328
498
  series: [{
329
- id: 'growth',
330
- data: exponentialData,
331
- color: '#10b981'
499
+ id: 'continuousGradient',
500
+ data: data,
501
+ gradient: {
502
+ stops: [{
503
+ offset: 0,
504
+ color: "rgb(" + theme.spectrum[currentSpectrumColor + "80"] + ")"
505
+ }, {
506
+ offset: Math.max(...data),
507
+ color: "rgb(" + theme.spectrum[currentSpectrumColor + "20"] + ")"
508
+ }]
509
+ }
510
+ }, {
511
+ id: 'discreteGradient',
512
+ data: data.map(d => d + 50),
513
+ // You can create a "discrete" gradient by having multiple stops at the same offset
514
+ gradient: {
515
+ stops: _ref3 => {
516
+ let {
517
+ min,
518
+ max
519
+ } = _ref3;
520
+ return [
521
+ // Allows a function which accepts min/max or direct array
522
+ {
523
+ offset: min,
524
+ color: "rgb(" + theme.spectrum[currentSpectrumColor + "80"] + ")"
525
+ }, {
526
+ offset: min + (max - min) / 3,
527
+ color: "rgb(" + theme.spectrum[currentSpectrumColor + "80"] + ")"
528
+ }, {
529
+ offset: min + (max - min) / 3,
530
+ color: "rgb(" + theme.spectrum[currentSpectrumColor + "50"] + ")"
531
+ }, {
532
+ offset: min + (max - min) / 3 * 2,
533
+ color: "rgb(" + theme.spectrum[currentSpectrumColor + "50"] + ")"
534
+ }, {
535
+ offset: min + (max - min) / 3 * 2,
536
+ color: "rgb(" + theme.spectrum[currentSpectrumColor + "20"] + ")"
537
+ }, {
538
+ offset: max,
539
+ color: "rgb(" + theme.spectrum[currentSpectrumColor + "20"] + ")"
540
+ }];
541
+ }
542
+ }
543
+ }, {
544
+ id: 'xAxisGradient',
545
+ data: data.map(d => d + 100),
546
+ gradient: {
547
+ // You can also configure by the x-axis.
548
+ axis: 'x',
549
+ stops: _ref4 => {
550
+ let {
551
+ min,
552
+ max
553
+ } = _ref4;
554
+ return [{
555
+ offset: min,
556
+ color: "rgb(" + theme.spectrum[currentSpectrumColor + "80"] + ")",
557
+ opacity: 0
558
+ }, {
559
+ offset: max,
560
+ color: "rgb(" + theme.spectrum[currentSpectrumColor + "20"] + ")",
561
+ opacity: 1
562
+ }];
563
+ }
564
+ }
332
565
  }],
566
+ strokeWidth: 4,
333
567
  yAxis: {
334
- scaleType: selectedScaleType == null ? void 0 : selectedScaleType.id,
335
- requestedTickCount: 5,
336
- tickLabelFormatter: value => value.toLocaleString(),
337
- showGrid: true,
338
- width: 70
568
+ showGrid: true
339
569
  }
340
570
  })]
341
571
  });
342
- };
343
- export const ColorShiftChart = () => {
572
+ }
573
+ function GainLossChart() {
344
574
  const theme = useTheme();
345
- const [activeTab, setActiveTab] = useState({
346
- id: '1H',
347
- label: '1H'
348
- });
349
- const tabConversion = {
350
- '1H': 'hour',
351
- '1D': 'day',
352
- '1W': 'week',
353
- '1M': 'month',
354
- '1Y': 'year',
355
- All: 'all'
356
- };
357
- const currentPriceData = activeTab ? sparklineInteractiveData[tabConversion[activeTab.id]] : sparklineInteractiveData.hour;
358
- const currentData = useMemo(() => [...currentPriceData.map(price => price.value)], [currentPriceData]);
359
- const currentTimestamps = useMemo(() => [...currentPriceData.map(price => price.date.toISOString())], [currentPriceData]);
360
- const startPrice = currentData[0];
361
- const currentPrice = currentData[currentData.length - 1];
362
- const priceChange = currentPrice - startPrice;
363
-
364
- // Determine colors based on trend
365
- const trendColor = useMemo(() => {
366
- return priceChange >= 0 ? theme.color.fgPositive : theme.color.fgNegative;
367
- }, [priceChange, theme.color.fgPositive, theme.color.fgNegative]);
368
- const activeBackground = useMemo(() => {
369
- return priceChange >= 0 ? 'bgPositiveWash' : 'bgNegativeWash';
370
- }, [priceChange]);
371
- const formatPrice = useCallback(price => {
372
- return "$" + price.toLocaleString('en-US', {
373
- minimumFractionDigits: 2,
374
- maximumFractionDigits: 2
375
- });
376
- }, []);
377
- const scrubberLabel = useCallback(index => {
378
- const timestamp = currentTimestamps[index];
379
- return formatChartDate(timestamp, (activeTab == null ? void 0 : activeTab.id) || '1H');
380
- }, [activeTab == null ? void 0 : activeTab.id, currentTimestamps]);
575
+ const data = useMemo(() => [-40, -28, -21, -5, 48, -5, -28, 2, -29, -46, 16, -30, -29, 8], []);
576
+ const negativeColor = "rgb(" + theme.spectrum.gray15 + ")";
577
+ const positiveColor = theme.color.fgPositive;
578
+ const tickLabelFormatter = useCallback(value => new Intl.NumberFormat('en-US', {
579
+ style: 'currency',
580
+ currency: 'USD',
581
+ maximumFractionDigits: 0
582
+ }).format(value), []);
381
583
 
382
- // Custom tab component that uses the trend color for both active and inactive states
383
- const ColorShiftTab = useMemo(() => /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
384
- let {
385
- label
386
- } = _ref,
387
- props = _objectWithoutPropertiesLoose(_ref, _excluded);
388
- return /*#__PURE__*/_jsx(SegmentedTab, _extends({
389
- ref: ref,
390
- activeColor: trendColor,
391
- color: trendColor,
392
- font: "label1",
393
- label: label
394
- }, props));
395
- })), [trendColor]);
396
- const tabs = useMemo(() => [{
397
- id: '1H',
398
- label: '1H'
399
- }, {
400
- id: '1D',
401
- label: '1D'
402
- }, {
403
- id: '1W',
404
- label: '1W'
405
- }, {
406
- id: '1M',
407
- label: '1M'
408
- }, {
409
- id: '1Y',
410
- label: '1Y'
411
- }, {
412
- id: 'All',
413
- label: 'All'
414
- }], []);
415
- return /*#__PURE__*/_jsx(Box, {
416
- style: {
417
- marginLeft: -16,
418
- marginRight: -16
419
- },
420
- children: /*#__PURE__*/_jsxs(VStack, {
421
- gap: 3,
422
- width: "100%",
423
- children: [/*#__PURE__*/_jsxs(LineChart, {
424
- enableScrubbing: true,
425
- showArea: true,
426
- showXAxis: true,
427
- height: defaultChartHeight,
428
- inset: {
429
- left: 0,
430
- right: 24,
431
- bottom: 0
432
- },
433
- series: [{
434
- id: 'price',
435
- data: currentData,
436
- color: trendColor
437
- }],
438
- children: [/*#__PURE__*/_jsx(Scrubber, {
439
- idlePulse: true,
440
- label: scrubberLabel
441
- }), /*#__PURE__*/_jsx(ReferenceLine, {
442
- dataY: startPrice,
443
- label: formatPrice(startPrice),
444
- labelProps: {
445
- horizontalAlignment: 'right',
446
- inset: 4,
447
- borderRadius: 4,
448
- dx: -8,
449
- color: theme.color.fgInverse,
450
- background: priceChange >= 0 ? theme.color.bgPositive : theme.color.bgNegative
451
- },
452
- stroke: priceChange >= 0 ? theme.color.bgPositive : theme.color.bgNegative
453
- })]
454
- }), /*#__PURE__*/_jsx(PeriodSelector, {
455
- TabComponent: ColorShiftTab,
456
- activeBackground: activeBackground,
457
- activeTab: activeTab,
458
- onChange: setActiveTab,
459
- tabs: tabs
460
- })]
461
- })
462
- });
463
- };
464
- export const PriceChart = () => {
465
- const {
466
- latestPrice,
467
- formattedPrice
468
- } = useMemo(() => {
469
- const latestPrice = sparklineInteractiveData.hour[sparklineInteractiveData.hour.length - 1].value;
470
- return {
471
- latestPrice,
472
- formattedPrice: "$" + latestPrice.toLocaleString('en-US', {
473
- minimumFractionDigits: 2,
474
- maximumFractionDigits: 2
475
- })
476
- };
477
- }, []);
478
- const tabs = useMemo(() => [{
479
- id: '1H',
480
- label: '1H'
481
- }, {
482
- id: '1D',
483
- label: '1D'
484
- }, {
485
- id: '1W',
486
- label: '1W'
487
- }, {
488
- id: '1M',
489
- label: '1M'
490
- }, {
491
- id: '1Y',
492
- label: '1Y'
493
- }, {
494
- id: 'All',
495
- label: 'All'
496
- }], []);
497
- const [activeTab, setActiveTab] = useState(tabs[0]);
498
- const isLive = useMemo(() => (activeTab == null ? void 0 : activeTab.id) === '1H', [activeTab]);
499
- const activeBackground = useMemo(() => !isLive ? 'bgPrimaryWash' : 'bgNegativeWash', [isLive]);
500
- const [isHovering, setIsHovering] = useState(false);
501
- const tabConversion = {
502
- '1H': 'hour',
503
- '1D': 'day',
504
- '1W': 'week',
505
- '1M': 'month',
506
- '1Y': 'year',
507
- All: 'all'
508
- };
509
- const currentPriceData = activeTab ? sparklineInteractiveData[tabConversion[activeTab.id]] : sparklineInteractiveData.hour;
510
- const currentData = useMemo(() => [...currentPriceData.map(price => price.value)], [currentPriceData]);
511
- const currentTimestamps = useMemo(() => [...currentPriceData.map(price => price.date.toISOString())], [currentPriceData]);
512
- const startPrice = currentData[0];
513
- const {
514
- lowestPriceIndices,
515
- highestPriceIndices
516
- } = useMemo(() => {
517
- if (currentData.length === 0) {
518
- return {
519
- lowestPriceIndices: [],
520
- highestPriceIndices: [],
521
- minPrice: 0,
522
- maxPrice: 0
523
- };
524
- }
525
- let minPrice = currentData[0];
526
- let maxPrice = currentData[0];
527
-
528
- // First pass: find min and max values
529
- for (let i = 1; i < currentData.length; i++) {
530
- if (currentData[i] < minPrice) {
531
- minPrice = currentData[i];
532
- }
533
- if (currentData[i] > maxPrice) {
534
- maxPrice = currentData[i];
535
- }
536
- }
537
-
538
- // Second pass: find all indices where min and max occur
539
- const lowestPriceIndices = [];
540
- const highestPriceIndices = [];
541
- for (let i = 0; i < currentData.length; i++) {
542
- if (currentData[i] === minPrice) {
543
- lowestPriceIndices.push(i);
544
- }
545
- if (currentData[i] === maxPrice) {
546
- highestPriceIndices.push(i);
547
- }
548
- }
549
- return {
550
- lowestPriceIndices,
551
- highestPriceIndices,
552
- minPrice,
553
- maxPrice
554
- };
555
- }, [currentData]);
556
- const latestPriceCoords = useMemo(() => {
557
- if (currentData.length === 0) return {};
558
- return {
559
- x: currentData.length - 1,
560
- y: currentData[currentData.length - 1]
561
- };
562
- }, [currentData]);
563
- const onScrubberPositionChange = useCallback(item => {
564
- setIsHovering(item !== undefined);
565
- }, []);
566
- const {
567
- trendPrice,
568
- trendPreviousPrice,
569
- trendDirection
570
- } = useMemo(() => {
571
- return calculateTrendData(undefined, currentData, currentTimestamps, startPrice, latestPrice, (activeTab == null ? void 0 : activeTab.id) || '1H');
572
- }, [currentData, currentTimestamps, startPrice, latestPrice, activeTab]);
573
- const calculatedPriceChange = trendPrice - trendPreviousPrice;
574
- const calculatedPercentChange = calculatedPriceChange / trendPreviousPrice * 100;
575
- const formattedPriceChange = "$" + Math.abs(calculatedPriceChange).toLocaleString('en-US', {
576
- minimumFractionDigits: 2,
577
- maximumFractionDigits: 2
578
- }) + " (" + Math.abs(calculatedPercentChange).toLocaleString('en-US', {
579
- minimumFractionDigits: 2,
580
- maximumFractionDigits: 2
581
- }) + "%)";
582
- const scrubberLabel = useCallback(item => {
583
- if (item === undefined) return null;
584
- const timestamp = currentTimestamps[item];
585
- const price = currentData[item];
586
- const formattedPrice = price.toLocaleString('en-US', {
587
- minimumFractionDigits: 2,
588
- maximumFractionDigits: 2
589
- }) + ' USD';
590
- const formattedDate = formatChartDate(timestamp, (activeTab == null ? void 0 : activeTab.id) || '1H');
591
- return /*#__PURE__*/_jsxs(_Fragment, {
592
- children: [/*#__PURE__*/_jsx("tspan", {
593
- style: {
594
- fontWeight: 'bold',
595
- display: 'inline-block'
596
- },
597
- children: formattedPrice
598
- }), /*#__PURE__*/_jsxs("tspan", {
599
- style: {
600
- display: 'inline-block'
601
- },
602
- children: [" ", formattedDate]
603
- })]
604
- });
605
- }, [currentTimestamps, currentData, activeTab == null ? void 0 : activeTab.id]);
606
- const formatPrice = useCallback(value => {
607
- return "$" + value.toLocaleString('en-US', {
608
- minimumFractionDigits: 2,
609
- maximumFractionDigits: 2
610
- });
611
- }, []);
612
- return /*#__PURE__*/_jsxs(VStack, {
613
- gap: 3,
614
- width: "100%",
615
- children: [/*#__PURE__*/_jsx(LineChart, {
616
- showArea: true,
617
- height: defaultChartHeight,
618
- inset: {
619
- left: 0,
620
- right: 3,
621
- bottom: 3,
622
- top: 3
623
- },
624
- onScrubberPositionChange: onScrubberPositionChange,
625
- series: [{
626
- id: 'price',
627
- data: currentData,
628
- color: assets.eth.color,
629
- renderPoints: _ref2 => {
630
- let {
631
- dataX: index
632
- } = _ref2;
633
- if (highestPriceIndices.includes(index)) {
634
- return {
635
- opacity: 0,
636
- label: formatPrice(currentData[index]),
637
- labelProps: {
638
- position: 'top',
639
- dy: -16
640
- }
641
- };
642
- }
643
- if (lowestPriceIndices.includes(index)) {
644
- return {
645
- opacity: 0,
646
- label: formatPrice(currentData[index]),
647
- labelProps: {
648
- position: 'bottom',
649
- dy: 16
650
- }
651
- };
652
- }
653
- }
654
- }],
655
- yAxis: {
656
- domainLimit: 'strict'
657
- },
658
- children: /*#__PURE__*/_jsx(Scrubber, {})
659
- }), /*#__PURE__*/_jsx(PeriodSelector, {
660
- activeTab: activeTab,
661
- onChange: tab => setActiveTab(tab),
662
- tabs: tabs
663
- })]
664
- });
665
- };
666
- export const ForecastChart = () => {
667
- const getDataFromSparkline = startDate => {
668
- const allData = sparklineInteractiveData.all;
669
- if (!allData || allData.length === 0) return [];
670
- const timelineData = allData.filter(point => point.date >= startDate);
671
- return timelineData.map(point => ({
672
- date: point.date,
673
- value: point.value
674
- }));
675
- };
676
- const historicalData = useMemo(() => getDataFromSparkline(new Date('2019-01-01')), []);
677
- const annualGrowthRate = 10;
678
- const generateForecastData = useCallback((lastDate, lastPrice, growthRate) => {
679
- const dailyGrowthRate = Math.pow(1 + growthRate / 100, 1 / 365) - 1;
680
- const forecastData = [];
681
- const fiveYearsFromNow = new Date(lastDate);
682
- fiveYearsFromNow.setFullYear(fiveYearsFromNow.getFullYear() + 5);
683
-
684
- // Generate daily forecast points for 5 years
685
- const currentDate = new Date(lastDate);
686
- let currentPrice = lastPrice;
687
- while (currentDate <= fiveYearsFromNow) {
688
- currentPrice = currentPrice * (1 + dailyGrowthRate * 10);
689
- forecastData.push({
690
- date: new Date(currentDate),
691
- value: Math.round(currentPrice)
692
- });
693
- currentDate.setDate(currentDate.getDate() + 10);
694
- }
695
- return forecastData;
696
- }, []);
697
- const forecastData = useMemo(() => {
698
- if (historicalData.length === 0) return [];
699
- const lastPoint = historicalData[historicalData.length - 1];
700
- return generateForecastData(lastPoint.date, lastPoint.value, annualGrowthRate);
701
- }, [generateForecastData, historicalData, annualGrowthRate]);
702
-
703
- // Combine all data points with dates converted to timestamps for x-axis
704
- const allDataPoints = useMemo(() => [...historicalData, ...forecastData], [historicalData, forecastData]);
705
- const formatPrice = useCallback(price => {
706
- return new Intl.NumberFormat('en-US', {
707
- style: 'currency',
708
- currency: 'USD',
709
- minimumFractionDigits: 0,
710
- maximumFractionDigits: 0
711
- }).format(price);
712
- }, []);
713
-
714
- // Format x-axis labels to show years
715
- const formatXAxisLabel = useCallback(timestamp => {
716
- return new Date(timestamp).getFullYear().toString();
717
- }, []);
718
- return /*#__PURE__*/_jsx(LineChart, {
719
- enableScrubbing: true,
720
- showArea: true,
721
- showXAxis: true,
722
- areaType: "dotted",
723
- height: defaultChartHeight,
724
- inset: {
725
- top: 4,
726
- left: 0,
727
- right: 0,
728
- bottom: 0
729
- },
730
- series: [{
731
- id: 'historical',
732
- data: historicalData.map(d => d.value),
733
- color: assets.btc.color
734
- }, {
735
- id: 'forecast',
736
- data: [...historicalData.map(d => null), ...forecastData.map(d => d.value)],
737
- color: assets.btc.color,
738
- type: 'dotted'
739
- }],
740
- xAxis: {
741
- data: allDataPoints.map(d => d.date.getTime()),
742
- tickLabelFormatter: formatXAxisLabel,
743
- tickInterval: 32
744
- },
745
- children: /*#__PURE__*/_jsx(Scrubber, {})
746
- });
747
- };
748
- const PeriodSelectorExample = () => {
749
- const tabs = [{
750
- id: '1H',
751
- label: '1H'
752
- }, {
753
- id: '1D',
754
- label: '1D'
755
- }, {
756
- id: '1W',
757
- label: '1W'
758
- }, {
759
- id: '1M',
760
- label: '1M'
761
- }, {
762
- id: '1Y',
763
- label: '1Y'
764
- }, {
765
- id: 'All',
766
- label: 'All'
767
- }];
768
- const [activeTab, setActiveTab] = useState(tabs[0]);
769
- return /*#__PURE__*/_jsx(PeriodSelector, {
770
- activeTab: activeTab,
771
- onChange: tab => setActiveTab(tab),
772
- tabs: tabs
773
- });
774
- };
775
- const BTCTab = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref3, ref) => {
776
- let {
777
- label
778
- } = _ref3,
779
- props = _objectWithoutPropertiesLoose(_ref3, _excluded2);
780
- const {
781
- activeTab
782
- } = useTabsContext();
783
- const isActive = (activeTab == null ? void 0 : activeTab.id) === props.id;
784
- return /*#__PURE__*/_jsx(SegmentedTab, _extends({
785
- ref: ref,
786
- label: /*#__PURE__*/_jsx(TextLabel1, {
787
- style: {
788
- color: isActive ? assets.btc.color : undefined
789
- },
790
- children: label
791
- })
792
- }, props));
793
- }));
794
- const BTCActiveIndicator = /*#__PURE__*/memo(_ref4 => {
795
- let {
796
- style
797
- } = _ref4,
798
- props = _objectWithoutPropertiesLoose(_ref4, _excluded3);
799
- return /*#__PURE__*/_jsx(PeriodSelectorActiveIndicator, _extends({}, props, {
800
- style: [style, {
801
- backgroundColor: assets.btc.color + "1A"
802
- }]
803
- }));
804
- });
805
- const AssetPriceDotted = () => {
806
- const currentPrice = sparklineInteractiveData.hour[sparklineInteractiveData.hour.length - 1].value;
807
- const tabs = useMemo(() => [{
808
- id: 'hour',
809
- label: '1H'
810
- }, {
811
- id: 'day',
812
- label: '1D'
813
- }, {
814
- id: 'week',
815
- label: '1W'
816
- }, {
817
- id: 'month',
818
- label: '1M'
819
- }, {
820
- id: 'year',
821
- label: '1Y'
822
- }, {
823
- id: 'all',
824
- label: 'All'
825
- }], []);
826
- const [timePeriod, setTimePeriod] = useState(tabs[0]);
827
- const sparklineTimePeriodData = useMemo(() => {
828
- return sparklineInteractiveData[timePeriod.id];
829
- }, [timePeriod]);
830
- const sparklineTimePeriodDataValues = useMemo(() => {
831
- return sparklineTimePeriodData.map(d => d.value);
832
- }, [sparklineTimePeriodData]);
833
- const sparklineTimePeriodDataTimestamps = useMemo(() => {
834
- return sparklineTimePeriodData.map(d => d.date);
835
- }, [sparklineTimePeriodData]);
836
- const onPeriodChange = useCallback(period => {
837
- setTimePeriod(period || tabs[0]);
838
- }, [tabs, setTimePeriod]);
839
- const formatPrice = useCallback(price => {
840
- return new Intl.NumberFormat('en-US', {
841
- style: 'currency',
842
- currency: 'USD'
843
- }).format(price);
844
- }, []);
845
- const formatDate = useCallback(date => {
846
- const dayOfWeek = date.toLocaleDateString('en-US', {
847
- weekday: 'short'
848
- });
849
- const monthDay = date.toLocaleDateString('en-US', {
850
- month: 'short',
851
- day: 'numeric'
852
- });
853
- const time = date.toLocaleTimeString('en-US', {
854
- hour: 'numeric',
855
- minute: '2-digit',
856
- hour12: true
857
- });
858
- return dayOfWeek + ", " + monthDay + ", " + time;
859
- }, []);
860
- const scrubberLabel = useCallback(dataIndex => {
861
- const price = new Intl.NumberFormat('en-US', {
862
- minimumFractionDigits: 2,
863
- maximumFractionDigits: 2
864
- }).format(sparklineTimePeriodDataValues[dataIndex]);
865
- const date = formatDate(sparklineTimePeriodDataTimestamps[dataIndex]);
866
- return price + " USD " + date;
867
- }, [sparklineTimePeriodDataValues, formatDate, sparklineTimePeriodDataTimestamps]);
868
-
869
- // Chart overview accessibility label
870
- const chartOverviewLabel = useMemo(() => {
871
- if (sparklineTimePeriodData.length === 0) return '';
872
- const firstDate = sparklineTimePeriodData[0].date;
873
- const lastDate = sparklineTimePeriodData[sparklineTimePeriodData.length - 1].date;
874
- const currentYear = new Date().getFullYear();
875
- const shouldIncludeTime = timePeriod.id === 'hour' || timePeriod.id === 'day';
876
- const dateRangeOptions = _extends({
877
- month: 'long',
878
- day: 'numeric',
879
- year: firstDate.getFullYear() !== currentYear ? 'numeric' : undefined
880
- }, shouldIncludeTime && {
881
- hour: 'numeric',
882
- minute: '2-digit',
883
- hour12: true
884
- });
885
- const startDateStr = shouldIncludeTime ? firstDate.toLocaleString('en-US', dateRangeOptions) : firstDate.toLocaleDateString('en-US', dateRangeOptions);
886
- const endDateStr = shouldIncludeTime ? lastDate.toLocaleString('en-US', _extends({}, dateRangeOptions, {
887
- year: lastDate.getFullYear() !== currentYear ? 'numeric' : undefined
888
- })) : lastDate.toLocaleDateString('en-US', _extends({}, dateRangeOptions, {
889
- year: lastDate.getFullYear() !== currentYear ? 'numeric' : undefined
890
- }));
891
- return "Price chart for Bitcoin, " + startDateStr + " to " + endDateStr + ". Swipe left or right to navigate data points.";
892
- }, [sparklineTimePeriodData, timePeriod.id]);
893
- return /*#__PURE__*/_jsx(Box, {
894
- accessibilityLabel: chartOverviewLabel,
895
- accessibilityLiveRegion: "polite",
896
- children: /*#__PURE__*/_jsxs(VStack, {
897
- gap: 2,
898
- children: [/*#__PURE__*/_jsx(SectionHeader, {
899
- "aria-hidden": "true",
900
- balance: /*#__PURE__*/_jsxs(Text, {
901
- font: "title2",
902
- children: [formatPrice(currentPrice), " ", sparklineTimePeriodDataValues.length]
903
- }),
904
- end: /*#__PURE__*/_jsx(VStack, {
905
- justifyContent: "center",
906
- children: /*#__PURE__*/_jsx(RemoteImage, {
907
- shape: "circle",
908
- size: "xl",
909
- source: assets.btc.imageUrl
910
- })
911
- }),
912
- padding: 0,
913
- title: /*#__PURE__*/_jsx(Text, {
914
- font: "title1",
915
- children: "Bitcoin"
916
- })
917
- }), /*#__PURE__*/_jsx(LineChart, {
918
- enableScrubbing: true,
919
- showArea: true,
920
- accessibilityLiveRegion: "polite",
921
- areaType: "dotted",
922
- height: defaultChartHeight,
923
- series: [{
924
- id: 'btc',
925
- data: sparklineTimePeriodDataValues,
926
- color: assets.btc.color
927
- }],
928
- children: /*#__PURE__*/_jsx(Scrubber, {
929
- idlePulse: true,
930
- label: scrubberLabel
931
- })
932
- }), /*#__PURE__*/_jsx(PeriodSelector, {
933
- TabComponent: BTCTab,
934
- TabsActiveIndicatorComponent: BTCActiveIndicator,
935
- accessibilityLabel: "Select time period for chart",
936
- activeTab: timePeriod,
937
- onChange: onPeriodChange,
938
- tabs: tabs
939
- })]
940
- })
941
- });
942
- };
943
- const AssetPriceDottedNonMemoized = () => {
944
- const [scrubIndex, setScrubIndex] = useState(undefined);
945
- const currentPrice = sparklineInteractiveData.hour[sparklineInteractiveData.hour.length - 1].value;
946
- const tabs = useMemo(() => [{
947
- id: 'hour',
948
- label: '1H'
949
- }, {
950
- id: 'day',
951
- label: '1D'
952
- }, {
953
- id: 'week',
954
- label: '1W'
955
- }, {
956
- id: 'month',
957
- label: '1M'
958
- }, {
959
- id: 'year',
960
- label: '1Y'
961
- }, {
962
- id: 'all',
963
- label: 'All'
964
- }], []);
965
- const [timePeriod, setTimePeriod] = useState(tabs[0]);
966
- const sparklineTimePeriodData = useMemo(() => {
967
- return sparklineInteractiveData[timePeriod.id];
968
- }, [timePeriod]);
969
- const sparklineTimePeriodDataValues = useMemo(() => {
970
- return sparklineTimePeriodData.map(d => d.value);
971
- }, [sparklineTimePeriodData]);
972
- const sparklineTimePeriodDataTimestamps = useMemo(() => {
973
- return sparklineTimePeriodData.map(d => d.date);
974
- }, [sparklineTimePeriodData]);
975
- const onPeriodChange = useCallback(period => {
976
- setTimePeriod(period || tabs[0]);
977
- }, [tabs, setTimePeriod]);
978
- const formatPrice = useCallback(price => {
979
- return new Intl.NumberFormat('en-US', {
980
- style: 'currency',
981
- currency: 'USD'
982
- }).format(price);
983
- }, []);
984
- const formatDate = useCallback(date => {
985
- const dayOfWeek = date.toLocaleDateString('en-US', {
986
- weekday: 'short'
987
- });
988
- const monthDay = date.toLocaleDateString('en-US', {
989
- month: 'short',
990
- day: 'numeric'
991
- });
992
- const time = date.toLocaleTimeString('en-US', {
993
- hour: 'numeric',
994
- minute: '2-digit',
995
- hour12: true
996
- });
997
- return dayOfWeek + ", " + monthDay + ", " + time;
998
- }, []);
999
- const scrubberLabel = useMemo(() => {
1000
- if (scrubIndex === undefined) return null;
1001
- const price = new Intl.NumberFormat('en-US', {
1002
- minimumFractionDigits: 2,
1003
- maximumFractionDigits: 2
1004
- }).format(sparklineTimePeriodDataValues[scrubIndex]);
1005
- const date = formatDate(sparklineTimePeriodDataTimestamps[scrubIndex]);
1006
- return price + " USD " + date;
1007
- }, [scrubIndex, sparklineTimePeriodDataValues, formatDate, sparklineTimePeriodDataTimestamps]);
1008
- return /*#__PURE__*/_jsxs(VStack, {
1009
- gap: 2,
1010
- children: [/*#__PURE__*/_jsx(SectionHeader, {
1011
- balance: /*#__PURE__*/_jsxs(Text, {
1012
- font: "title2",
1013
- children: [formatPrice(currentPrice), " ", sparklineTimePeriodDataValues.length]
1014
- }),
1015
- end: /*#__PURE__*/_jsx(VStack, {
1016
- justifyContent: "center",
1017
- children: /*#__PURE__*/_jsx(RemoteImage, {
1018
- shape: "circle",
1019
- size: "xl",
1020
- source: assets.btc.imageUrl
1021
- })
1022
- }),
1023
- padding: 0,
1024
- title: /*#__PURE__*/_jsx(Text, {
1025
- font: "title1",
1026
- children: "Bitcoin"
1027
- })
1028
- }), /*#__PURE__*/_jsx(LineChart, {
1029
- enableScrubbing: true,
1030
- showArea: true,
1031
- areaType: "dotted",
1032
- height: defaultChartHeight,
1033
- onScrubberPositionChange: setScrubIndex,
1034
- series: [{
1035
- id: 'btc',
1036
- data: sparklineTimePeriodDataValues,
1037
- color: assets.btc.color
1038
- }],
1039
- children: /*#__PURE__*/_jsx(Scrubber, {
1040
- label: scrubberLabel
1041
- })
1042
- }), /*#__PURE__*/_jsx(PeriodSelector, {
1043
- TabComponent: BTCTab,
1044
- TabsActiveIndicatorComponent: BTCActiveIndicator,
1045
- activeTab: timePeriod,
1046
- onChange: onPeriodChange,
1047
- tabs: tabs
1048
- })]
1049
- });
1050
- };
1051
- const AssetPriceMultipleDotted = () => {
1052
- const [scrubIndex, setScrubIndex] = useState(undefined);
1053
- const currentPrice = sparklineInteractiveData.hour[sparklineInteractiveData.hour.length - 1].value;
1054
- const tabs = useMemo(() => [{
1055
- id: 'hour',
1056
- label: '1H'
1057
- }, {
1058
- id: 'day',
1059
- label: '1D'
1060
- }, {
1061
- id: 'week',
1062
- label: '1W'
1063
- }, {
1064
- id: 'month',
1065
- label: '1M'
1066
- }, {
1067
- id: 'year',
1068
- label: '1Y'
1069
- }, {
1070
- id: 'all',
1071
- label: 'All'
1072
- }], []);
1073
- const [timePeriod, setTimePeriod] = useState(tabs[0]);
1074
- const sparklineTimePeriodData = useMemo(() => {
1075
- return sparklineInteractiveData[timePeriod.id];
1076
- }, [timePeriod]);
1077
- const sparklineTimePeriodDataValues = useMemo(() => {
1078
- return sparklineTimePeriodData.map(d => d.value);
1079
- }, [sparklineTimePeriodData]);
1080
- const sparklineTimePeriodDataTimestamps = useMemo(() => {
1081
- return sparklineTimePeriodData.map(d => d.date);
1082
- }, [sparklineTimePeriodData]);
1083
- const onPeriodChange = useCallback(period => {
1084
- setTimePeriod(period || tabs[0]);
1085
- }, [tabs, setTimePeriod]);
1086
- const formatPrice = useCallback(price => {
1087
- return new Intl.NumberFormat('en-US', {
1088
- style: 'currency',
1089
- currency: 'USD'
1090
- }).format(price);
1091
- }, []);
1092
- const formatDate = useCallback(date => {
1093
- const dayOfWeek = date.toLocaleDateString('en-US', {
1094
- weekday: 'short'
1095
- });
1096
- const monthDay = date.toLocaleDateString('en-US', {
1097
- month: 'short',
1098
- day: 'numeric'
1099
- });
1100
- const time = date.toLocaleTimeString('en-US', {
1101
- hour: 'numeric',
1102
- minute: '2-digit',
1103
- hour12: true
1104
- });
1105
- return dayOfWeek + ", " + monthDay + ", " + time;
1106
- }, []);
1107
- const scrubberLabel = useMemo(() => {
1108
- if (scrubIndex === undefined) return null;
1109
- const price = new Intl.NumberFormat('en-US', {
1110
- minimumFractionDigits: 2,
1111
- maximumFractionDigits: 2
1112
- }).format(sparklineTimePeriodDataValues[scrubIndex]);
1113
- const date = formatDate(sparklineTimePeriodDataTimestamps[scrubIndex]);
1114
- return price + " USD " + date;
1115
- }, [scrubIndex, sparklineTimePeriodDataValues, formatDate, sparklineTimePeriodDataTimestamps]);
1116
- return /*#__PURE__*/_jsxs(VStack, {
1117
- gap: 2,
1118
- children: [/*#__PURE__*/_jsx(SectionHeader, {
1119
- balance: /*#__PURE__*/_jsxs(Text, {
1120
- font: "title2",
1121
- children: [formatPrice(currentPrice), " ", sparklineTimePeriodDataValues.length]
1122
- }),
1123
- end: /*#__PURE__*/_jsx(VStack, {
1124
- justifyContent: "center",
1125
- children: /*#__PURE__*/_jsx(RemoteImage, {
1126
- shape: "circle",
1127
- size: "xl",
1128
- source: assets.btc.imageUrl
1129
- })
1130
- }),
1131
- padding: 0,
1132
- title: /*#__PURE__*/_jsx(Text, {
1133
- font: "title1",
1134
- children: "Bitcoin"
1135
- })
1136
- }), /*#__PURE__*/_jsx(LineChart, {
1137
- enableScrubbing: true,
1138
- height: defaultChartHeight,
1139
- series: [{
1140
- id: 'btc',
1141
- data: sparklineTimePeriodDataValues,
1142
- color: assets.btc.color
1143
- }, {
1144
- id: 'eth',
1145
- data: sparklineTimePeriodDataValues.map(d => d * 0.75),
1146
- color: assets.eth.color
1147
- }, {
1148
- id: 'xrp',
1149
- data: sparklineTimePeriodDataValues.map(d => d * 0.5),
1150
- color: assets.xrp.color
1151
- }],
1152
- children: /*#__PURE__*/_jsx(Scrubber, {})
1153
- }), /*#__PURE__*/_jsx(PeriodSelector, {
1154
- TabComponent: BTCTab,
1155
- TabsActiveIndicatorComponent: BTCActiveIndicator,
1156
- activeTab: timePeriod,
1157
- onChange: onPeriodChange,
1158
- tabs: tabs
1159
- })]
1160
- });
1161
- };
1162
- const GainLossChart = () => {
1163
- const theme = useTheme();
1164
- const gradientId = useId();
1165
- const data = [-40, -28, -21, -5, 48, -5, -28, 2, -29, -46, 16, -30, -29, 8];
1166
- const ChartDefs = _ref5 => {
1167
- let {
1168
- threshold = 0
1169
- } = _ref5;
1170
- const {
1171
- getYScale
1172
- } = useCartesianChartContext();
1173
- // get the default y-axis scale
1174
- const yScale = getYScale();
1175
- if (yScale) {
1176
- const domain = yScale.domain();
1177
- const range = yScale.range();
1178
- const baselinePercentage = (threshold - domain[0]) / (domain[1] - domain[0]) * 100;
1179
- const negativeColor = "rgb(" + theme.spectrum.gray20 + ")";
1180
- const positiveColor = theme.color.fgPositive;
1181
- return /*#__PURE__*/_jsxs(Defs, {
1182
- children: [/*#__PURE__*/_jsxs(LinearGradient, {
1183
- gradientUnits: "userSpaceOnUse",
1184
- id: gradientId + "-solid",
1185
- x1: "0%",
1186
- x2: "0%",
1187
- y1: range[0],
1188
- y2: range[1],
1189
- children: [/*#__PURE__*/_jsx(Stop, {
1190
- offset: "0%",
1191
- stopColor: negativeColor
1192
- }), /*#__PURE__*/_jsx(Stop, {
1193
- offset: baselinePercentage + "%",
1194
- stopColor: negativeColor
1195
- }), /*#__PURE__*/_jsx(Stop, {
1196
- offset: baselinePercentage + "%",
1197
- stopColor: positiveColor
1198
- }), /*#__PURE__*/_jsx(Stop, {
1199
- offset: "100%",
1200
- stopColor: positiveColor
1201
- })]
1202
- }), /*#__PURE__*/_jsxs(LinearGradient, {
1203
- gradientUnits: "userSpaceOnUse",
1204
- id: gradientId + "-gradient",
1205
- x1: "0%",
1206
- x2: "0%",
1207
- y1: range[0],
1208
- y2: range[1],
1209
- children: [/*#__PURE__*/_jsx(Stop, {
1210
- offset: "0%",
1211
- stopColor: negativeColor,
1212
- stopOpacity: 0.3
1213
- }), /*#__PURE__*/_jsx(Stop, {
1214
- offset: baselinePercentage + "%",
1215
- stopColor: negativeColor,
1216
- stopOpacity: 0
1217
- }), /*#__PURE__*/_jsx(Stop, {
1218
- offset: baselinePercentage + "%",
1219
- stopColor: positiveColor,
1220
- stopOpacity: 0
1221
- }), /*#__PURE__*/_jsx(Stop, {
1222
- offset: "100%",
1223
- stopColor: positiveColor,
1224
- stopOpacity: 0.3
1225
- })]
1226
- })]
1227
- });
1228
- }
1229
- return null;
584
+ // Line gradient: hard color change at 0 (full opacity for line)
585
+ const lineGradient = {
586
+ stops: [{
587
+ offset: 0,
588
+ color: negativeColor
589
+ }, {
590
+ offset: 0,
591
+ color: positiveColor
592
+ }]
1230
593
  };
1231
- const tickLabelFormatter = useCallback(value => new Intl.NumberFormat('en-US', {
1232
- style: 'currency',
1233
- currency: 'USD',
1234
- maximumFractionDigits: 0
1235
- }).format(value), []);
1236
- const solidColor = "url(#" + gradientId + "-solid)";
594
+ const GradientDottedArea = /*#__PURE__*/memo(props => /*#__PURE__*/_jsx(DottedArea, _extends({}, props, {
595
+ gradient: {
596
+ stops: _ref5 => {
597
+ let {
598
+ min,
599
+ max
600
+ } = _ref5;
601
+ return [{
602
+ offset: min,
603
+ color: negativeColor,
604
+ opacity: 0.4
605
+ }, {
606
+ offset: 0,
607
+ color: negativeColor,
608
+ opacity: 0
609
+ }, {
610
+ offset: 0,
611
+ color: positiveColor,
612
+ opacity: 0
613
+ }, {
614
+ offset: max,
615
+ color: positiveColor,
616
+ opacity: 0.4
617
+ }];
618
+ }
619
+ }
620
+ })));
1237
621
  return /*#__PURE__*/_jsxs(CartesianChart, {
1238
622
  enableScrubbing: true,
1239
- height: defaultChartHeight,
1240
- inset: {
1241
- top: 1.5,
1242
- bottom: 1.5,
1243
- left: 0,
1244
- right: 0
1245
- },
623
+ height: 200,
1246
624
  series: [{
1247
625
  id: 'prices',
1248
626
  data: data,
1249
- color: solidColor
627
+ gradient: lineGradient
1250
628
  }],
1251
- children: [/*#__PURE__*/_jsx(ChartDefs, {}), /*#__PURE__*/_jsx(YAxis, {
629
+ children: [/*#__PURE__*/_jsx(YAxis, {
1252
630
  showGrid: true,
1253
631
  requestedTickCount: 2,
1254
632
  tickLabelFormatter: tickLabelFormatter
1255
- }), /*#__PURE__*/_jsx(Area, {
1256
- curve: "monotone",
1257
- fill: "url(#" + gradientId + "-gradient)",
1258
- seriesId: "prices"
1259
633
  }), /*#__PURE__*/_jsx(Line, {
1260
- curve: "monotone",
634
+ showArea: true,
635
+ AreaComponent: GradientDottedArea,
1261
636
  seriesId: "prices",
1262
- stroke: solidColor,
1263
637
  strokeWidth: 3
1264
638
  }), /*#__PURE__*/_jsx(Scrubber, {
1265
639
  hideOverlay: true
1266
640
  })]
1267
641
  });
1268
- };
1269
- const BitcoinChartWithScrubberBeacon = () => {
642
+ }
643
+ function HighLowPrice() {
644
+ const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
645
+ const minPrice = Math.min(...data);
646
+ const maxPrice = Math.max(...data);
647
+ const minPriceIndex = data.indexOf(minPrice);
648
+ const maxPriceIndex = data.indexOf(maxPrice);
649
+ const formatPrice = useCallback(price => {
650
+ return "$" + price.toLocaleString('en-US', {
651
+ minimumFractionDigits: 2,
652
+ maximumFractionDigits: 2
653
+ });
654
+ }, []);
655
+ return /*#__PURE__*/_jsxs(LineChart, {
656
+ showArea: true,
657
+ height: 200,
658
+ series: [{
659
+ id: 'prices',
660
+ data: data
661
+ }],
662
+ children: [/*#__PURE__*/_jsx(Point, {
663
+ dataX: minPriceIndex,
664
+ dataY: minPrice,
665
+ label: formatPrice(minPrice),
666
+ labelPosition: "bottom"
667
+ }), /*#__PURE__*/_jsx(Point, {
668
+ dataX: maxPriceIndex,
669
+ dataY: maxPrice,
670
+ label: formatPrice(maxPrice),
671
+ labelPosition: "top"
672
+ })]
673
+ });
674
+ }
675
+ function StylingScrubber() {
1270
676
  const theme = useTheme();
1271
- const prices = [...btcCandles].reverse().map(candle => parseFloat(candle.close));
1272
- const latestPrice = prices[prices.length - 1];
1273
- const formatPrice = price => {
1274
- return new Intl.NumberFormat('en-US', {
1275
- style: 'currency',
1276
- currency: 'USD'
1277
- }).format(price);
677
+ const pages = ['Page A', 'Page B', 'Page C', 'Page D', 'Page E', 'Page F', 'Page G'];
678
+ const pageViews = [2400, 1398, 9800, 3908, 4800, 3800, 4300];
679
+ const uniqueVisitors = [4000, 3000, 2000, 2780, 1890, 2390, 3490];
680
+ const numberFormatter = useCallback(value => new Intl.NumberFormat('en-US', {
681
+ maximumFractionDigits: 0
682
+ }).format(value), []);
683
+ return /*#__PURE__*/_jsx(LineChart, {
684
+ enableScrubbing: true,
685
+ showArea: true,
686
+ showXAxis: true,
687
+ showYAxis: true,
688
+ height: 200,
689
+ series: [{
690
+ id: 'pageViews',
691
+ data: pageViews,
692
+ color: theme.color.accentBoldGreen,
693
+ // Label will render next to scrubber beacon
694
+ label: 'Page Views'
695
+ }, {
696
+ id: 'uniqueVisitors',
697
+ data: uniqueVisitors,
698
+ color: theme.color.accentBoldPurple,
699
+ label: 'Unique Visitors',
700
+ // Default area is gradient
701
+ areaType: 'dotted'
702
+ }],
703
+ xAxis: {
704
+ // Used on the x-axis to provide context for each index from the series data array
705
+ data: pages
706
+ },
707
+ yAxis: {
708
+ showGrid: true,
709
+ tickLabelFormatter: numberFormatter
710
+ },
711
+ children: /*#__PURE__*/_jsx(Scrubber, {
712
+ idlePulse: true,
713
+ LineComponent: SolidLine,
714
+ seriesIds: ['pageViews']
715
+ })
716
+ });
717
+ }
718
+ function Compact() {
719
+ const theme = useTheme();
720
+ const dimensions = {
721
+ width: 62,
722
+ height: 18
1278
723
  };
1279
- const formatPercentChange = price => {
1280
- return new Intl.NumberFormat('en-US', {
1281
- style: 'percent',
724
+ const sparklineData = prices.map(price => parseFloat(price)).filter((price, index) => index % 10 === 0);
725
+ const positiveFloor = Math.min(...sparklineData) - 10;
726
+ const negativeData = sparklineData.map(price => -1 * price).reverse();
727
+ const negativeCeiling = Math.max(...negativeData) + 10;
728
+ const formatPrice = useCallback(price => {
729
+ return "$" + price.toLocaleString('en-US', {
1282
730
  minimumFractionDigits: 2,
1283
731
  maximumFractionDigits: 2
1284
- }).format(price);
1285
- };
1286
- const percentChange = (latestPrice - prices[0]) / prices[0];
1287
- return /*#__PURE__*/_jsx(Box, {
1288
- borderRadius: 300,
1289
- overflow: "hidden",
1290
- style: {
1291
- backgroundColor: '#ED702F'
1292
- },
1293
- children: /*#__PURE__*/_jsxs(VStack, {
732
+ });
733
+ }, []);
734
+ const CompactChart = /*#__PURE__*/memo(_ref6 => {
735
+ let {
736
+ data,
737
+ showArea,
738
+ color,
739
+ referenceY
740
+ } = _ref6;
741
+ return /*#__PURE__*/_jsx(Box, {
1294
742
  style: {
1295
- backgroundColor: 'rgba(0, 0, 0, 0.80)'
743
+ padding: 1
1296
744
  },
1297
- children: [/*#__PURE__*/_jsxs(HStack, {
1298
- alignItems: "center",
1299
- gap: 2,
1300
- padding: 2,
1301
- paddingBottom: 0,
1302
- children: [/*#__PURE__*/_jsx(RemoteImage, {
1303
- shape: "circle",
1304
- size: "xxl",
1305
- source: assets.btc.imageUrl
1306
- }), /*#__PURE__*/_jsxs(VStack, {
1307
- flexGrow: 1,
1308
- gap: 0.25,
1309
- children: [/*#__PURE__*/_jsx(Text, {
1310
- font: "title1",
1311
- style: {
1312
- color: 'white'
1313
- },
1314
- children: "BTC"
1315
- }), /*#__PURE__*/_jsx(Text, {
1316
- color: "fgMuted",
1317
- font: "label1",
1318
- children: "Bitcoin"
1319
- })]
1320
- }), /*#__PURE__*/_jsxs(VStack, {
1321
- alignItems: "flex-end",
1322
- gap: 0.25,
1323
- children: [/*#__PURE__*/_jsx(Text, {
1324
- font: "title1",
1325
- style: {
1326
- color: 'white'
1327
- },
1328
- children: formatPrice(latestPrice)
1329
- }), /*#__PURE__*/_jsxs(Text, {
1330
- color: "fgPositive",
1331
- font: "label1",
1332
- children: ["+", formatPercentChange(percentChange)]
1333
- })]
1334
- })]
1335
- }), /*#__PURE__*/_jsx(LineChart, {
1336
- showArea: true,
1337
- height: 64,
1338
- inset: {
1339
- left: 0,
1340
- right: 3,
1341
- bottom: 0,
1342
- top: 2
1343
- },
745
+ children: /*#__PURE__*/_jsx(LineChart, _extends({}, dimensions, {
746
+ enableScrubbing: false,
747
+ inset: 0,
1344
748
  series: [{
1345
- id: 'btcPrice',
1346
- data: prices,
1347
- color: assets.btc.color
749
+ id: 'btc',
750
+ data,
751
+ color
1348
752
  }],
1349
- width: "100%",
1350
- children: /*#__PURE__*/_jsx(Scrubber, {})
1351
- })]
1352
- })
753
+ showArea: showArea,
754
+ children: /*#__PURE__*/_jsx(ReferenceLine, {
755
+ dataY: referenceY
756
+ })
757
+ }))
758
+ });
1353
759
  });
1354
- };
1355
- const ScrubberWithImperativeHandle = () => {
1356
- const theme = useTheme();
1357
- const scrubberRef = useRef(null);
1358
- return /*#__PURE__*/_jsxs(VStack, {
1359
- gap: 2,
1360
- children: [/*#__PURE__*/_jsx(LineChart, {
1361
- enableScrubbing: true,
1362
- showYAxis: true,
1363
- height: defaultChartHeight,
1364
- series: [{
1365
- id: 'priceA',
1366
- data: [2400, 1398, 9800, 3908, 4800, 3800, 4300],
1367
- label: 'Page Views',
1368
- color: theme.color.accentBoldBlue,
1369
- curve: 'natural'
1370
- }, {
1371
- id: 'priceB',
1372
- data: [2000, 2491, 4501, 6049, 5019, 4930, 5910],
1373
- label: 'Unique Visitors G',
1374
- color: theme.color.accentBoldGreen,
1375
- curve: 'natural'
1376
- }, {
1377
- id: 'priceC',
1378
- data: [1000, 4910, 2300, 5910, 3940, 2940, 1940],
1379
- label: 'Unique Visitors P',
1380
- color: theme.color.accentBoldPurple,
1381
- curve: 'natural'
1382
- }],
1383
- yAxis: {
1384
- domain: {
1385
- min: 0
1386
- },
1387
- showGrid: true,
1388
- tickLabelFormatter: value => value.toLocaleString()
1389
- },
1390
- children: /*#__PURE__*/_jsx(Scrubber, {
1391
- ref: scrubberRef
1392
- })
1393
- }), /*#__PURE__*/_jsx(Button, {
1394
- onPress: () => {
1395
- var _scrubberRef$current;
1396
- return (_scrubberRef$current = scrubberRef.current) == null ? void 0 : _scrubberRef$current.pulse();
760
+ const ChartCell = /*#__PURE__*/memo(_ref7 => {
761
+ let {
762
+ data,
763
+ showArea,
764
+ color,
765
+ referenceY,
766
+ subdetail
767
+ } = _ref7;
768
+ return /*#__PURE__*/_jsx(ListCell, {
769
+ detail: formatPrice(parseFloat(prices[0])),
770
+ intermediary: /*#__PURE__*/_jsx(CompactChart, {
771
+ color: color,
772
+ data: data,
773
+ referenceY: referenceY,
774
+ showArea: showArea
775
+ }),
776
+ media: /*#__PURE__*/_jsx(Avatar, {
777
+ src: assets.btc.imageUrl
778
+ }),
779
+ onPress: () => console.log('clicked'),
780
+ spacingVariant: "condensed",
781
+ style: {
782
+ padding: 0
1397
783
  },
1398
- children: "Pulse Beacons"
1399
- })]
784
+ subdetail: subdetail
785
+ });
1400
786
  });
1401
- };
1402
- const BTCPriceChart = () => {
1403
- const tabs = [{
1404
- id: 'hour',
1405
- label: '1H'
1406
- }, {
1407
- id: 'day',
1408
- label: '1D'
1409
- }, {
1410
- id: 'week',
1411
- label: '1W'
1412
- }, {
1413
- id: 'month',
1414
- label: '1M'
1415
- }, {
1416
- id: 'year',
1417
- label: '1Y'
1418
- }, {
1419
- id: 'all',
1420
- label: 'All'
1421
- }];
1422
- const [activeTab, setActiveTab] = useState(tabs[0]);
1423
- const [highlightedItem, setHighlightedItem] = useState();
1424
- const currentPriceData = activeTab ? sparklineInteractiveData[activeTab.id] : sparklineInteractiveData.hour;
1425
- const currentData = useMemo(() => [...currentPriceData.map(price => price.value)], [currentPriceData]);
1426
- const currentTimestamps = useMemo(() => [...currentPriceData.map(price => price.date.toISOString())], [currentPriceData]);
1427
- const currentPrice = currentData[currentData.length - 1];
1428
- const startPrice = currentData[0];
1429
- const onScrubberPositionChange = useCallback(item => {
1430
- setHighlightedItem(item);
787
+ return /*#__PURE__*/_jsxs(VStack, {
788
+ children: [/*#__PURE__*/_jsx(ChartCell, {
789
+ color: assets.btc.color,
790
+ data: sparklineData,
791
+ referenceY: parseFloat(prices[Math.floor(prices.length / 4)]),
792
+ subdetail: "-4.55%"
793
+ }), /*#__PURE__*/_jsx(ChartCell, {
794
+ showArea: true,
795
+ color: assets.btc.color,
796
+ data: sparklineData,
797
+ referenceY: parseFloat(prices[Math.floor(prices.length / 4)]),
798
+ subdetail: "-4.55%"
799
+ }), /*#__PURE__*/_jsx(ChartCell, {
800
+ showArea: true,
801
+ color: theme.color.fgPositive,
802
+ data: sparklineData,
803
+ referenceY: positiveFloor,
804
+ subdetail: "+0.25%"
805
+ }), /*#__PURE__*/_jsx(ChartCell, {
806
+ showArea: true,
807
+ color: theme.color.fgNegative,
808
+ data: negativeData,
809
+ referenceY: negativeCeiling,
810
+ subdetail: "-4.55%"
811
+ })]
812
+ });
813
+ }
814
+ function AssetPriceWithDottedArea() {
815
+ const fontMgr = useMemo(() => {
816
+ const fontProvider = Skia.TypefaceFontProvider.Make();
817
+ // Register system fonts if available, otherwise Skia will use defaults
818
+ return fontProvider;
1431
819
  }, []);
1432
- const displayPrice = highlightedItem !== null && highlightedItem !== undefined ? currentData[highlightedItem] : currentPrice;
1433
- const btcAccentColor = '#F0A73C';
1434
- const {
1435
- displayDate
1436
- } = useMemo(() => {
1437
- return calculateTrendData(highlightedItem, currentData, currentTimestamps, startPrice, currentPrice, (activeTab == null ? void 0 : activeTab.id) || 'hour');
1438
- }, [highlightedItem, currentData, currentTimestamps, startPrice, currentPrice, activeTab]);
1439
- const formattedPrice = "$" + displayPrice.toLocaleString('en-US', {
1440
- minimumFractionDigits: 2,
1441
- maximumFractionDigits: 2
820
+ const BTCTab = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref8, ref) => {
821
+ let {
822
+ label
823
+ } = _ref8,
824
+ props = _objectWithoutPropertiesLoose(_ref8, _excluded2);
825
+ const {
826
+ activeTab
827
+ } = useTabsContext();
828
+ const isActive = (activeTab == null ? void 0 : activeTab.id) === props.id;
829
+ return /*#__PURE__*/_jsx(SegmentedTab, _extends({
830
+ ref: ref,
831
+ label: /*#__PURE__*/_jsx(Text, {
832
+ font: "label1",
833
+ style: {
834
+ color: isActive ? assets.btc.color : undefined
835
+ },
836
+ children: label
837
+ })
838
+ }, props));
839
+ }));
840
+ const BTCActiveIndicator = /*#__PURE__*/memo(_ref9 => {
841
+ let {
842
+ style
843
+ } = _ref9,
844
+ props = _objectWithoutPropertiesLoose(_ref9, _excluded3);
845
+ return /*#__PURE__*/_jsx(PeriodSelectorActiveIndicator, _extends({}, props, {
846
+ style: [style, {
847
+ backgroundColor: assets.btc.color + "1A"
848
+ }]
849
+ }));
1442
850
  });
1443
- const AreaComponent = useMemo(() => props => /*#__PURE__*/_jsx(GradientArea, _extends({}, props, {
1444
- peakOpacity: 0.15
1445
- })), []);
1446
- return /*#__PURE__*/_jsx(Box, {
1447
- borderRadius: 300,
1448
- overflow: "hidden",
1449
- style: {
1450
- backgroundColor: btcAccentColor
1451
- },
1452
- width: "100%",
1453
- children: /*#__PURE__*/_jsxs(VStack, {
1454
- gap: 3,
1455
- width: "100%",
1456
- children: [/*#__PURE__*/_jsxs(HStack, {
1457
- alignItems: "flex-start",
1458
- gap: 3,
1459
- justifyContent: "space-between",
1460
- padding: 4,
1461
- children: [/*#__PURE__*/_jsxs(VStack, {
1462
- flexGrow: 1,
1463
- gap: 1,
1464
- children: [/*#__PURE__*/_jsx(Text, {
1465
- font: "title1",
1466
- children: "Coinbase Wrapped BTC"
1467
- }), /*#__PURE__*/_jsx(Text, {
1468
- font: "title2",
1469
- children: formattedPrice
1470
- })]
1471
- }), /*#__PURE__*/_jsx(VStack, {
851
+ const AssetPriceDotted = /*#__PURE__*/memo(() => {
852
+ const theme = useTheme();
853
+ const currentPrice = sparklineInteractiveData.hour[sparklineInteractiveData.hour.length - 1].value;
854
+ const tabs = useMemo(() => [{
855
+ id: 'hour',
856
+ label: '1H'
857
+ }, {
858
+ id: 'day',
859
+ label: '1D'
860
+ }, {
861
+ id: 'week',
862
+ label: '1W'
863
+ }, {
864
+ id: 'month',
865
+ label: '1M'
866
+ }, {
867
+ id: 'year',
868
+ label: '1Y'
869
+ }, {
870
+ id: 'all',
871
+ label: 'All'
872
+ }], []);
873
+ const [timePeriod, setTimePeriod] = useState(tabs[0]);
874
+ const sparklineTimePeriodData = useMemo(() => {
875
+ return sparklineInteractiveData[timePeriod.id];
876
+ }, [timePeriod]);
877
+ const sparklineTimePeriodDataValues = useMemo(() => {
878
+ return sparklineTimePeriodData.map(d => d.value);
879
+ }, [sparklineTimePeriodData]);
880
+ const sparklineTimePeriodDataTimestamps = useMemo(() => {
881
+ return sparklineTimePeriodData.map(d => d.date);
882
+ }, [sparklineTimePeriodData]);
883
+ const onPeriodChange = useCallback(period => {
884
+ setTimePeriod(period || tabs[0]);
885
+ }, [tabs, setTimePeriod]);
886
+ const priceFormatter = useMemo(() => new Intl.NumberFormat('en-US', {
887
+ style: 'currency',
888
+ currency: 'USD'
889
+ }), []);
890
+ const formatPrice = useCallback(price => {
891
+ return priceFormatter.format(price);
892
+ }, [priceFormatter]);
893
+ const formatDate = useCallback(date => {
894
+ const dayOfWeek = date.toLocaleDateString('en-US', {
895
+ weekday: 'short'
896
+ });
897
+ const monthDay = date.toLocaleDateString('en-US', {
898
+ month: 'short',
899
+ day: 'numeric'
900
+ });
901
+ const time = date.toLocaleTimeString('en-US', {
902
+ hour: 'numeric',
903
+ minute: '2-digit',
904
+ hour12: true
905
+ });
906
+ return dayOfWeek + ", " + monthDay + ", " + time;
907
+ }, []);
908
+ return /*#__PURE__*/_jsxs(VStack, {
909
+ gap: 2,
910
+ children: [/*#__PURE__*/_jsx(SectionHeader, {
911
+ balance: /*#__PURE__*/_jsx(Text, {
912
+ font: "title2",
913
+ children: formatPrice(currentPrice)
914
+ }),
915
+ end: /*#__PURE__*/_jsx(VStack, {
1472
916
  justifyContent: "center",
1473
917
  children: /*#__PURE__*/_jsx(RemoteImage, {
1474
918
  shape: "circle",
1475
- size: "xxl",
919
+ size: "xl",
1476
920
  source: assets.btc.imageUrl
1477
921
  })
1478
- })]
1479
- }), /*#__PURE__*/_jsxs(CartesianChart, {
922
+ }),
923
+ title: /*#__PURE__*/_jsx(Text, {
924
+ font: "title1",
925
+ children: "Bitcoin"
926
+ })
927
+ }), /*#__PURE__*/_jsx(LineChart, {
1480
928
  enableScrubbing: true,
929
+ showArea: true,
930
+ areaType: "dotted",
1481
931
  height: 200,
1482
932
  inset: {
1483
- bottom: 0,
1484
- right: 3,
1485
- left: 0,
1486
- top: 6
933
+ top: 52
1487
934
  },
1488
- onScrubberPositionChange: onScrubberPositionChange,
1489
935
  series: [{
1490
- id: 'price',
1491
- data: currentData,
1492
- color: 'black'
936
+ id: 'btc',
937
+ data: sparklineTimePeriodDataValues,
938
+ color: assets.btc.color
1493
939
  }],
1494
- width: "100%",
1495
- children: [/*#__PURE__*/_jsx(Line, {
1496
- showArea: true,
1497
- AreaComponent: AreaComponent,
1498
- seriesId: "price",
1499
- strokeWidth: 3
1500
- }), /*#__PURE__*/_jsx(Scrubber, {
940
+ children: /*#__PURE__*/_jsx(Scrubber, {
1501
941
  idlePulse: true,
1502
- label: displayDate,
1503
- labelProps: {
1504
- color: 'black'
1505
- },
1506
- lineStroke: "black"
1507
- })]
1508
- }), /*#__PURE__*/_jsx(Box, {
1509
- padding: 2,
1510
- children: /*#__PURE__*/_jsx(PeriodSelector, {
1511
- activeTab: activeTab,
1512
- onChange: tab => setActiveTab(tab),
1513
- tabs: tabs
942
+ labelElevated: true,
943
+ label: d => {
944
+ const date = formatDate(sparklineTimePeriodDataTimestamps[d]);
945
+ const price = formatPrice(sparklineTimePeriodDataValues[d]);
946
+ const regularStyle = {
947
+ fontFamilies: ['Inter'],
948
+ fontSize: 14,
949
+ fontStyle: {
950
+ weight: FontWeight.Normal
951
+ },
952
+ color: Skia.Color(theme.color.fgMuted)
953
+ };
954
+ const boldStyle = _extends({
955
+ fontFamilies: ['Inter']
956
+ }, regularStyle, {
957
+ fontStyle: {
958
+ weight: FontWeight.Bold
959
+ }
960
+ });
961
+
962
+ // 3. Use the ParagraphBuilder
963
+ const builder = Skia.ParagraphBuilder.Make({
964
+ textAlign: TextAlign.Left
965
+ }, fontMgr);
966
+ builder.pushStyle(boldStyle);
967
+ builder.addText(price);
968
+ builder.pushStyle(regularStyle);
969
+ builder.addText(" " + date);
970
+ const para = builder.build();
971
+ para.layout(512);
972
+ return para;
973
+ }
1514
974
  })
975
+ }), /*#__PURE__*/_jsx(PeriodSelector, {
976
+ TabComponent: BTCTab,
977
+ TabsActiveIndicatorComponent: BTCActiveIndicator,
978
+ activeTab: timePeriod,
979
+ onChange: onPeriodChange,
980
+ tabs: tabs
1515
981
  })]
1516
- })
982
+ });
1517
983
  });
1518
- };
1519
- const LiveAssetPrice = () => {
1520
- const scrubberRef = useRef(null);
1521
- const initialData = useMemo(() => {
1522
- return sparklineInteractiveData.hour.map(d => d.value);
984
+ return /*#__PURE__*/_jsx(AssetPriceDotted, {});
985
+ }
986
+ const LegendDot = /*#__PURE__*/memo(props => {
987
+ return /*#__PURE__*/_jsx(Box, _extends({
988
+ borderRadius: 1000,
989
+ height: 10,
990
+ width: 10
991
+ }, props));
992
+ });
993
+ const LegendEntry = /*#__PURE__*/memo(_ref0 => {
994
+ let {
995
+ color = assets.btc.color,
996
+ label,
997
+ value
998
+ } = _ref0;
999
+ return /*#__PURE__*/_jsxs(Box, {
1000
+ alignItems: "center",
1001
+ flexDirection: "row",
1002
+ gap: 0.5,
1003
+ children: [/*#__PURE__*/_jsx(LegendDot, {
1004
+ style: {
1005
+ backgroundColor: color
1006
+ }
1007
+ }), /*#__PURE__*/_jsx(Text, {
1008
+ font: "label2",
1009
+ children: label
1010
+ }), value && /*#__PURE__*/_jsx(Text, {
1011
+ color: "fgMuted",
1012
+ font: "label2",
1013
+ style: {
1014
+ fontWeight: 'bold'
1015
+ },
1016
+ children: value
1017
+ })]
1018
+ });
1019
+ });
1020
+ const PerformanceHeader = /*#__PURE__*/memo(_ref1 => {
1021
+ let {
1022
+ scrubberPosition,
1023
+ sparklineTimePeriodDataValues
1024
+ } = _ref1;
1025
+ const theme = useTheme();
1026
+ const formatPriceThousands = useCallback(price => {
1027
+ return new Intl.NumberFormat('en-US', {
1028
+ style: 'currency',
1029
+ currency: 'USD',
1030
+ minimumFractionDigits: 0,
1031
+ maximumFractionDigits: 0
1032
+ }).format(price / 1000) + "k";
1523
1033
  }, []);
1524
- const [priceData, setPriceData] = useState(initialData);
1525
- const lastDataPointTimeRef = useRef(Date.now());
1526
- const updateCountRef = useRef(0);
1527
- const intervalSeconds = 3600 / initialData.length;
1528
- const maxPercentChange = Math.abs(initialData[initialData.length - 1] - initialData[0]) * 0.05;
1529
- useEffect(() => {
1530
- const priceUpdateInterval = setInterval(() => {
1531
- var _scrubberRef$current2;
1532
- setPriceData(currentData => {
1533
- const newData = [...currentData];
1534
- const lastPrice = newData[newData.length - 1];
1535
- const priceChange = (Math.random() - 0.5) * maxPercentChange;
1536
- const newPrice = Math.round((lastPrice + priceChange) * 100) / 100;
1537
-
1538
- // Check if we should roll over to a new data point
1539
- const currentTime = Date.now();
1540
- const timeSinceLastPoint = (currentTime - lastDataPointTimeRef.current) / 1000;
1541
- if (timeSinceLastPoint >= intervalSeconds) {
1542
- // Time for a new data point - remove first, add new at end
1543
- lastDataPointTimeRef.current = currentTime;
1544
- newData.shift(); // Remove oldest data point
1545
- newData.push(newPrice); // Add new data point
1546
- updateCountRef.current = 0;
1547
- } else {
1548
- // Just update the last data point
1549
- newData[newData.length - 1] = newPrice;
1550
- updateCountRef.current++;
1551
- }
1552
- return newData;
1553
- });
1554
-
1555
- // Pulse the scrubber on each update
1556
- (_scrubberRef$current2 = scrubberRef.current) == null || _scrubberRef$current2.pulse();
1557
- }, 2000 + Math.random() * 1000);
1558
- return () => clearInterval(priceUpdateInterval);
1559
- }, [intervalSeconds, maxPercentChange]);
1034
+ const shownPosition = scrubberPosition !== undefined ? scrubberPosition : sparklineTimePeriodDataValues.length - 1;
1035
+ return /*#__PURE__*/_jsxs(HStack, {
1036
+ gap: 1,
1037
+ paddingX: 1,
1038
+ children: [/*#__PURE__*/_jsx(LegendEntry, {
1039
+ color: theme.color.fgPositive,
1040
+ label: "High Price",
1041
+ value: formatPriceThousands(sparklineTimePeriodDataValues[shownPosition] * 1.2)
1042
+ }), /*#__PURE__*/_jsx(LegendEntry, {
1043
+ color: assets.btc.color,
1044
+ label: "Actual Price",
1045
+ value: formatPriceThousands(sparklineTimePeriodDataValues[shownPosition])
1046
+ }), /*#__PURE__*/_jsx(LegendEntry, {
1047
+ color: theme.color.fgNegative,
1048
+ label: "Low Price",
1049
+ value: formatPriceThousands(sparklineTimePeriodDataValues[shownPosition] * 0.8)
1050
+ })]
1051
+ });
1052
+ });
1053
+ const PerformanceChart = /*#__PURE__*/memo(_ref10 => {
1054
+ let {
1055
+ timePeriod,
1056
+ onScrubberPositionChange
1057
+ } = _ref10;
1058
+ const theme = useTheme();
1059
+ const sparklineTimePeriodData = useMemo(() => {
1060
+ return sparklineInteractiveData[timePeriod.id];
1061
+ }, [timePeriod]);
1062
+ const sparklineTimePeriodDataValues = useMemo(() => {
1063
+ return sparklineTimePeriodData.map(d => d.value);
1064
+ }, [sparklineTimePeriodData]);
1065
+ const sparklineTimePeriodDataTimestamps = useMemo(() => {
1066
+ return sparklineTimePeriodData.map(d => d.date);
1067
+ }, [sparklineTimePeriodData]);
1068
+ const formatPriceThousands = useCallback(price => {
1069
+ return new Intl.NumberFormat('en-US', {
1070
+ style: 'currency',
1071
+ currency: 'USD',
1072
+ minimumFractionDigits: 0,
1073
+ maximumFractionDigits: 0
1074
+ }).format(price / 1000) + "k";
1075
+ }, []);
1076
+ const formatDate = useCallback(date => {
1077
+ const dayOfWeek = date.toLocaleDateString('en-US', {
1078
+ weekday: 'short'
1079
+ });
1080
+ const monthDay = date.toLocaleDateString('en-US', {
1081
+ month: 'short',
1082
+ day: 'numeric'
1083
+ });
1084
+ const time = date.toLocaleTimeString('en-US', {
1085
+ hour: 'numeric',
1086
+ minute: '2-digit',
1087
+ hour12: true
1088
+ });
1089
+ return dayOfWeek + ", " + monthDay + ", " + time;
1090
+ }, []);
1091
+ const getScrubberLabel = useCallback(d => formatDate(sparklineTimePeriodDataTimestamps[d]), [formatDate, sparklineTimePeriodDataTimestamps]);
1560
1092
  return /*#__PURE__*/_jsx(LineChart, {
1561
1093
  enableScrubbing: true,
1562
1094
  showArea: true,
1563
- height: defaultChartHeight,
1095
+ showYAxis: true,
1096
+ areaType: "dotted",
1097
+ height: 300,
1098
+ inset: {
1099
+ top: 52,
1100
+ left: 0,
1101
+ right: 0
1102
+ },
1103
+ onScrubberPositionChange: onScrubberPositionChange,
1564
1104
  series: [{
1105
+ id: 'high',
1106
+ data: sparklineTimePeriodDataValues.map(d => d * 1.2),
1107
+ color: theme.color.fgPositive,
1108
+ label: 'High Price'
1109
+ }, {
1565
1110
  id: 'btc',
1566
- data: priceData,
1567
- color: assets.btc.color
1111
+ data: sparklineTimePeriodDataValues,
1112
+ color: assets.btc.color,
1113
+ label: 'Actual Price'
1114
+ }, {
1115
+ id: 'low',
1116
+ data: sparklineTimePeriodDataValues.map(d => d * 0.8),
1117
+ color: theme.color.fgNegative,
1118
+ label: 'Low Price'
1568
1119
  }],
1120
+ yAxis: {
1121
+ showGrid: true,
1122
+ tickLabelFormatter: formatPriceThousands
1123
+ },
1569
1124
  children: /*#__PURE__*/_jsx(Scrubber, {
1570
- ref: scrubberRef
1125
+ idlePulse: true,
1126
+ label: getScrubberLabel
1571
1127
  })
1572
1128
  });
1573
- };
1574
- const availabilityEvents = [{
1575
- date: new Date('2022-01-01'),
1576
- availability: 79
1577
- }, {
1578
- date: new Date('2022-01-03'),
1579
- availability: 81
1580
- }, {
1581
- date: new Date('2022-01-04'),
1582
- availability: 82
1583
- }, {
1584
- date: new Date('2022-01-06'),
1585
- availability: 91
1586
- }, {
1587
- date: new Date('2022-01-07'),
1588
- availability: 92
1589
- }, {
1590
- date: new Date('2022-01-10'),
1591
- availability: 86
1592
- }];
1593
-
1594
- // Generate prediction probability data for three candidates
1595
- const generatePredictionData = () => {
1596
- const now = new Date();
1597
- const baseTimestamp = now.getTime();
1598
-
1599
- // Generate data for different time periods
1600
- const generateForPeriod = (points, intervalMs) => {
1601
- return Array.from({
1602
- length: points
1603
- }, (_, i) => {
1604
- const timestamp = new Date(baseTimestamp - (points - 1 - i) * intervalMs);
1605
-
1606
- // Generate percentage values for three candidates that roughly sum to 100%
1607
- const candidate1 = 45 + Math.random() * 30; // 45-75%
1608
- const candidate2 = 15 + Math.random() * 20; // 15-35%
1609
- const candidate3 = Math.max(0, 100 - candidate1 - candidate2 - Math.random() * 5); // remainder
1610
-
1611
- return {
1612
- date: timestamp,
1613
- candidate1: Math.round(candidate1 * 10) / 10,
1614
- candidate2: Math.round(candidate2 * 10) / 10,
1615
- candidate3: Math.round(candidate3 * 10) / 10
1616
- };
1617
- });
1618
- };
1619
- return {
1620
- hour: generateForPeriod(60, 60 * 1000),
1621
- // 60 points, 1 min apart
1622
- day: generateForPeriod(96, 15 * 60 * 1000),
1623
- // 96 points, 15 min apart
1624
- week: generateForPeriod(84, 2 * 60 * 60 * 1000),
1625
- // 84 points, 2 hours apart
1626
- month: generateForPeriod(120, 6 * 60 * 60 * 1000),
1627
- // 120 points, 6 hours apart
1628
- year: generateForPeriod(365, 24 * 60 * 60 * 1000),
1629
- // 365 points, 1 day apart
1630
- all: generateForPeriod(100, 7 * 24 * 60 * 60 * 1000) // 100 points, 1 week apart
1631
- };
1632
- };
1633
- const predictionData = generatePredictionData();
1634
- const PredictionLegend = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref6, ref) => {
1635
- let {
1636
- data,
1637
- colors
1638
- } = _ref6;
1639
- const [selectedIndex, setSelectedIndex] = useState(data.candidate1.length - 1);
1640
- useImperativeHandle(ref, () => ({
1641
- updateSelectedIndex: index => {
1642
- setSelectedIndex(index);
1643
- }
1644
- }));
1645
- const candidate1Value = Math.round(data.candidate1[selectedIndex]);
1646
- const candidate2Value = Math.round(data.candidate2[selectedIndex]);
1647
- const candidate3Value = Math.round(data.candidate3[selectedIndex]);
1648
- return /*#__PURE__*/_jsxs(VStack, {
1649
- gap: 2,
1650
- children: [/*#__PURE__*/_jsxs(HStack, {
1651
- alignItems: "center",
1652
- gap: 2,
1653
- children: [/*#__PURE__*/_jsx(Box, {
1654
- style: {
1655
- width: 12,
1656
- height: 12,
1657
- borderRadius: 6,
1658
- backgroundColor: colors.pink
1659
- }
1660
- }), /*#__PURE__*/_jsx(Text, {
1661
- font: "label2",
1662
- children: "Noah Wyle"
1663
- }), /*#__PURE__*/_jsxs(Text, {
1664
- font: "label1",
1665
- children: [candidate1Value, "%"]
1666
- })]
1667
- }), /*#__PURE__*/_jsxs(HStack, {
1668
- alignItems: "center",
1669
- gap: 2,
1670
- children: [/*#__PURE__*/_jsx(Box, {
1671
- style: {
1672
- width: 12,
1673
- height: 12,
1674
- borderRadius: 6,
1675
- backgroundColor: colors.teal
1676
- }
1677
- }), /*#__PURE__*/_jsx(Text, {
1678
- font: "label2",
1679
- children: "Adam Scott"
1680
- }), /*#__PURE__*/_jsxs(Text, {
1681
- font: "label1",
1682
- children: [candidate2Value, "%"]
1683
- })]
1684
- }), /*#__PURE__*/_jsxs(HStack, {
1685
- alignItems: "center",
1686
- gap: 2,
1687
- children: [/*#__PURE__*/_jsx(Box, {
1688
- style: {
1689
- width: 12,
1690
- height: 12,
1691
- borderRadius: 6,
1692
- backgroundColor: colors.green
1693
- }
1694
- }), /*#__PURE__*/_jsx(Text, {
1695
- font: "label2",
1696
- children: "Pedro Pascal"
1697
- }), /*#__PURE__*/_jsxs(Text, {
1698
- font: "label1",
1699
- children: [candidate3Value, "%"]
1700
- })]
1701
- })]
1702
- });
1703
- }));
1704
- const PredictionChart = () => {
1705
- const theme = useTheme();
1706
-
1707
- // Ref for the legend component
1708
- const legendRef = useRef(null);
1709
-
1710
- // Define colors using spectrum
1711
- const colors = useMemo(() => ({
1712
- pink: "rgb(" + theme.spectrum.pink50 + ")",
1713
- teal: "rgb(" + theme.spectrum.teal50 + ")",
1714
- green: "rgb(" + theme.spectrum.green50 + ")"
1715
- }), [theme.spectrum]);
1129
+ });
1130
+ function Performance() {
1716
1131
  const tabs = useMemo(() => [{
1717
1132
  id: 'hour',
1718
1133
  label: '1H'
@@ -1733,231 +1148,305 @@ const PredictionChart = () => {
1733
1148
  label: 'All'
1734
1149
  }], []);
1735
1150
  const [timePeriod, setTimePeriod] = useState(tabs[0]);
1736
- const periodData = useMemo(() => {
1737
- return predictionData[timePeriod.id];
1151
+ const [scrubberPosition, setScrubberPosition] = useState();
1152
+ const sparklineTimePeriodData = useMemo(() => {
1153
+ return sparklineInteractiveData[timePeriod.id];
1738
1154
  }, [timePeriod]);
1739
- const candidate1Data = useMemo(() => periodData.map(d => d.candidate1), [periodData]);
1740
- const candidate2Data = useMemo(() => periodData.map(d => d.candidate2), [periodData]);
1741
- const candidate3Data = useMemo(() => periodData.map(d => d.candidate3), [periodData]);
1742
- const timestamps = useMemo(() => periodData.map(d => d.date), [periodData]);
1743
-
1744
- // Data object for the legend
1745
- const legendData = useMemo(() => ({
1746
- candidate1: candidate1Data,
1747
- candidate2: candidate2Data,
1748
- candidate3: candidate3Data
1749
- }), [candidate1Data, candidate2Data, candidate3Data]);
1750
-
1751
- // Update legend via imperative ref when scrubber position changes
1752
- const onScrubberPositionChange = useCallback(dataIndex => {
1753
- var _legendRef$current;
1754
- const idx = dataIndex != null ? dataIndex : candidate1Data.length - 1;
1755
- (_legendRef$current = legendRef.current) == null || _legendRef$current.updateSelectedIndex(idx);
1756
- }, [candidate1Data.length]);
1757
-
1758
- // Update legend when data length changes
1759
- useEffect(() => {
1760
- var _legendRef$current2;
1761
- (_legendRef$current2 = legendRef.current) == null || _legendRef$current2.updateSelectedIndex(candidate1Data.length - 1);
1762
- }, [candidate1Data.length]);
1155
+ const sparklineTimePeriodDataValues = useMemo(() => {
1156
+ return sparklineTimePeriodData.map(d => d.value);
1157
+ }, [sparklineTimePeriodData]);
1763
1158
  const onPeriodChange = useCallback(period => {
1764
1159
  setTimePeriod(period || tabs[0]);
1765
1160
  }, [tabs]);
1766
- const scrubberLabel = useCallback(dataIndex => {
1767
- const date = timestamps[dataIndex];
1768
- const currentYear = new Date().getFullYear();
1769
- const shouldIncludeTime = timePeriod.id === 'hour' || timePeriod.id === 'day';
1770
- const dateOptions = _extends({
1771
- weekday: 'short',
1161
+ return /*#__PURE__*/_jsxs(VStack, {
1162
+ gap: 2,
1163
+ style: {
1164
+ marginLeft: -8,
1165
+ marginRight: -8
1166
+ },
1167
+ children: [/*#__PURE__*/_jsx(PerformanceHeader, {
1168
+ scrubberPosition: scrubberPosition,
1169
+ sparklineTimePeriodDataValues: sparklineTimePeriodDataValues
1170
+ }), /*#__PURE__*/_jsx(PerformanceChart, {
1171
+ onScrubberPositionChange: setScrubberPosition,
1172
+ timePeriod: timePeriod
1173
+ }), /*#__PURE__*/_jsx(PeriodSelector, {
1174
+ activeTab: timePeriod,
1175
+ onChange: onPeriodChange,
1176
+ tabs: tabs
1177
+ })]
1178
+ });
1179
+ }
1180
+ function MonotoneAssetPrice() {
1181
+ const theme = useTheme();
1182
+ const prices = sparklineInteractiveData.hour;
1183
+ const fontMgr = useMemo(() => {
1184
+ const fontProvider = Skia.TypefaceFontProvider.Make();
1185
+ // Register system fonts if available, otherwise Skia will use defaults
1186
+ return fontProvider;
1187
+ }, []);
1188
+ const priceFormatter = useMemo(() => new Intl.NumberFormat('en-US', {
1189
+ style: 'currency',
1190
+ currency: 'USD'
1191
+ }), []);
1192
+ const scrubberPriceFormatter = useMemo(() => new Intl.NumberFormat('en-US', {
1193
+ minimumFractionDigits: 2,
1194
+ maximumFractionDigits: 2
1195
+ }), []);
1196
+ const formatPrice = useCallback(price => {
1197
+ return priceFormatter.format(price);
1198
+ }, [priceFormatter]);
1199
+ const formatDate = useCallback(date => {
1200
+ const dayOfWeek = date.toLocaleDateString('en-US', {
1201
+ weekday: 'short'
1202
+ });
1203
+ const monthDay = date.toLocaleDateString('en-US', {
1772
1204
  month: 'short',
1773
- day: 'numeric',
1774
- year: date.getFullYear() !== currentYear ? 'numeric' : undefined
1775
- }, shouldIncludeTime && {
1776
- hour: 'numeric',
1777
- minute: '2-digit',
1778
- hour12: true
1205
+ day: 'numeric'
1779
1206
  });
1780
- const dateStr = shouldIncludeTime ? date.toLocaleString('en-US', dateOptions) : date.toLocaleDateString('en-US', dateOptions);
1781
- return dateStr;
1782
- }, [timestamps, timePeriod.id]);
1783
- const chartOverviewLabel = useMemo(() => {
1784
- if (periodData.length === 0) return '';
1785
- const firstDate = periodData[0].date;
1786
- const lastDate = periodData[periodData.length - 1].date;
1787
- const currentYear = new Date().getFullYear();
1788
- const shouldIncludeTime = timePeriod.id === 'hour' || timePeriod.id === 'day';
1789
- const dateRangeOptions = _extends({
1790
- month: 'long',
1791
- day: 'numeric',
1792
- year: firstDate.getFullYear() !== currentYear ? 'numeric' : undefined
1793
- }, shouldIncludeTime && {
1207
+ const time = date.toLocaleTimeString('en-US', {
1794
1208
  hour: 'numeric',
1795
1209
  minute: '2-digit',
1796
1210
  hour12: true
1797
1211
  });
1798
- const startDateStr = shouldIncludeTime ? firstDate.toLocaleString('en-US', dateRangeOptions) : firstDate.toLocaleDateString('en-US', dateRangeOptions);
1799
- const endDateStr = shouldIncludeTime ? lastDate.toLocaleString('en-US', _extends({}, dateRangeOptions, {
1800
- year: lastDate.getFullYear() !== currentYear ? 'numeric' : undefined
1801
- })) : lastDate.toLocaleDateString('en-US', _extends({}, dateRangeOptions, {
1802
- year: lastDate.getFullYear() !== currentYear ? 'numeric' : undefined
1803
- }));
1804
- return "Prediction chart, " + startDateStr + " to " + endDateStr + ". Swipe left or right to navigate data points.";
1805
- }, [periodData, timePeriod.id]);
1806
- return /*#__PURE__*/_jsx(Box, {
1807
- accessibilityLabel: chartOverviewLabel,
1808
- accessibilityLiveRegion: "polite",
1809
- children: /*#__PURE__*/_jsxs(VStack, {
1810
- gap: 4,
1811
- children: [/*#__PURE__*/_jsx(PredictionLegend, {
1812
- ref: legendRef,
1813
- colors: colors,
1814
- data: legendData
1815
- }), /*#__PURE__*/_jsx(Box, {
1816
- style: {
1817
- marginLeft: -16,
1818
- marginRight: -16
1819
- },
1820
- children: /*#__PURE__*/_jsx(LineChart, {
1821
- enableScrubbing: true,
1822
- showYAxis: true,
1823
- accessibilityLiveRegion: "polite",
1824
- height: defaultChartHeight,
1825
- inset: {
1826
- left: 0
1827
- },
1828
- onScrubberPositionChange: onScrubberPositionChange,
1829
- series: [{
1830
- id: 'candidate1',
1831
- data: candidate1Data,
1832
- color: colors.pink
1833
- }, {
1834
- id: 'candidate2',
1835
- data: candidate2Data,
1836
- color: colors.teal
1837
- }, {
1838
- id: 'candidate3',
1839
- data: candidate3Data,
1840
- color: colors.green
1841
- }],
1842
- xAxis: {
1843
- range: _ref7 => {
1844
- let {
1845
- min,
1846
- max
1847
- } = _ref7;
1848
- return {
1849
- min,
1850
- max: max - 32
1851
- };
1852
- }
1853
- },
1854
- yAxis: {
1855
- domain: {
1856
- min: 0,
1857
- max: 100
1858
- },
1859
- tickLabelFormatter: value => value + "%",
1860
- requestedTickCount: 5,
1861
- showGrid: true
1862
- },
1863
- children: /*#__PURE__*/_jsx(Scrubber, {
1864
- idlePulse: true,
1865
- label: scrubberLabel
1866
- })
1867
- })
1868
- }), /*#__PURE__*/_jsx(PeriodSelector, {
1869
- accessibilityLabel: "Select time period for prediction chart",
1870
- activeTab: timePeriod,
1871
- onChange: onPeriodChange,
1872
- tabs: tabs
1873
- })]
1874
- })
1875
- });
1876
- };
1877
- const AvailabilityChart = () => {
1878
- const theme = useTheme();
1879
- const [scrubIndex, setScrubIndex] = useState();
1880
- const ChartDefs = /*#__PURE__*/memo(_ref8 => {
1881
- var _rangeBounds$min, _rangeBounds$max, _yScale, _yScale2, _yScale3, _yScale4;
1212
+ return dayOfWeek + ", " + monthDay + ", " + time;
1213
+ }, []);
1214
+ const scrubberLabel = useCallback(index => {
1215
+ const price = scrubberPriceFormatter.format(prices[index].value);
1216
+ const date = formatDate(prices[index].date);
1217
+ const regularStyle = {
1218
+ fontFamilies: ['Inter'],
1219
+ fontSize: 14,
1220
+ fontStyle: {
1221
+ weight: FontWeight.Normal
1222
+ },
1223
+ color: Skia.Color(theme.color.fgMuted)
1224
+ };
1225
+ const boldStyle = _extends({
1226
+ fontFamilies: ['Inter']
1227
+ }, regularStyle, {
1228
+ fontStyle: {
1229
+ weight: FontWeight.Bold
1230
+ }
1231
+ });
1232
+ const builder = Skia.ParagraphBuilder.Make({
1233
+ textAlign: TextAlign.Left
1234
+ }, fontMgr);
1235
+ builder.pushStyle(boldStyle);
1236
+ builder.addText(price + " USD");
1237
+ builder.pushStyle(regularStyle);
1238
+ builder.addText(" " + date);
1239
+ const para = builder.build();
1240
+ para.layout(512);
1241
+ return para;
1242
+ }, [scrubberPriceFormatter, prices, formatDate, theme.color.fgMuted, fontMgr]);
1243
+ const formatAxisLabelPrice = useCallback(price => {
1244
+ return formatPrice(price);
1245
+ }, [formatPrice]);
1246
+
1247
+ // Custom tick label component with offset positioning
1248
+ const CustomYAxisTickLabel = useCallback(props => /*#__PURE__*/_jsx(DefaultAxisTickLabel, _extends({}, props, {
1249
+ dx: 4,
1250
+ dy: -12,
1251
+ horizontalAlignment: "left"
1252
+ })), []);
1253
+ const CustomScrubberBeacon = /*#__PURE__*/memo(_ref11 => {
1882
1254
  let {
1883
- yellowThresholdPercentage = 85,
1884
- greenThresholdPercentage = 90
1885
- } = _ref8;
1255
+ dataX,
1256
+ dataY,
1257
+ seriesId,
1258
+ isIdle,
1259
+ animate = true
1260
+ } = _ref11;
1886
1261
  const {
1887
- getYScale,
1888
- getYAxis
1262
+ getSeries,
1263
+ getXSerializableScale,
1264
+ getYSerializableScale
1889
1265
  } = useCartesianChartContext();
1890
- const yScale = getYScale();
1891
- const yAxis = getYAxis();
1892
- if (!yScale) return null;
1893
- const rangeBounds = yAxis == null ? void 0 : yAxis.domain;
1894
- const rangeMin = (_rangeBounds$min = rangeBounds == null ? void 0 : rangeBounds.min) != null ? _rangeBounds$min : 0;
1895
- const rangeMax = (_rangeBounds$max = rangeBounds == null ? void 0 : rangeBounds.max) != null ? _rangeBounds$max : 100;
1266
+ const targetSeries = useMemo(() => getSeries(seriesId), [getSeries, seriesId]);
1267
+ const xScale = useMemo(() => getXSerializableScale(), [getXSerializableScale]);
1268
+ const yScale = useMemo(() => getYSerializableScale(targetSeries == null ? void 0 : targetSeries.yAxisId), [getYSerializableScale, targetSeries == null ? void 0 : targetSeries.yAxisId]);
1269
+ const animatedX = useSharedValue(0);
1270
+ const animatedY = useSharedValue(0);
1896
1271
 
1897
- // Calculate the Y positions in the chart coordinate system
1898
- const yellowThresholdY = (_yScale = yScale(yellowThresholdPercentage)) != null ? _yScale : 0;
1899
- const greenThresholdY = (_yScale2 = yScale(greenThresholdPercentage)) != null ? _yScale2 : 0;
1900
- const minY = (_yScale3 = yScale(rangeMax)) != null ? _yScale3 : 0; // Top of chart (max value)
1901
- const maxY = (_yScale4 = yScale(rangeMin)) != null ? _yScale4 : 0; // Bottom of chart (min value)
1272
+ // Calculate the target point position - project data to pixels
1273
+ const targetPoint = useDerivedValue(() => {
1274
+ if (!xScale || !yScale) return {
1275
+ x: 0,
1276
+ y: 0
1277
+ };
1278
+ return projectPointWithSerializableScale({
1279
+ x: unwrapAnimatedValue(dataX),
1280
+ y: unwrapAnimatedValue(dataY),
1281
+ xScale,
1282
+ yScale
1283
+ });
1284
+ }, [dataX, dataY, xScale, yScale]);
1285
+ useAnimatedReaction(() => {
1286
+ return {
1287
+ point: targetPoint.value,
1288
+ isIdle: unwrapAnimatedValue(isIdle)
1289
+ };
1290
+ }, (current, previous) => {
1291
+ // When animation is disabled, on initial render, or when we are starting,
1292
+ // continuing, or finishing scrubbing we should immediately transition
1293
+ if (!animate || previous === null || !previous.isIdle || !current.isIdle) {
1294
+ animatedX.value = current.point.x;
1295
+ animatedY.value = current.point.y;
1296
+ return;
1297
+ }
1298
+ animatedX.value = buildTransition(current.point.x, defaultTransition);
1299
+ animatedY.value = buildTransition(current.point.y, defaultTransition);
1300
+ }, [animate]);
1902
1301
 
1903
- // Calculate percentages based on actual chart positions
1904
- const yellowThreshold = (yellowThresholdY - minY) / (maxY - minY) * 100;
1905
- const greenThreshold = (greenThresholdY - minY) / (maxY - minY) * 100;
1906
- return /*#__PURE__*/_jsx(Defs, {
1907
- children: /*#__PURE__*/_jsxs(LinearGradient, {
1908
- gradientUnits: "userSpaceOnUse",
1909
- id: "availabilityGradient",
1910
- x1: "0%",
1911
- x2: "0%",
1912
- y1: minY,
1913
- y2: maxY,
1914
- children: [/*#__PURE__*/_jsx(Stop, {
1915
- offset: "0%",
1916
- stopColor: theme.color.fgPositive
1917
- }), /*#__PURE__*/_jsx(Stop, {
1918
- offset: greenThreshold + "%",
1919
- stopColor: theme.color.fgPositive
1920
- }), /*#__PURE__*/_jsx(Stop, {
1921
- offset: greenThreshold + "%",
1922
- stopColor: theme.color.fgWarning
1923
- }), /*#__PURE__*/_jsx(Stop, {
1924
- offset: yellowThreshold + "%",
1925
- stopColor: theme.color.fgWarning
1926
- }), /*#__PURE__*/_jsx(Stop, {
1927
- offset: yellowThreshold + "%",
1928
- stopColor: theme.color.fgNegative
1929
- }), /*#__PURE__*/_jsx(Stop, {
1930
- offset: "100%",
1931
- stopColor: theme.color.fgNegative
1932
- })]
1933
- })
1302
+ // Create animated point using the animated values
1303
+ const animatedPoint = useDerivedValue(() => {
1304
+ return {
1305
+ x: animatedX.value,
1306
+ y: animatedY.value
1307
+ };
1308
+ }, [animatedX, animatedY]);
1309
+ return /*#__PURE__*/_jsxs(_Fragment, {
1310
+ children: [/*#__PURE__*/_jsx(Circle, {
1311
+ c: animatedPoint,
1312
+ color: theme.color.bg,
1313
+ r: 5
1314
+ }), /*#__PURE__*/_jsx(Circle, {
1315
+ c: animatedPoint,
1316
+ color: theme.color.fg,
1317
+ r: 5,
1318
+ strokeWidth: 3,
1319
+ style: "stroke"
1320
+ })]
1934
1321
  });
1935
1322
  });
1323
+ return /*#__PURE__*/_jsx(LineChart, {
1324
+ enableScrubbing: true,
1325
+ showYAxis: true,
1326
+ height: 200,
1327
+ inset: {
1328
+ top: 64
1329
+ },
1330
+ series: [{
1331
+ id: 'btc',
1332
+ data: prices.map(price => price.value),
1333
+ color: theme.color.fg,
1334
+ gradient: {
1335
+ axis: 'x',
1336
+ stops: _ref12 => {
1337
+ let {
1338
+ min
1339
+ } = _ref12;
1340
+ return [{
1341
+ offset: min,
1342
+ color: theme.color.fg,
1343
+ opacity: 0
1344
+ }, {
1345
+ offset: 32,
1346
+ color: theme.color.fg,
1347
+ opacity: 1
1348
+ }];
1349
+ }
1350
+ }
1351
+ }],
1352
+ xAxis: {
1353
+ range: _ref13 => {
1354
+ let {
1355
+ max
1356
+ } = _ref13;
1357
+ return {
1358
+ min: 96,
1359
+ max
1360
+ };
1361
+ }
1362
+ },
1363
+ yAxis: {
1364
+ position: 'left',
1365
+ width: 0,
1366
+ showGrid: true,
1367
+ tickLabelFormatter: formatAxisLabelPrice,
1368
+ TickLabelComponent: CustomYAxisTickLabel
1369
+ },
1370
+ children: /*#__PURE__*/_jsx(Scrubber, {
1371
+ hideOverlay: true,
1372
+ labelElevated: true,
1373
+ BeaconComponent: CustomScrubberBeacon,
1374
+ LineComponent: SolidLine,
1375
+ label: scrubberLabel
1376
+ })
1377
+ });
1378
+ }
1379
+ function ServiceAvailability() {
1380
+ const theme = useTheme();
1381
+ const availabilityEvents = useMemo(() => [{
1382
+ date: new Date('2022-01-01'),
1383
+ availability: 79
1384
+ }, {
1385
+ date: new Date('2022-01-03'),
1386
+ availability: 81
1387
+ }, {
1388
+ date: new Date('2022-01-04'),
1389
+ availability: 82
1390
+ }, {
1391
+ date: new Date('2022-01-06'),
1392
+ availability: 91
1393
+ }, {
1394
+ date: new Date('2022-01-07'),
1395
+ availability: 92
1396
+ }, {
1397
+ date: new Date('2022-01-10'),
1398
+ availability: 86
1399
+ }], []);
1936
1400
  return /*#__PURE__*/_jsxs(CartesianChart, {
1937
1401
  enableScrubbing: true,
1938
- height: defaultChartHeight,
1939
- onScrubberPositionChange: setScrubIndex,
1402
+ height: 200,
1940
1403
  series: [{
1941
1404
  id: 'availability',
1942
1405
  data: availabilityEvents.map(event => event.availability),
1943
- color: 'url(#availabilityGradient)'
1406
+ gradient: {
1407
+ stops: _ref14 => {
1408
+ let {
1409
+ min,
1410
+ max
1411
+ } = _ref14;
1412
+ return [{
1413
+ offset: min,
1414
+ color: theme.color.fgNegative
1415
+ }, {
1416
+ offset: 85,
1417
+ color: theme.color.fgNegative
1418
+ }, {
1419
+ offset: 85,
1420
+ color: theme.color.fgWarning
1421
+ }, {
1422
+ offset: 90,
1423
+ color: theme.color.fgWarning
1424
+ }, {
1425
+ offset: 90,
1426
+ color: theme.color.fgPositive
1427
+ }, {
1428
+ offset: max,
1429
+ color: theme.color.fgPositive
1430
+ }];
1431
+ }
1432
+ }
1944
1433
  }],
1945
1434
  xAxis: {
1946
1435
  data: availabilityEvents.map(event => event.date.getTime())
1947
1436
  },
1948
1437
  yAxis: {
1949
- domain: _ref9 => {
1438
+ domain: _ref15 => {
1950
1439
  let {
1951
1440
  min,
1952
1441
  max
1953
- } = _ref9;
1442
+ } = _ref15;
1954
1443
  return {
1955
1444
  min: Math.max(min - 2, 0),
1956
1445
  max: Math.min(max + 2, 100)
1957
1446
  };
1958
1447
  }
1959
1448
  },
1960
- children: [/*#__PURE__*/_jsx(ChartDefs, {}), /*#__PURE__*/_jsx(XAxis, {
1449
+ children: [/*#__PURE__*/_jsx(XAxis, {
1961
1450
  showGrid: true,
1962
1451
  showLine: true,
1963
1452
  showTickMarks: true,
@@ -1970,279 +1459,490 @@ const AvailabilityChart = () => {
1970
1459
  tickLabelFormatter: value => value + "%"
1971
1460
  }), /*#__PURE__*/_jsx(Line, {
1972
1461
  curve: "stepAfter",
1973
- renderPoints: () => ({
1462
+ points: props => _extends({}, props, {
1974
1463
  fill: theme.color.bg,
1975
- stroke: 'url(#availabilityGradient)',
1976
- strokeWidth: 2
1464
+ stroke: props.fill
1977
1465
  }),
1978
1466
  seriesId: "availability"
1979
1467
  }), /*#__PURE__*/_jsx(Scrubber, {
1980
- overlayOffset: 10
1468
+ hideOverlay: true
1981
1469
  })]
1982
1470
  });
1983
- };
1984
- const sampleData = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
1985
- const LineChartStories = () => {
1986
- const theme = useTheme();
1987
- return /*#__PURE__*/_jsxs(ExampleScreen, {
1988
- children: [/*#__PURE__*/_jsx(Example, {
1989
- title: "Basic",
1471
+ }
1472
+ function ForecastAssetPrice() {
1473
+ const startYear = 2020;
1474
+ const data = [50, 45, 47, 46, 54, 54, 60, 61, 63, 66, 70];
1475
+ const currentIndex = 6;
1476
+ const strokeWidth = 3;
1477
+ // To prevent cutting off the edge of our lines
1478
+ const clipOffset = strokeWidth;
1479
+ const axisFormatter = useCallback(dataIndex => {
1480
+ return "" + (startYear + dataIndex);
1481
+ }, [startYear]);
1482
+ const HistoricalLineComponent = /*#__PURE__*/memo(props => {
1483
+ const {
1484
+ drawingArea,
1485
+ getXScale
1486
+ } = useCartesianChartContext();
1487
+ const xScale = getXScale();
1488
+ const historicalClipPath = useMemo(() => {
1489
+ if (!xScale || !drawingArea) return null;
1490
+ const currentX = xScale(currentIndex);
1491
+ if (currentX === undefined) return null;
1492
+
1493
+ // Create clip path for historical data (left side)
1494
+ const clip = Skia.Path.Make();
1495
+ clip.addRect({
1496
+ x: drawingArea.x - clipOffset,
1497
+ y: drawingArea.y - clipOffset,
1498
+ width: currentX + clipOffset - drawingArea.x,
1499
+ height: drawingArea.height + clipOffset * 2
1500
+ });
1501
+ return clip;
1502
+ }, [xScale, drawingArea]);
1503
+ if (!historicalClipPath) return null;
1504
+ return /*#__PURE__*/_jsx(Group, {
1505
+ clip: historicalClipPath,
1506
+ children: /*#__PURE__*/_jsx(SolidLine, _extends({
1507
+ strokeWidth: strokeWidth
1508
+ }, props))
1509
+ });
1510
+ });
1511
+
1512
+ // Since the solid and dotted line have different curves,
1513
+ // we need two separate line components. Otherwise we could
1514
+ // have one line component with SolidLine and DottedLine inside
1515
+ // of it and two clipPaths.
1516
+ const ForecastLineComponent = /*#__PURE__*/memo(props => {
1517
+ const {
1518
+ drawingArea,
1519
+ getXScale
1520
+ } = useCartesianChartContext();
1521
+ const xScale = getXScale();
1522
+ const forecastClipPath = useMemo(() => {
1523
+ if (!xScale || !drawingArea) return null;
1524
+ const currentX = xScale(currentIndex);
1525
+ if (currentX === undefined) return null;
1526
+
1527
+ // Create clip path for forecast data (right side)
1528
+ const clip = Skia.Path.Make();
1529
+ clip.addRect({
1530
+ x: currentX,
1531
+ y: drawingArea.y - clipOffset,
1532
+ width: drawingArea.x + drawingArea.width - currentX + clipOffset * 2,
1533
+ height: drawingArea.height + clipOffset * 2
1534
+ });
1535
+ return clip;
1536
+ }, [xScale, drawingArea]);
1537
+ if (!forecastClipPath) return null;
1538
+ return /*#__PURE__*/_jsx(Group, {
1539
+ clip: forecastClipPath,
1540
+ children: /*#__PURE__*/_jsx(DottedLine, _extends({
1541
+ dashIntervals: [0, strokeWidth * 2],
1542
+ strokeWidth: strokeWidth
1543
+ }, props))
1544
+ });
1545
+ });
1546
+ const CustomScrubber = /*#__PURE__*/memo(() => {
1547
+ const {
1548
+ scrubberPosition
1549
+ } = useScrubberContext();
1550
+ const idleScrubberOpacity = useDerivedValue(() => scrubberPosition.value === undefined ? 1 : 0, [scrubberPosition]);
1551
+ const scrubberOpacity = useDerivedValue(() => scrubberPosition.value !== undefined ? 1 : 0, [scrubberPosition]);
1552
+
1553
+ // Fade in animation for the Scrubber
1554
+ const fadeInOpacity = useSharedValue(0);
1555
+ useEffect(() => {
1556
+ fadeInOpacity.value = withDelay(350, withTiming(1, {
1557
+ duration: 150
1558
+ }));
1559
+ }, [fadeInOpacity]);
1560
+ return /*#__PURE__*/_jsxs(Group, {
1561
+ opacity: fadeInOpacity,
1562
+ children: [/*#__PURE__*/_jsx(Group, {
1563
+ opacity: scrubberOpacity,
1564
+ children: /*#__PURE__*/_jsx(Scrubber, {
1565
+ hideOverlay: true
1566
+ })
1567
+ }), /*#__PURE__*/_jsx(Group, {
1568
+ opacity: idleScrubberOpacity,
1569
+ children: /*#__PURE__*/_jsx(DefaultScrubberBeacon, {
1570
+ isIdle: true,
1571
+ dataX: currentIndex,
1572
+ dataY: data[currentIndex],
1573
+ seriesId: "price"
1574
+ })
1575
+ })]
1576
+ });
1577
+ });
1578
+ return /*#__PURE__*/_jsxs(CartesianChart, {
1579
+ enableScrubbing: true,
1580
+ height: 200,
1581
+ series: [{
1582
+ id: 'price',
1583
+ data,
1584
+ color: assets.btc.color
1585
+ }],
1586
+ children: [/*#__PURE__*/_jsx(Line, {
1587
+ LineComponent: HistoricalLineComponent,
1588
+ curve: "linear",
1589
+ seriesId: "price"
1590
+ }), /*#__PURE__*/_jsx(Line, {
1591
+ LineComponent: ForecastLineComponent,
1592
+ curve: "monotone",
1593
+ seriesId: "price",
1594
+ type: "dotted"
1595
+ }), /*#__PURE__*/_jsx(XAxis, {
1596
+ position: "bottom",
1597
+ requestedTickCount: 3,
1598
+ tickLabelFormatter: axisFormatter
1599
+ }), /*#__PURE__*/_jsx(CustomScrubber, {})]
1600
+ });
1601
+ }
1602
+ function DataCardWithLineChart() {
1603
+ const {
1604
+ spectrum
1605
+ } = useTheme();
1606
+ const exampleThumbnail = /*#__PURE__*/_jsx(RemoteImage, {
1607
+ accessibilityLabel: "Ethereum",
1608
+ shape: "circle",
1609
+ size: "xl",
1610
+ source: ethBackground,
1611
+ testID: "thumbnail"
1612
+ });
1613
+ const getLineChartSeries = useCallback(() => [{
1614
+ id: 'price',
1615
+ data: prices.slice(0, 30).map(price => parseFloat(price)),
1616
+ color: "rgb(" + spectrum.green70 + ")"
1617
+ }], [spectrum.green70]);
1618
+ const lineChartSeries = useMemo(() => getLineChartSeries(), [getLineChartSeries]);
1619
+ const lineChartSeries2 = useMemo(() => getLineChartSeries(), [getLineChartSeries]);
1620
+ const ref = useRef(null);
1621
+ return /*#__PURE__*/_jsxs(VStack, {
1622
+ gap: 2,
1623
+ children: [/*#__PURE__*/_jsx(DataCard, {
1624
+ layout: "vertical",
1625
+ subtitle: "Price trend",
1626
+ thumbnail: exampleThumbnail,
1627
+ title: "Line Chart Card",
1990
1628
  children: /*#__PURE__*/_jsx(LineChart, {
1991
- enableScrubbing: true,
1992
1629
  showArea: true,
1993
- showYAxis: true,
1994
- curve: "monotone",
1995
- height: defaultChartHeight,
1996
- series: [{
1997
- id: 'prices',
1998
- data: sampleData
1999
- }],
2000
- yAxis: {
2001
- showGrid: true
2002
- },
2003
- children: /*#__PURE__*/_jsx(Scrubber, {})
2004
- })
2005
- }), /*#__PURE__*/_jsx(Example, {
2006
- title: "Simple",
2007
- children: /*#__PURE__*/_jsx(LineChart, {
2008
- curve: "monotone",
2009
- height: defaultChartHeight,
2010
- series: [{
2011
- id: 'prices',
2012
- data: sampleData
2013
- }]
2014
- })
2015
- }), /*#__PURE__*/_jsx(Example, {
2016
- title: "Gain/Loss",
2017
- children: /*#__PURE__*/_jsx(GainLossChart, {})
2018
- }), /*#__PURE__*/_jsx(Example, {
2019
- title: "BTC Price Chart",
2020
- children: /*#__PURE__*/_jsx(BTCPriceChart, {})
2021
- }), /*#__PURE__*/_jsx(Example, {
2022
- title: "Price Chart",
2023
- children: /*#__PURE__*/_jsx(PriceChart, {})
2024
- }), /*#__PURE__*/_jsx(Example, {
2025
- title: "Asset Price Dotted",
2026
- children: /*#__PURE__*/_jsx(AssetPriceDotted, {})
2027
- }), /*#__PURE__*/_jsx(Example, {
2028
- title: "Multiple Series",
2029
- children: /*#__PURE__*/_jsx(LineChart, {
2030
- enableScrubbing: true,
2031
- showXAxis: true,
2032
- showYAxis: true,
2033
- height: defaultChartHeight,
2034
- series: [{
2035
- id: 'pageViews',
2036
- data: [2400, 1398, 9800, 3908, 4800, 3800, 4300],
2037
- label: 'Page Views',
2038
- color: theme.color.accentBoldBlue,
2039
- curve: 'natural'
2040
- }, {
2041
- id: 'uniqueVisitors',
2042
- data: [4000, 3000, 2000, 2780, 1890, 2390, 3490],
2043
- label: 'Unique Visitors',
2044
- color: theme.color.accentBoldGreen,
2045
- curve: 'natural'
2046
- }],
2047
- xAxis: {
2048
- data: ['Page A', 'Page B', 'Page C', 'Page D', 'Page E', 'Page F', 'Page G'],
2049
- scaleType: 'band'
2050
- },
2051
- yAxis: {
2052
- domain: {
2053
- min: 0
2054
- },
2055
- showGrid: true,
2056
- tickLabelFormatter: value => value.toLocaleString()
2057
- },
2058
- children: /*#__PURE__*/_jsx(Scrubber, {})
2059
- })
2060
- }), /*#__PURE__*/_jsx(Example, {
2061
- title: "Points",
2062
- children: /*#__PURE__*/_jsxs(CartesianChart, {
2063
- height: defaultChartHeight,
2064
- series: [{
2065
- id: 'prices',
2066
- data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58]
2067
- }],
2068
- children: [/*#__PURE__*/_jsx(Area, {
2069
- curve: "monotone",
2070
- fill: "rgb(" + theme.spectrum.blue5 + ")",
2071
- seriesId: "prices"
2072
- }), /*#__PURE__*/_jsx(Line, {
2073
- curve: "monotone",
2074
- renderPoints: _ref0 => {
2075
- let {
2076
- dataX
2077
- } = _ref0,
2078
- props = _objectWithoutPropertiesLoose(_ref0, _excluded4);
2079
- return [4, 6, 7, 9, 10].includes(dataX) ? _extends({}, props, {
2080
- strokeWidth: 2,
2081
- stroke: theme.color.bg,
2082
- radius: 5,
2083
- onClick: () => alert('You have clicked a key market shift!')
2084
- }) : false;
2085
- },
2086
- seriesId: "prices"
2087
- })]
1630
+ accessibilityLabel: "Ethereum price chart",
1631
+ areaType: "dotted",
1632
+ height: 120,
1633
+ inset: 0,
1634
+ series: lineChartSeries
2088
1635
  })
2089
- }), /*#__PURE__*/_jsx(Example, {
2090
- title: "Data Formats",
1636
+ }), /*#__PURE__*/_jsx(DataCard, {
1637
+ layout: "vertical",
1638
+ subtitle: "Price trend",
1639
+ thumbnail: exampleThumbnail,
1640
+ title: "Line Chart with Tag",
1641
+ titleAccessory: /*#__PURE__*/_jsx(Text, {
1642
+ dangerouslySetColor: "rgb(" + spectrum.green70 + ")",
1643
+ font: "label1",
1644
+ children: "\u2197 25.25%"
1645
+ }),
2091
1646
  children: /*#__PURE__*/_jsx(LineChart, {
2092
- enableScrubbing: true,
2093
1647
  showArea: true,
2094
- showXAxis: true,
2095
- showYAxis: true,
2096
- curve: "natural",
2097
- height: defaultChartHeight,
2098
- renderPoints: () => true,
2099
- series: [{
2100
- id: 'line',
2101
- data: [2, 5.5, 2, 8.5, 1.5, 5]
2102
- }],
2103
- xAxis: {
2104
- data: [1, 2, 3, 5, 8, 10],
2105
- showLine: true,
2106
- showTickMarks: true,
2107
- showGrid: true
2108
- },
2109
- yAxis: {
2110
- domain: {
2111
- min: 0
2112
- },
2113
- position: 'left',
2114
- showLine: true,
2115
- showTickMarks: true,
2116
- showGrid: true
2117
- },
2118
- children: /*#__PURE__*/_jsx(Scrubber, {})
1648
+ accessibilityLabel: "Ethereum price chart",
1649
+ areaType: "dotted",
1650
+ height: 100,
1651
+ inset: 0,
1652
+ series: lineChartSeries
2119
1653
  })
2120
- }), /*#__PURE__*/_jsx(Example, {
2121
- title: "Bitcoin Chart with Scrubber Beacon",
2122
- children: /*#__PURE__*/_jsx(BitcoinChartWithScrubberBeacon, {})
2123
- }), /*#__PURE__*/_jsx(Example, {
2124
- title: "Scrubber with Imperative Handle",
2125
- children: /*#__PURE__*/_jsx(ScrubberWithImperativeHandle, {})
2126
- }), /*#__PURE__*/_jsx(Example, {
2127
- title: "Asset Price",
2128
- children: /*#__PURE__*/_jsx(AssetPrice, {})
2129
- }), /*#__PURE__*/_jsx(Example, {
2130
- title: "Line Styles",
2131
- children: /*#__PURE__*/_jsx(LineStyles, {})
2132
- }), /*#__PURE__*/_jsx(Example, {
2133
- title: "Chart Scale",
2134
- children: /*#__PURE__*/_jsx(ChartScale, {})
2135
- }), /*#__PURE__*/_jsx(Example, {
2136
- title: "Color Shift Chart",
2137
- children: /*#__PURE__*/_jsx(ColorShiftChart, {})
2138
- }), /*#__PURE__*/_jsx(Example, {
2139
- title: "Price Chart",
2140
- children: /*#__PURE__*/_jsx(PriceChart, {})
2141
- }), /*#__PURE__*/_jsx(Example, {
2142
- title: "Forecast Chart",
2143
- children: /*#__PURE__*/_jsx(ForecastChart, {})
2144
- }), /*#__PURE__*/_jsx(Example, {
2145
- title: "Period Selector",
2146
- children: /*#__PURE__*/_jsx(PeriodSelectorExample, {})
2147
- }), /*#__PURE__*/_jsx(Example, {
2148
- title: "Live Asset Price",
2149
- children: /*#__PURE__*/_jsx(LiveAssetPrice, {})
2150
- }), /*#__PURE__*/_jsx(Example, {
2151
- title: "Availability Chart",
2152
- children: /*#__PURE__*/_jsx(AvailabilityChart, {})
2153
- })]
2154
- });
2155
- };
2156
- const AssetPriceScreen = () => {
2157
- return /*#__PURE__*/_jsxs(ExampleScreen, {
2158
- children: [/*#__PURE__*/_jsx(Example, {
2159
- title: "Scrubber with Imperative Handle",
2160
- children: /*#__PURE__*/_jsx(ScrubberWithImperativeHandle, {})
2161
- }), /*#__PURE__*/_jsx(Example, {
2162
- title: "Basic",
1654
+ }), /*#__PURE__*/_jsx(DataCard, {
1655
+ ref: ref,
1656
+ renderAsPressable: true,
1657
+ layout: "vertical",
1658
+ onPress: NoopFn,
1659
+ subtitle: "Clickable line chart card",
1660
+ thumbnail: exampleThumbnail,
1661
+ title: "Actionable Line Chart",
1662
+ titleAccessory: /*#__PURE__*/_jsx(Text, {
1663
+ dangerouslySetColor: "rgb(" + spectrum.green70 + ")",
1664
+ font: "label1",
1665
+ children: "\u2197 8.5%"
1666
+ }),
2163
1667
  children: /*#__PURE__*/_jsx(LineChart, {
2164
- enableScrubbing: true,
2165
1668
  showArea: true,
2166
- showYAxis: true,
2167
- curve: "monotone",
2168
- height: defaultChartHeight,
2169
- series: [{
2170
- id: 'prices',
2171
- data: sampleData
2172
- }],
2173
- yAxis: {
2174
- showGrid: true
2175
- },
2176
- children: /*#__PURE__*/_jsx(Scrubber, {})
2177
- })
2178
- }), /*#__PURE__*/_jsx(Example, {
2179
- title: "Simple",
2180
- children: /*#__PURE__*/_jsx(LineChart, {
2181
- curve: "monotone",
2182
- height: defaultChartHeight,
2183
- series: [{
2184
- id: 'prices',
2185
- data: sampleData
2186
- }]
1669
+ accessibilityLabel: "Ethereum price chart",
1670
+ areaType: "dotted",
1671
+ height: 120,
1672
+ inset: 0,
1673
+ series: lineChartSeries,
1674
+ showXAxis: false,
1675
+ showYAxis: false
2187
1676
  })
2188
- }), /*#__PURE__*/_jsx(Example, {
2189
- title: "Data Formats",
1677
+ }), /*#__PURE__*/_jsx(DataCard, {
1678
+ layout: "vertical",
1679
+ subtitle: "Price trend",
1680
+ thumbnail: /*#__PURE__*/_jsx(RemoteImage, {
1681
+ accessibilityLabel: "Bitcoin",
1682
+ shape: "circle",
1683
+ size: "xl",
1684
+ source: assets.btc.imageUrl,
1685
+ testID: "thumbnail"
1686
+ }),
1687
+ title: "Card with Line Chart",
1688
+ titleAccessory: /*#__PURE__*/_jsx(Text, {
1689
+ dangerouslySetColor: "rgb(" + spectrum.green70 + ")",
1690
+ font: "label1",
1691
+ children: "\u2197 25.25%"
1692
+ }),
2190
1693
  children: /*#__PURE__*/_jsx(LineChart, {
2191
- enableScrubbing: true,
2192
1694
  showArea: true,
2193
- showXAxis: true,
2194
- showYAxis: true,
2195
- curve: "natural",
2196
- height: defaultChartHeight,
2197
- series: [{
2198
- id: 'line',
2199
- data: [2, 5.5, 2, 8.5, 1.5, 5]
2200
- }],
2201
- xAxis: {
2202
- data: [1, 2, 3, 5, 8, 10],
2203
- showLine: true,
2204
- showTickMarks: true,
2205
- showGrid: true
2206
- },
2207
- yAxis: {
2208
- domain: {
2209
- min: 0
2210
- },
2211
- position: 'left',
2212
- showLine: true,
2213
- showTickMarks: true,
2214
- showGrid: true
2215
- },
2216
- children: /*#__PURE__*/_jsx(Scrubber, {})
1695
+ accessibilityLabel: "Price chart",
1696
+ areaType: "dotted",
1697
+ height: 100,
1698
+ inset: 0,
1699
+ series: lineChartSeries2,
1700
+ showXAxis: false,
1701
+ showYAxis: false
2217
1702
  })
2218
- }), /*#__PURE__*/_jsx(Example, {
2219
- title: "Color Shift Chart",
2220
- children: /*#__PURE__*/_jsx(ColorShiftChart, {})
2221
- }), /*#__PURE__*/_jsx(Example, {
2222
- title: "Asset Price Dotted",
2223
- children: /*#__PURE__*/_jsx(AssetPriceDotted, {})
2224
- }), /*#__PURE__*/_jsx(Example, {
2225
- title: "Asset Price Multiple Dotted",
2226
- children: /*#__PURE__*/_jsx(AssetPriceMultipleDotted, {})
2227
- }), /*#__PURE__*/_jsx(Example, {
2228
- title: "Asset Price Dotted (Old)",
2229
- children: /*#__PURE__*/_jsx(AssetPriceDottedNonMemoized, {})
2230
- }), /*#__PURE__*/_jsx(Example, {
2231
- title: "BTC Price Chart",
2232
- children: /*#__PURE__*/_jsx(BTCPriceChart, {})
2233
- }), /*#__PURE__*/_jsx(Example, {
2234
- title: "Gain/Loss",
2235
- children: /*#__PURE__*/_jsx(GainLossChart, {})
2236
- }), /*#__PURE__*/_jsx(Example, {
2237
- title: "Live Asset Price",
2238
- children: /*#__PURE__*/_jsx(LiveAssetPrice, {})
2239
- }), /*#__PURE__*/_jsx(Example, {
2240
- title: "Prediction Chart",
2241
- children: /*#__PURE__*/_jsx(PredictionChart, {})
2242
- }), /*#__PURE__*/_jsx(Example, {
2243
- title: "Availability Chart",
2244
- children: /*#__PURE__*/_jsx(AvailabilityChart, {})
2245
1703
  })]
2246
1704
  });
2247
- };
2248
- export default AssetPriceScreen;
1705
+ }
1706
+ function ExampleNavigator() {
1707
+ const theme = useTheme();
1708
+ const [currentIndex, setCurrentIndex] = useState(0);
1709
+ const examples = useMemo(() => [{
1710
+ title: 'Basic',
1711
+ component: /*#__PURE__*/_jsx(LineChart, {
1712
+ showArea: true,
1713
+ height: 200,
1714
+ series: [{
1715
+ id: 'prices',
1716
+ data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58]
1717
+ }]
1718
+ })
1719
+ }, {
1720
+ title: 'Multiple Lines',
1721
+ component: /*#__PURE__*/_jsx(MultipleLine, {})
1722
+ }, {
1723
+ title: 'Data Format',
1724
+ component: /*#__PURE__*/_jsx(DataFormat, {})
1725
+ }, {
1726
+ title: 'Live Updates',
1727
+ component: /*#__PURE__*/_jsx(LiveUpdates, {})
1728
+ }, {
1729
+ title: 'Missing Data',
1730
+ component: /*#__PURE__*/_jsx(MissingData, {})
1731
+ }, {
1732
+ title: 'Empty State',
1733
+ component: /*#__PURE__*/_jsx(LineChart, {
1734
+ height: 200,
1735
+ series: [{
1736
+ id: 'line',
1737
+ color: "rgb(" + theme.spectrum.gray50 + ")",
1738
+ data: [1, 1],
1739
+ showArea: true
1740
+ }],
1741
+ yAxis: {
1742
+ domain: {
1743
+ min: -1,
1744
+ max: 3
1745
+ }
1746
+ }
1747
+ })
1748
+ }, {
1749
+ title: 'Scales',
1750
+ component: /*#__PURE__*/_jsx(LineChart, {
1751
+ showArea: true,
1752
+ showYAxis: true,
1753
+ height: 200,
1754
+ series: [{
1755
+ id: 'prices',
1756
+ data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58]
1757
+ }],
1758
+ yAxis: {
1759
+ scaleType: 'log',
1760
+ showGrid: true,
1761
+ ticks: [1, 10, 100]
1762
+ }
1763
+ })
1764
+ }, {
1765
+ title: 'Interaction',
1766
+ component: /*#__PURE__*/_jsx(Interaction, {})
1767
+ }, {
1768
+ title: 'Points',
1769
+ component: /*#__PURE__*/_jsx(Points, {})
1770
+ }, {
1771
+ title: 'Transitions',
1772
+ component: /*#__PURE__*/_jsx(Transitions, {})
1773
+ }, {
1774
+ title: 'Basic Accessible',
1775
+ component: /*#__PURE__*/_jsx(BasicAccessible, {})
1776
+ }, {
1777
+ title: 'Styling Axes',
1778
+ component: /*#__PURE__*/_jsx(LineChart, {
1779
+ showArea: true,
1780
+ showXAxis: true,
1781
+ showYAxis: true,
1782
+ height: 200,
1783
+ series: [{
1784
+ id: 'prices',
1785
+ data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58]
1786
+ }],
1787
+ xAxis: {
1788
+ showGrid: true,
1789
+ showLine: true,
1790
+ showTickMarks: true,
1791
+ tickLabelFormatter: dataX => "Day " + dataX
1792
+ },
1793
+ yAxis: {
1794
+ showGrid: true,
1795
+ showLine: true,
1796
+ showTickMarks: true
1797
+ }
1798
+ })
1799
+ }, {
1800
+ title: 'Gradients',
1801
+ component: /*#__PURE__*/_jsx(Gradients, {})
1802
+ }, {
1803
+ title: 'Gain/Loss',
1804
+ component: /*#__PURE__*/_jsx(GainLossChart, {})
1805
+ }, {
1806
+ title: 'Styling Lines',
1807
+ component: /*#__PURE__*/_jsx(LineChart, {
1808
+ height: 200,
1809
+ series: [{
1810
+ id: 'top',
1811
+ data: [15, 28, 32, 44, 46, 36, 40, 45, 48, 38]
1812
+ }, {
1813
+ id: 'upperMiddle',
1814
+ data: [12, 23, 21, 29, 34, 28, 31, 38, 42, 35],
1815
+ color: '#ef4444',
1816
+ type: 'dotted'
1817
+ }, {
1818
+ id: 'lowerMiddle',
1819
+ data: [8, 15, 14, 25, 20, 18, 22, 28, 24, 30],
1820
+ color: '#f59e0b',
1821
+ curve: 'natural',
1822
+ gradient: {
1823
+ axis: 'x',
1824
+ stops: [{
1825
+ offset: 0,
1826
+ color: '#E3D74D'
1827
+ }, {
1828
+ offset: 9,
1829
+ color: '#F7931A'
1830
+ }]
1831
+ },
1832
+ strokeWidth: 6
1833
+ }, {
1834
+ id: 'bottom',
1835
+ data: [4, 8, 11, 15, 16, 14, 16, 10, 12, 14],
1836
+ color: '#800080',
1837
+ curve: 'step',
1838
+ AreaComponent: DottedArea,
1839
+ showArea: true
1840
+ }]
1841
+ })
1842
+ }, {
1843
+ title: 'Styling Reference Lines',
1844
+ component: /*#__PURE__*/_jsxs(LineChart, {
1845
+ enableScrubbing: true,
1846
+ showArea: true,
1847
+ height: 200,
1848
+ series: [{
1849
+ id: 'prices',
1850
+ data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
1851
+ color: theme.color.fgPositive
1852
+ }],
1853
+ xAxis: {
1854
+ // Give space before the end of the chart for the scrubber
1855
+ range: _ref16 => {
1856
+ let {
1857
+ min,
1858
+ max
1859
+ } = _ref16;
1860
+ return {
1861
+ min,
1862
+ max: max - 24
1863
+ };
1864
+ }
1865
+ },
1866
+ children: [/*#__PURE__*/_jsx(ReferenceLine, {
1867
+ LineComponent: props => /*#__PURE__*/_jsx(DottedLine, _extends({}, props, {
1868
+ dashIntervals: [0, 16],
1869
+ strokeWidth: 3
1870
+ })),
1871
+ dataY: 10,
1872
+ stroke: theme.color.fg
1873
+ }), /*#__PURE__*/_jsx(Scrubber, {})]
1874
+ })
1875
+ }, {
1876
+ title: 'High/Low Price',
1877
+ component: /*#__PURE__*/_jsx(HighLowPrice, {})
1878
+ }, {
1879
+ title: 'Styling Scrubber',
1880
+ component: /*#__PURE__*/_jsx(StylingScrubber, {})
1881
+ }, {
1882
+ title: 'Compact',
1883
+ component: /*#__PURE__*/_jsx(Compact, {})
1884
+ }, {
1885
+ title: 'Asset Price With Dotted Area',
1886
+ component: /*#__PURE__*/_jsx(AssetPriceWithDottedArea, {})
1887
+ }, {
1888
+ title: 'Performance',
1889
+ component: /*#__PURE__*/_jsx(Performance, {})
1890
+ }, {
1891
+ title: 'Monotone Asset Price',
1892
+ component: /*#__PURE__*/_jsx(MonotoneAssetPrice, {})
1893
+ }, {
1894
+ title: 'Service Availability',
1895
+ component: /*#__PURE__*/_jsx(ServiceAvailability, {})
1896
+ }, {
1897
+ title: 'Forecast Asset Price',
1898
+ component: /*#__PURE__*/_jsx(ForecastAssetPrice, {})
1899
+ }, {
1900
+ title: 'In DataCard',
1901
+ component: /*#__PURE__*/_jsx(DataCardWithLineChart, {})
1902
+ }], [theme.color.fg, theme.color.fgPositive, theme.spectrum.gray50]);
1903
+ const currentExample = examples[currentIndex];
1904
+ const handlePrevious = useCallback(() => {
1905
+ setCurrentIndex(prev => (prev - 1 + examples.length) % examples.length);
1906
+ }, [examples.length]);
1907
+ const handleNext = useCallback(() => {
1908
+ setCurrentIndex(prev => (prev + 1 + examples.length) % examples.length);
1909
+ }, [examples.length]);
1910
+ return /*#__PURE__*/_jsx(ExampleScreen, {
1911
+ paddingX: 0,
1912
+ children: /*#__PURE__*/_jsxs(VStack, {
1913
+ gap: 4,
1914
+ children: [/*#__PURE__*/_jsxs(HStack, {
1915
+ alignItems: "center",
1916
+ justifyContent: "space-between",
1917
+ padding: 2,
1918
+ children: [/*#__PURE__*/_jsx(IconButton, {
1919
+ accessibilityHint: "Navigate to previous example",
1920
+ accessibilityLabel: "Previous",
1921
+ name: "arrowLeft",
1922
+ onPress: handlePrevious,
1923
+ variant: "secondary"
1924
+ }), /*#__PURE__*/_jsxs(VStack, {
1925
+ alignItems: "center",
1926
+ children: [/*#__PURE__*/_jsx(Text, {
1927
+ font: "title3",
1928
+ children: currentExample.title
1929
+ }), /*#__PURE__*/_jsxs(Text, {
1930
+ color: "fgMuted",
1931
+ font: "label1",
1932
+ children: [currentIndex + 1, " / ", examples.length]
1933
+ })]
1934
+ }), /*#__PURE__*/_jsx(IconButton, {
1935
+ accessibilityHint: "Navigate to next example",
1936
+ accessibilityLabel: "Next",
1937
+ name: "arrowRight",
1938
+ onPress: handleNext,
1939
+ variant: "secondary"
1940
+ })]
1941
+ }), /*#__PURE__*/_jsx(Box, {
1942
+ padding: 1,
1943
+ children: currentExample.component
1944
+ })]
1945
+ })
1946
+ });
1947
+ }
1948
+ export default ExampleNavigator;