@oanda/labs-crowd-view-widget 1.0.54 → 1.0.56

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 (193) hide show
  1. package/CHANGELOG.md +452 -0
  2. package/dist/main/CrowdViewWidget/components/Chart/Chart.js +42 -58
  3. package/dist/main/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  4. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js +33 -8
  5. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  6. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getDataZoomConfig.js +15 -9
  7. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getDataZoomConfig.js.map +1 -1
  8. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js +28 -14
  9. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js.map +1 -1
  10. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js +55 -15
  11. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js.map +1 -1
  12. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js +171 -0
  13. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js.map +1 -0
  14. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getSeriesCandlestickConfig.js +7 -14
  15. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getSeriesCandlestickConfig.js.map +1 -1
  16. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getSeriesHeatmapConfig.js +20 -2
  17. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getSeriesHeatmapConfig.js.map +1 -1
  18. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getSeriesSentimentConfig.js +13 -5
  19. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getSeriesSentimentConfig.js.map +1 -1
  20. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js +51 -5
  21. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js.map +1 -1
  22. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js +30 -7
  23. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js.map +1 -1
  24. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js +27 -29
  25. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js.map +1 -1
  26. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/index.js +11 -0
  27. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/index.js.map +1 -1
  28. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.js +18 -0
  29. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.js.map +1 -0
  30. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.js +6 -5
  31. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.js.map +1 -1
  32. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/getLabelData.js +1 -20
  33. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/getLabelData.js.map +1 -1
  34. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js +91 -21
  35. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js.map +1 -1
  36. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.js +59 -0
  37. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.js.map +1 -0
  38. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate.js +34 -0
  39. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate.js.map +1 -0
  40. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/index.js +44 -0
  41. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/index.js.map +1 -1
  42. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.js +11 -0
  43. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.js.map +1 -0
  44. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.js +19 -0
  45. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.js.map +1 -0
  46. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/index.js +11 -0
  47. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/index.js.map +1 -1
  48. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.js +1 -1
  49. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.js.map +1 -1
  50. package/dist/main/CrowdViewWidget/components/Chart/getOption.js +41 -23
  51. package/dist/main/CrowdViewWidget/components/Chart/getOption.js.map +1 -1
  52. package/dist/main/CrowdViewWidget/components/Chart/types.js.map +1 -1
  53. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js +7 -4
  54. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  55. package/dist/main/CrowdViewWidget/components/Chart/useResizeObserver.js +47 -0
  56. package/dist/main/CrowdViewWidget/components/Chart/useResizeObserver.js.map +1 -0
  57. package/dist/main/CrowdViewWidget/components/Legend/Legend.js +1 -1
  58. package/dist/main/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  59. package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js +11 -1
  60. package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
  61. package/dist/main/CrowdViewWidget/constants.js +31 -10
  62. package/dist/main/CrowdViewWidget/constants.js.map +1 -1
  63. package/dist/main/CrowdViewWidget/selectConfig.js +3 -9
  64. package/dist/main/CrowdViewWidget/selectConfig.js.map +1 -1
  65. package/dist/main/translations/sources/en.json +10 -8
  66. package/dist/module/CrowdViewWidget/components/Chart/Chart.js +43 -58
  67. package/dist/module/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  68. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js +32 -7
  69. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  70. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getDataZoomConfig.js +15 -9
  71. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getDataZoomConfig.js.map +1 -1
  72. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js +28 -14
  73. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js.map +1 -1
  74. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js +55 -15
  75. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js.map +1 -1
  76. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js +163 -0
  77. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js.map +1 -0
  78. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getSeriesCandlestickConfig.js +7 -14
  79. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getSeriesCandlestickConfig.js.map +1 -1
  80. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getSeriesHeatmapConfig.js +20 -2
  81. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getSeriesHeatmapConfig.js.map +1 -1
  82. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getSeriesSentimentConfig.js +13 -5
  83. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getSeriesSentimentConfig.js.map +1 -1
  84. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js +50 -5
  85. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js.map +1 -1
  86. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js +31 -8
  87. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js.map +1 -1
  88. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js +28 -30
  89. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js.map +1 -1
  90. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/index.js +1 -0
  91. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/index.js.map +1 -1
  92. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.js +11 -0
  93. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.js.map +1 -0
  94. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.js +6 -5
  95. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.js.map +1 -1
  96. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/getLabelData.js +1 -20
  97. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/getLabelData.js.map +1 -1
  98. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js +91 -21
  99. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js.map +1 -1
  100. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.js +52 -0
  101. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.js.map +1 -0
  102. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate.js +27 -0
  103. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate.js.map +1 -0
  104. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/index.js +4 -0
  105. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/index.js.map +1 -1
  106. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.js +4 -0
  107. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.js.map +1 -0
  108. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.js +12 -0
  109. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.js.map +1 -0
  110. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/index.js +1 -0
  111. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/index.js.map +1 -1
  112. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.js +1 -1
  113. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.js.map +1 -1
  114. package/dist/module/CrowdViewWidget/components/Chart/getOption.js +42 -24
  115. package/dist/module/CrowdViewWidget/components/Chart/getOption.js.map +1 -1
  116. package/dist/module/CrowdViewWidget/components/Chart/types.js.map +1 -1
  117. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js +8 -5
  118. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  119. package/dist/module/CrowdViewWidget/components/Chart/useResizeObserver.js +40 -0
  120. package/dist/module/CrowdViewWidget/components/Chart/useResizeObserver.js.map +1 -0
  121. package/dist/module/CrowdViewWidget/components/Legend/Legend.js +1 -1
  122. package/dist/module/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  123. package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js +11 -1
  124. package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
  125. package/dist/module/CrowdViewWidget/constants.js +31 -10
  126. package/dist/module/CrowdViewWidget/constants.js.map +1 -1
  127. package/dist/module/CrowdViewWidget/selectConfig.js +3 -9
  128. package/dist/module/CrowdViewWidget/selectConfig.js.map +1 -1
  129. package/dist/module/translations/sources/en.json +10 -8
  130. package/dist/types/CrowdViewWidget/components/Chart/Chart.d.ts +1 -1
  131. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getDataZoomConfig.d.ts +5 -1
  132. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getGridLines.d.ts +2 -1
  133. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.d.ts +8 -0
  134. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getSeriesCandlestickConfig.d.ts +2 -3
  135. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getSeriesHeatmapConfig.d.ts +2 -1
  136. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getSeriesSentimentConfig.d.ts +2 -1
  137. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.d.ts +6 -1
  138. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.d.ts +2 -1
  139. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.d.ts +1 -2
  140. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/index.d.ts +1 -0
  141. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.d.ts +1 -0
  142. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.d.ts +1 -1
  143. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/getLabelData.d.ts +1 -16
  144. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.d.ts +6 -1
  145. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.d.ts +4 -0
  146. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate.d.ts +3 -0
  147. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/index.d.ts +4 -0
  148. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.d.ts +1 -0
  149. package/dist/types/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.d.ts +2 -0
  150. package/dist/types/CrowdViewWidget/components/Chart/dataUtils/index.d.ts +1 -0
  151. package/dist/types/CrowdViewWidget/components/Chart/types.d.ts +6 -0
  152. package/dist/types/CrowdViewWidget/components/Chart/useResizeObserver.d.ts +5 -0
  153. package/dist/types/CrowdViewWidget/constants.d.ts +28 -15
  154. package/package.json +3 -3
  155. package/src/CrowdViewWidget/components/Chart/Chart.tsx +78 -80
  156. package/src/CrowdViewWidget/components/Chart/ChartWithData.tsx +51 -14
  157. package/src/CrowdViewWidget/components/Chart/chartOptions/getDataZoomConfig.ts +11 -2
  158. package/src/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.ts +34 -19
  159. package/src/CrowdViewWidget/components/Chart/chartOptions/getGridLines.ts +71 -8
  160. package/src/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.ts +207 -0
  161. package/src/CrowdViewWidget/components/Chart/chartOptions/getSeriesCandlestickConfig.ts +7 -15
  162. package/src/CrowdViewWidget/components/Chart/chartOptions/getSeriesHeatmapConfig.ts +22 -1
  163. package/src/CrowdViewWidget/components/Chart/chartOptions/getSeriesSentimentConfig.ts +13 -4
  164. package/src/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.ts +91 -37
  165. package/src/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.ts +62 -30
  166. package/src/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.ts +22 -33
  167. package/src/CrowdViewWidget/components/Chart/chartOptions/index.ts +1 -0
  168. package/src/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.ts +14 -0
  169. package/src/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.ts +6 -4
  170. package/src/CrowdViewWidget/components/Chart/chartUtils/getLabelData.ts +11 -35
  171. package/src/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.ts +157 -31
  172. package/src/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.ts +80 -0
  173. package/src/CrowdViewWidget/components/Chart/chartUtils/handleTooltipUpdate.ts +37 -0
  174. package/src/CrowdViewWidget/components/Chart/chartUtils/index.ts +4 -0
  175. package/src/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.ts +3 -0
  176. package/src/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.ts +13 -0
  177. package/src/CrowdViewWidget/components/Chart/dataUtils/index.ts +1 -0
  178. package/src/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.ts +1 -1
  179. package/src/CrowdViewWidget/components/Chart/getOption.ts +47 -17
  180. package/src/CrowdViewWidget/components/Chart/types.ts +6 -0
  181. package/src/CrowdViewWidget/components/Chart/useCrowdViewData.ts +12 -5
  182. package/src/CrowdViewWidget/components/Chart/useResizeObserver.ts +51 -0
  183. package/src/CrowdViewWidget/components/Legend/Legend.tsx +1 -1
  184. package/src/CrowdViewWidget/components/Legend/LegendBar.tsx +18 -1
  185. package/src/CrowdViewWidget/constants.ts +34 -7
  186. package/src/CrowdViewWidget/selectConfig.ts +4 -12
  187. package/src/translations/sources/en.json +10 -8
  188. package/test/components/Chart/utils/chartUtils.test.ts +29 -6
  189. package/test/components/Chart/utils/handleLabelUpdate.test.ts +435 -0
  190. package/test/components/Chart/utils/handleTooltipUpdate.test.ts +140 -0
  191. package/test/components/Legend.test.tsx +3 -3
  192. package/test/components/LegendBar.test.tsx +22 -20
  193. package/test/utils/processPriceCandles.test.ts +4 -4
@@ -1,17 +1,28 @@
1
+ import { colorPalette } from '@oanda/labs-widget-common';
2
+
1
3
  import { BookType } from '../../../../gql/types/graphql';
2
4
  import type { Bucket, TooltipParam } from '../types';
3
5
 
4
6
  const DATE_FORMAT_OPTIONS: Intl.DateTimeFormatOptions = {
5
- hour: '2-digit',
6
- minute: '2-digit',
7
- year: 'numeric',
8
7
  day: 'numeric',
9
8
  month: 'numeric',
10
- timeZoneName: 'short',
9
+ year: 'numeric',
10
+
11
+ hour: 'numeric',
12
+ minute: '2-digit',
13
+ hour12: false,
14
+
15
+ timeZoneName: 'shortOffset',
11
16
  };
12
17
 
13
18
  const SENTIMENT_DISPLAY_PRECISION = 2;
14
19
 
20
+ const TOOLTIP_STYLES = {
21
+ MOBILE_COLUMN_WIDTH: '33.3%',
22
+ PADDING_BOTTOM: '4px',
23
+ FULL_WIDTH: '100%',
24
+ } as const;
25
+
15
26
  const calculateBucketDisplayPrecision = (bucketWidth: number): number => {
16
27
  return bucketWidth.toString().split('.')[1]?.length || 0;
17
28
  };
@@ -26,6 +37,22 @@ const findSeriesParam = <T extends TooltipParam['seriesId']>(
26
37
  );
27
38
  };
28
39
 
40
+ const findSentimentSeriesParam = (
41
+ params: TooltipParam[],
42
+ sentimentLongs: (number | null)[],
43
+ sentimentShorts: (number | null)[]
44
+ ): {
45
+ sentimentLong: number | null;
46
+ sentimentShort: number | null;
47
+ } => {
48
+ const index = params[0].dataIndex as number;
49
+
50
+ return {
51
+ sentimentLong: sentimentLongs[index] ?? null,
52
+ sentimentShort: sentimentShorts[index] ?? null,
53
+ };
54
+ };
55
+
29
56
  const getSentimentOverbalanceLabel = (
30
57
  sentiment: number,
31
58
  bookType: BookType
@@ -43,11 +70,17 @@ const formatCandleData = (
43
70
  labelCallback: (key: string) => string
44
71
  ): string => {
45
72
  const [, open, close, low, high] = candleData;
46
- return `<p><b>${labelCallback('candle')}:</b></p>
47
- <p>${labelCallback('open_price')}: ${open} </p>
48
- <p>${labelCallback('close_price')}: ${close} </p>
49
- <p>${labelCallback('low')}: ${low} </p>
50
- <p>${labelCallback('high')}: ${high} </p>
73
+ const candleLabel = labelCallback('candle');
74
+ const openLabel = labelCallback('open');
75
+ const closeLabel = labelCallback('close');
76
+ const lowLabel = labelCallback('low');
77
+ const highLabel = labelCallback('high');
78
+
79
+ return `<div><b>${candleLabel}:</b></div>
80
+ <div>${openLabel}: ${open} </div>
81
+ <div>${closeLabel}: ${close} </div>
82
+ <div>${lowLabel}: ${low} </div>
83
+ <div>${highLabel}: ${high} </div>
51
84
  `;
52
85
  };
53
86
 
@@ -57,14 +90,16 @@ const formatBookData = ({
57
90
  bucketDisplayPrecision,
58
91
  bookType,
59
92
  labelCallback,
93
+ isDesktop,
60
94
  }: {
61
95
  matchedBucket: Bucket;
62
96
  bucketWidth: number;
63
97
  bucketDisplayPrecision: number;
64
98
  bookType: BookType;
65
99
  labelCallback: (key: string) => string;
100
+ isDesktop: boolean;
66
101
  }): string => {
67
- const bookLabelKey = bookType === BookType.Order ? 'orders' : 'positions';
102
+ const bookLabelKey = bookType === BookType.Order ? 'orders' : 'trades';
68
103
  const priceRangeStart = matchedBucket.price.toFixed(bucketDisplayPrecision);
69
104
  const priceRangeEnd = (matchedBucket.price + bucketWidth).toFixed(
70
105
  bucketDisplayPrecision
@@ -73,32 +108,39 @@ const formatBookData = ({
73
108
  matchedBucket.sentiment,
74
109
  bookType
75
110
  );
76
- const sentimentValue = Math.abs(matchedBucket.sentiment).toFixed(
77
- bucketDisplayPrecision
78
- );
111
+ const sentimentValue = Math.abs(matchedBucket.sentiment);
79
112
 
80
- return `<br /><p><b>${labelCallback(bookLabelKey)}:</b></p>
81
- <p>${labelCallback('price_range')}: ${priceRangeStart} - ${priceRangeEnd} </p>
82
- <p>${labelCallback(sentimentLabel)}: ${sentimentValue}% </p>`;
113
+ return isDesktop
114
+ ? `<div><b>${labelCallback(bookLabelKey)}:</b></div>
115
+ <div>${labelCallback('price_range')}: ${priceRangeStart} - ${priceRangeEnd} </div>
116
+ <div>${labelCallback(sentimentLabel)}: ${sentimentValue}% </div>`
117
+ : `<div><b>${labelCallback(bookLabelKey)}:</b></div>
118
+ <div>${labelCallback('price_range')}:</div>
119
+ <div>${priceRangeStart} - ${priceRangeEnd} </div>
120
+ <div>${labelCallback(sentimentLabel)}:</div>
121
+ <div>${sentimentValue}% </div>`;
83
122
  };
84
123
 
85
124
  const formatSentimentData = (
86
- sentimentParam: Extract<TooltipParam, { seriesId: 'sentiment' }>,
125
+ sentimentLong: number,
126
+ sentimentShort: number,
87
127
  labelCallback: (key: string) => string
88
128
  ): string => {
89
- const [, shortPercent, longPercent] = sentimentParam.value;
90
- return `<br /><p><b>${labelCallback('sentiment')}:</b></p>
91
- <p>${labelCallback('long')}: ${longPercent.toFixed(SENTIMENT_DISPLAY_PRECISION)}% </p>
92
- <p>${labelCallback('short')}: ${shortPercent.toFixed(SENTIMENT_DISPLAY_PRECISION)}% </p>`;
129
+ return `<div><b>${labelCallback('sentiment')}:</b></div>
130
+ <div>${labelCallback('long')}: ${sentimentLong.toFixed(SENTIMENT_DISPLAY_PRECISION)}% </div>
131
+ <div>${labelCallback('short')}: ${sentimentShort.toFixed(SENTIMENT_DISPLAY_PRECISION)}% </div>`;
93
132
  };
94
133
 
95
134
  const hasValidCandleData = (
96
135
  candleData: [number, number, number, number, number]
97
136
  ): boolean =>
98
- !!candleData[1] && !!candleData[2] && !!candleData[3] && !!candleData[3];
137
+ !!candleData[1] && !!candleData[2] && !!candleData[3] && !!candleData[4];
138
+
99
139
  const hasValidSentimentData = (
100
- sentimentParam: Extract<TooltipParam, { seriesId: 'sentiment' }> | undefined
101
- ): boolean => !!sentimentParam?.value[1] && !!sentimentParam?.value[2];
140
+ sentimentLong: number | null,
141
+ sentimentShort: number | null
142
+ ): boolean => sentimentLong !== null && sentimentShort !== null;
143
+
102
144
  const findMatchingBucket = (
103
145
  buckets: Bucket[][],
104
146
  bucketsIndex: number | undefined,
@@ -121,6 +163,11 @@ export const getTooltipFormatter = ({
121
163
  selectedPrice,
122
164
  bookType,
123
165
  labelCallback,
166
+ sentimentLongs,
167
+ sentimentShorts,
168
+ isDesktop,
169
+ locale,
170
+ isDark,
124
171
  }: {
125
172
  params: TooltipParam[];
126
173
  buckets: Bucket[][];
@@ -128,6 +175,11 @@ export const getTooltipFormatter = ({
128
175
  selectedPrice: number;
129
176
  bookType: BookType;
130
177
  labelCallback: (key: string) => string;
178
+ sentimentLongs: (number | null)[];
179
+ sentimentShorts: (number | null)[];
180
+ isDesktop: boolean;
181
+ locale: string;
182
+ isDark: boolean;
131
183
  }) => {
132
184
  if (!params || !Array.isArray(params) || params.length === 0) {
133
185
  return '';
@@ -135,7 +187,11 @@ export const getTooltipFormatter = ({
135
187
 
136
188
  const candleParam = findSeriesParam(params, 'candlestick');
137
189
  const booksParam = findSeriesParam(params, 'heatmap');
138
- const sentimentParam = findSeriesParam(params, 'sentiment');
190
+ const { sentimentLong, sentimentShort } = findSentimentSeriesParam(
191
+ params,
192
+ sentimentLongs,
193
+ sentimentShorts
194
+ );
139
195
 
140
196
  if (!candleParam) {
141
197
  return '';
@@ -152,16 +208,19 @@ export const getTooltipFormatter = ({
152
208
  bucketWidth
153
209
  );
154
210
  const showCandles = hasValidCandleData(candleData);
155
- const showSentiment = hasValidSentimentData(sentimentParam);
211
+ const showSentiment = hasValidSentimentData(sentimentLong, sentimentShort);
156
212
 
157
213
  if (!showCandles && !matchedBucket) {
158
214
  return '';
159
215
  }
160
216
 
161
- const timeFormatted = time.toLocaleString(undefined, DATE_FORMAT_OPTIONS);
217
+ const timeFormatted = time
218
+ .toLocaleString(locale, DATE_FORMAT_OPTIONS)
219
+ .replace(/\//g, '.');
162
220
  const candleSection = showCandles
163
221
  ? formatCandleData(candleData, labelCallback)
164
222
  : '';
223
+
165
224
  const bookSection = matchedBucket
166
225
  ? formatBookData({
167
226
  matchedBucket,
@@ -169,15 +228,82 @@ export const getTooltipFormatter = ({
169
228
  bucketDisplayPrecision,
170
229
  bookType,
171
230
  labelCallback,
231
+ isDesktop,
172
232
  })
173
233
  : '';
234
+
174
235
  const sentimentSection =
175
- showSentiment && sentimentParam
176
- ? formatSentimentData(sentimentParam, labelCallback)
236
+ showSentiment && sentimentLong && sentimentShort
237
+ ? formatSentimentData(sentimentLong, sentimentShort, labelCallback)
177
238
  : '';
178
239
 
179
- return `<p>${timeFormatted}</p><br />
240
+ if (isDesktop) {
241
+ return buildDesktopTooltip({
242
+ timeFormatted,
243
+ candleSection,
244
+ sentimentSection,
245
+ bookSection,
246
+ hasMatchedBucket: !!matchedBucket,
247
+ });
248
+ }
249
+
250
+ return buildMobileTooltip({
251
+ timeFormatted,
252
+ candleSection,
253
+ sentimentSection,
254
+ bookSection,
255
+ isDark,
256
+ });
257
+ };
258
+
259
+ interface TooltipSections {
260
+ timeFormatted: string;
261
+ candleSection: string;
262
+ sentimentSection: string;
263
+ bookSection: string;
264
+ }
265
+
266
+ const buildDesktopTooltip = ({
267
+ timeFormatted,
268
+ candleSection,
269
+ sentimentSection,
270
+ bookSection,
271
+ hasMatchedBucket,
272
+ }: TooltipSections & { hasMatchedBucket: boolean }): string => {
273
+ const padding = TOOLTIP_STYLES.PADDING_BOTTOM;
274
+ const sentimentPadding = hasMatchedBucket ? padding : '0px';
275
+
276
+ return `<div style="padding-bottom: ${padding};">${timeFormatted}</div>
277
+ <div style="padding-bottom: ${padding};">${candleSection}</div>
278
+ <div style="padding-bottom: ${sentimentPadding};">${sentimentSection}</div>
279
+ <div>${bookSection}</div>
280
+ `;
281
+ };
282
+
283
+ const buildMobileTooltip = ({
284
+ timeFormatted,
285
+ candleSection,
286
+ sentimentSection,
287
+ bookSection,
288
+ isDark,
289
+ }: TooltipSections & { isDark: boolean }): string => {
290
+ const backgroundColor = isDark ? colorPalette.black : colorPalette.white;
291
+ const columnWidth = TOOLTIP_STYLES.MOBILE_COLUMN_WIDTH;
292
+ const fullWidth = TOOLTIP_STYLES.FULL_WIDTH;
293
+ const padding = TOOLTIP_STYLES.PADDING_BOTTOM;
294
+
295
+ return `<div style="width: ${fullWidth}; background-color: ${backgroundColor};">
296
+ <div style="padding-bottom: ${padding};">${timeFormatted}</div>
297
+ <div style="width: ${fullWidth}; display: flex; justify-content: space-between;">
298
+ <div style="width: ${columnWidth};">
180
299
  ${candleSection}
300
+ </div>
301
+ <div style="width: ${columnWidth};">
302
+ ${sentimentSection}
303
+ </div>
304
+ <div style="width: ${columnWidth};">
181
305
  ${bookSection}
182
- ${sentimentSection}`;
306
+ </div>
307
+ </div>
308
+ </div>`;
183
309
  };
@@ -0,0 +1,80 @@
1
+ import type { EChartsType } from 'echarts';
2
+ import type { MutableRefObject } from 'react';
3
+
4
+ import type { ChartProps, DataZoomArray } from '../types';
5
+ import { formatXAxisAdditionalLabel } from './formatXAxisAdditionalLabel';
6
+ import { formatXAxisLabel } from './formatXAxisLabel';
7
+ import { getLabelData } from './getLabelData';
8
+ import { isDifferenceGreaterThanTwoWeeks } from './isDifferenceGreaterThanTwoWeeks';
9
+
10
+ export const handleLabelUpdate = (
11
+ instance: EChartsType,
12
+ mainData: ChartProps['mainData'],
13
+ labelTimerRef: MutableRefObject<NodeJS.Timeout | null>,
14
+ isGreaterThanTwoWeeksRef: MutableRefObject<boolean | null>,
15
+ locale: string
16
+ ): void => {
17
+ if (labelTimerRef.current) {
18
+ clearTimeout(labelTimerRef.current);
19
+ }
20
+ labelTimerRef.current = setTimeout(() => {
21
+ const { dates } = mainData;
22
+
23
+ const { dataZoom } = instance.getOption();
24
+ if (
25
+ !Array.isArray(dataZoom) ||
26
+ dataZoom.length === 0 ||
27
+ !dataZoom[0] ||
28
+ typeof dataZoom[0].startValue !== 'number' ||
29
+ typeof dataZoom[0].endValue !== 'number'
30
+ ) {
31
+ return;
32
+ }
33
+
34
+ const { startValue, endValue } = dataZoom[0] as DataZoomArray[0];
35
+
36
+ if (!dates[startValue] || !dates[endValue]) {
37
+ return;
38
+ }
39
+
40
+ const isGreaterThanTwoWeeks = isDifferenceGreaterThanTwoWeeks(
41
+ dates[startValue],
42
+ dates[endValue]
43
+ );
44
+
45
+ const wasGreaterThanTwoWeeks = isGreaterThanTwoWeeksRef.current ?? false;
46
+
47
+ if (isGreaterThanTwoWeeks !== wasGreaterThanTwoWeeks) {
48
+ const labelsData = getLabelData({
49
+ dates,
50
+ isGreaterThanTwoWeeks,
51
+ });
52
+
53
+ instance.setOption({
54
+ xAxis: [
55
+ {
56
+ id: 'main-xAxis',
57
+ axisLabel: {
58
+ formatter: (value: string) =>
59
+ formatXAxisLabel(value, isGreaterThanTwoWeeks, locale),
60
+ },
61
+ },
62
+ {
63
+ id: 'additional-xAxis',
64
+ axisLabel: {
65
+ customValues: labelsData,
66
+ formatter: (value: unknown) =>
67
+ formatXAxisAdditionalLabel(
68
+ value,
69
+ isGreaterThanTwoWeeks,
70
+ locale
71
+ ),
72
+ },
73
+ },
74
+ ],
75
+ });
76
+
77
+ isGreaterThanTwoWeeksRef.current = isGreaterThanTwoWeeks;
78
+ }
79
+ }, 50);
80
+ };
@@ -0,0 +1,37 @@
1
+ import type { EChartsType } from 'echarts';
2
+ import type { MutableRefObject } from 'react';
3
+
4
+ export const handleTooltipUpdate = (
5
+ instance: EChartsType,
6
+ zoomTimerRef: MutableRefObject<NodeJS.Timeout | null>,
7
+ tooltipVisibleRef: MutableRefObject<boolean>,
8
+ canHover: boolean
9
+ ): void => {
10
+ if (!canHover) {
11
+ return;
12
+ }
13
+
14
+ if (tooltipVisibleRef.current) {
15
+ instance.setOption({
16
+ tooltip: {
17
+ show: false,
18
+ },
19
+ });
20
+ tooltipVisibleRef.current = false;
21
+ }
22
+
23
+ if (zoomTimerRef.current) {
24
+ clearTimeout(zoomTimerRef.current);
25
+ }
26
+
27
+ zoomTimerRef.current = setTimeout(() => {
28
+ if (!tooltipVisibleRef.current) {
29
+ instance.setOption({
30
+ tooltip: {
31
+ show: true,
32
+ },
33
+ });
34
+ tooltipVisibleRef.current = true;
35
+ }
36
+ }, 100);
37
+ };
@@ -1,7 +1,11 @@
1
+ export * from './formatXAxisAdditionalLabel';
1
2
  export * from './formatXAxisLabel';
2
3
  export * from './getChartStyles';
3
4
  export * from './getGradientColor';
4
5
  export * from './getLabelData';
5
6
  export * from './getRectColor';
6
7
  export * from './getTooltipFormatter';
8
+ export * from './handleLabelUpdate';
9
+ export * from './handleTooltipUpdate';
7
10
  export * from './isDifferenceGreaterThanTwoWeeks';
11
+ export * from './normalizeLocale';
@@ -0,0 +1,3 @@
1
+ export const normalizeLocale = (locale: string): string => {
2
+ return locale === 'en' ? 'en-GB' : locale;
3
+ };
@@ -0,0 +1,13 @@
1
+ import { Granularity } from '../../../../gql/types/graphql';
2
+ import { BUCKET_CONFIG } from '../../../constants';
3
+
4
+ export const getMultiplayerForTimeSpan = (granularity: Granularity): number => {
5
+ const timeSpanMultiplierMap: Record<Granularity, number> = {
6
+ [Granularity.M5]: BUCKET_CONFIG.MULTIPLIER_5M,
7
+ [Granularity.M15]: BUCKET_CONFIG.MULTIPLIER_15M,
8
+ [Granularity.H1]: BUCKET_CONFIG.MULTIPLIER_1H,
9
+ [Granularity.H4]: BUCKET_CONFIG.MULTIPLIER_4H,
10
+ };
11
+
12
+ return timeSpanMultiplierMap[granularity] || BUCKET_CONFIG.MULTIPLIER_5M;
13
+ };
@@ -1,4 +1,5 @@
1
1
  export * from './getInstrumentConfigForDivision';
2
+ export * from './getMultiplayerForTimeSpan';
2
3
  export * from './getTimeSpanForGranularity';
3
4
  export * from './processOrderPositionBooks';
4
5
  export * from './processPriceCandles';
@@ -60,7 +60,7 @@ export const processPriceCandles = (
60
60
  candlesClose,
61
61
  candlesLow,
62
62
  candlesHigh,
63
- dates,
63
+ dates: [...dates, new Date().toISOString()],
64
64
  hasValidCandles: true,
65
65
  pipsLocation: priceCandlesData.priceCandles.pipsLocation,
66
66
  };
@@ -3,6 +3,7 @@ import {
3
3
  getDataZoomConfig,
4
4
  getGridConfig,
5
5
  getGridLines,
6
+ getLabelsConfig,
6
7
  getSeriesCandlestickConfig,
7
8
  getSeriesHeatmapConfig,
8
9
  getSeriesSentimentConfig,
@@ -35,15 +36,21 @@ export const getOption: GetOptionType = ({
35
36
  sentimentThresholdMin,
36
37
  sentimentThresholdMax,
37
38
  },
39
+ isLoading,
38
40
  isDark,
39
41
  isDesktop,
40
42
  labelCallback,
43
+ locale,
41
44
  }) => {
42
45
  const styles = getChartStyles(isDark);
43
46
  const selectedPriceRef = { current: 0 };
44
47
 
48
+ const initialStartZoom = isDesktop
49
+ ? CHART_CONFIG.INITIAL_START_ZOOM_DESKTOP
50
+ : CHART_CONFIG.INITIAL_START_ZOOM_MOBILE;
51
+
45
52
  const visibleXAxisData = dates.slice(
46
- (dates.length * CHART_CONFIG.INITIAL_START_ZOOM) / 100,
53
+ (dates.length * initialStartZoom) / 100,
47
54
  (dates.length * CHART_CONFIG.INITIAL_END_ZOOM) / 100
48
55
  );
49
56
 
@@ -52,6 +59,30 @@ export const getOption: GetOptionType = ({
52
59
  visibleXAxisData[visibleXAxisData.length - 1]
53
60
  );
54
61
 
62
+ const heatmapChart =
63
+ bookPrices && bookPrices.length > 0
64
+ ? [
65
+ getSeriesHeatmapConfig({
66
+ bucketWidth,
67
+ buckets,
68
+ isDark,
69
+ sentimentThresholdMax,
70
+ sentimentThresholdMin,
71
+ isLoading,
72
+ }),
73
+ ]
74
+ : [];
75
+
76
+ const sentimentChart =
77
+ sentimentLongs && sentimentLongs.length > 0
78
+ ? [
79
+ getSeriesSentimentConfig({
80
+ styles,
81
+ isLoading,
82
+ }),
83
+ ]
84
+ : [];
85
+
55
86
  return {
56
87
  animation: false,
57
88
  tooltip: getTooltipConfig({
@@ -62,24 +93,32 @@ export const getOption: GetOptionType = ({
62
93
  labelCallback,
63
94
  selectedPriceRef,
64
95
  tooltipLinesColor: styles.tooltipLinesColor,
96
+ sentimentLongs,
97
+ sentimentShorts,
98
+ isDesktop,
99
+ isDark,
100
+ locale,
65
101
  }),
66
102
  grid: getGridConfig({ isDesktop }),
67
103
  xAxis: getXAxisConfig({
68
104
  dates,
69
105
  isGreaterThanTwoWeeks,
106
+ locale,
70
107
  }),
71
108
  yAxis: getYAxisConfig({
109
+ isDesktop,
72
110
  bucketWidth,
73
111
  displayPrecision,
74
- isDesktop,
75
- labelCallback,
76
112
  }),
77
- dataZoom: getDataZoomConfig(),
113
+ dataZoom: getDataZoomConfig({ isDesktop }),
78
114
  visualMap: getVisualMapConfig({
79
115
  sentimentLongColor: styles.sentimentLongColor,
80
116
  sentimentShortColor: styles.sentimentShortColor,
81
117
  }),
82
- graphic: getGridLines({ isDark }),
118
+ graphic: [
119
+ ...getGridLines({ isDark, isDesktop }),
120
+ ...getLabelsConfig({ isDark, isDesktop, labelCallback }),
121
+ ],
83
122
  dataset: {
84
123
  source: {
85
124
  dates,
@@ -95,20 +134,11 @@ export const getOption: GetOptionType = ({
95
134
  },
96
135
  series: [
97
136
  getSeriesCandlestickConfig({
98
- dates,
99
- isGreaterThanTwoWeeks,
100
- styles,
101
- }),
102
- getSeriesHeatmapConfig({
103
- bucketWidth,
104
- buckets,
105
- isDark,
106
- sentimentThresholdMax,
107
- sentimentThresholdMin,
108
- }),
109
- getSeriesSentimentConfig({
110
137
  styles,
138
+ isLoading,
111
139
  }),
140
+ ...heatmapChart,
141
+ ...sentimentChart,
112
142
  ],
113
143
  };
114
144
  };
@@ -44,6 +44,9 @@ export interface UseCrowdViewDataReturn {
44
44
  mainData?: CrowdViewMainData | null;
45
45
  additionalData?: CrowdViewAdditionalData | null;
46
46
  loading: boolean;
47
+ priceCandlesLoading: boolean;
48
+ orderPositionLoading: boolean;
49
+ sentimentsLoading: boolean;
47
50
  error: boolean;
48
51
  }
49
52
 
@@ -52,7 +55,9 @@ interface GetOptionProps {
52
55
  additionalData: CrowdViewAdditionalData;
53
56
  isDark: boolean;
54
57
  isDesktop: boolean;
58
+ isLoading: boolean;
55
59
  labelCallback: (key: string, params?: Record<string, unknown>) => string;
60
+ locale: string;
56
61
  }
57
62
 
58
63
  export type GetOptionType = (props: GetOptionProps) => EChartsOption;
@@ -61,6 +66,7 @@ export interface ChartProps {
61
66
  mainData: CrowdViewMainData;
62
67
  additionalData: CrowdViewAdditionalData;
63
68
  isDesktop: boolean;
69
+ isLoading: boolean;
64
70
  }
65
71
 
66
72
  export interface ChartWithDataProps {
@@ -15,6 +15,7 @@ import type {
15
15
  import { BookType, DataSource, Division } from '../../../gql/types/graphql';
16
16
  import { BUCKET_CONFIG, INSTRUMENTS_CONFIG } from '../../constants';
17
17
  import {
18
+ getMultiplayerForTimeSpan,
18
19
  getTimeSpanForGranularity,
19
20
  processOrderPositionBooks,
20
21
  processPriceCandles,
@@ -83,7 +84,7 @@ export const useCrowdViewData = ({
83
84
  granularity,
84
85
  maxBookPrice: maxPrice,
85
86
  minBookPrice: minPrice,
86
- bucketMultiplier: 2,
87
+ bucketMultiplier: getMultiplayerForTimeSpan(granularity),
87
88
  bucketMargin: BUCKET_CONFIG.PRICE_MARGIN_MULTIPLIER,
88
89
  },
89
90
  fetchPolicy: 'no-cache',
@@ -161,7 +162,7 @@ export const useCrowdViewData = ({
161
162
  ]);
162
163
 
163
164
  const data = useMemo(() => {
164
- if (!priceCandlesData || !orderPositionData || !sentimentsData || error) {
165
+ if (!priceCandlesData || error) {
165
166
  return null;
166
167
  }
167
168
 
@@ -178,7 +179,10 @@ export const useCrowdViewData = ({
178
179
  sentimentLongs,
179
180
  },
180
181
  additionalData: {
181
- bucketWidth,
182
+ bucketWidth:
183
+ bucketWidth ||
184
+ INSTRUMENTS_CONFIG[instrument].defaultBucketWidth *
185
+ getMultiplayerForTimeSpan(granularity),
182
186
  buckets,
183
187
  displayPrecision: pipsLocation,
184
188
  bookType,
@@ -188,8 +192,6 @@ export const useCrowdViewData = ({
188
192
  };
189
193
  }, [
190
194
  priceCandlesData,
191
- orderPositionData,
192
- sentimentsData,
193
195
  error,
194
196
  dates,
195
197
  candlesOpen,
@@ -201,6 +203,8 @@ export const useCrowdViewData = ({
201
203
  sentimentShorts,
202
204
  sentimentLongs,
203
205
  bucketWidth,
206
+ granularity,
207
+ instrument,
204
208
  buckets,
205
209
  pipsLocation,
206
210
  bookType,
@@ -211,6 +215,9 @@ export const useCrowdViewData = ({
211
215
  return {
212
216
  mainData: data?.mainData,
213
217
  additionalData: data?.additionalData,
218
+ priceCandlesLoading,
219
+ orderPositionLoading,
220
+ sentimentsLoading,
214
221
  loading,
215
222
  error: !!error,
216
223
  };