@oanda/labs-crowd-view-widget 1.0.55 → 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.
- package/CHANGELOG.md +228 -0
- package/dist/main/CrowdViewWidget/components/Chart/Chart.js +8 -5
- package/dist/main/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js +15 -3
- package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js +28 -14
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js +19 -7
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js +20 -8
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js +44 -5
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js +14 -6
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js +11 -4
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.js +2 -2
- package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.js +6 -5
- package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js +72 -14
- package/dist/main/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.js +3 -3
- package/dist/main/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartUtils/index.js +11 -0
- package/dist/main/CrowdViewWidget/components/Chart/chartUtils/index.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.js +11 -0
- package/dist/main/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.js +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/getOption.js +9 -3
- package/dist/main/CrowdViewWidget/components/Chart/getOption.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/types.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/useResizeObserver.js +47 -0
- package/dist/main/CrowdViewWidget/components/Chart/useResizeObserver.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js +1 -1
- package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
- package/dist/main/CrowdViewWidget/constants.js +20 -7
- package/dist/main/CrowdViewWidget/constants.js.map +1 -1
- package/dist/main/CrowdViewWidget/selectConfig.js +3 -3
- package/dist/main/CrowdViewWidget/selectConfig.js.map +1 -1
- package/dist/main/translations/sources/en.json +10 -9
- package/dist/module/CrowdViewWidget/components/Chart/Chart.js +9 -6
- package/dist/module/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js +15 -3
- package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js +28 -14
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js +19 -7
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js +20 -8
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js +43 -5
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js +14 -6
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js +12 -5
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.js +2 -2
- package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.js +6 -5
- package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js +72 -14
- package/dist/module/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.js +3 -3
- package/dist/module/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartUtils/index.js +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/chartUtils/index.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.js +4 -0
- package/dist/module/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.js +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/getOption.js +9 -3
- package/dist/module/CrowdViewWidget/components/Chart/getOption.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/types.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/useResizeObserver.js +40 -0
- package/dist/module/CrowdViewWidget/components/Chart/useResizeObserver.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js +1 -1
- package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
- package/dist/module/CrowdViewWidget/constants.js +20 -7
- package/dist/module/CrowdViewWidget/constants.js.map +1 -1
- package/dist/module/CrowdViewWidget/selectConfig.js +3 -3
- package/dist/module/CrowdViewWidget/selectConfig.js.map +1 -1
- package/dist/module/translations/sources/en.json +10 -9
- package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.d.ts +4 -1
- package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.d.ts +2 -1
- package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.d.ts +2 -1
- package/dist/types/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.d.ts +1 -1
- package/dist/types/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.d.ts +1 -1
- package/dist/types/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.d.ts +4 -1
- package/dist/types/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.d.ts +1 -1
- package/dist/types/CrowdViewWidget/components/Chart/chartUtils/index.d.ts +1 -0
- package/dist/types/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.d.ts +1 -0
- package/dist/types/CrowdViewWidget/components/Chart/types.d.ts +1 -0
- package/dist/types/CrowdViewWidget/components/Chart/useResizeObserver.d.ts +5 -0
- package/dist/types/CrowdViewWidget/constants.d.ts +24 -19
- package/package.json +3 -3
- package/src/CrowdViewWidget/components/Chart/Chart.tsx +12 -5
- package/src/CrowdViewWidget/components/Chart/ChartWithData.tsx +22 -3
- package/src/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.ts +34 -19
- package/src/CrowdViewWidget/components/Chart/chartOptions/getGridLines.ts +27 -6
- package/src/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.ts +26 -10
- package/src/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.ts +86 -43
- package/src/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.ts +12 -5
- package/src/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.ts +12 -6
- package/src/CrowdViewWidget/components/Chart/chartUtils/formatXAxisAdditionalLabel.ts +3 -2
- package/src/CrowdViewWidget/components/Chart/chartUtils/formatXAxisLabel.ts +6 -4
- package/src/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.ts +119 -22
- package/src/CrowdViewWidget/components/Chart/chartUtils/handleLabelUpdate.ts +8 -3
- package/src/CrowdViewWidget/components/Chart/chartUtils/index.ts +1 -0
- package/src/CrowdViewWidget/components/Chart/chartUtils/normalizeLocale.ts +3 -0
- package/src/CrowdViewWidget/components/Chart/dataUtils/processPriceCandles.ts +1 -1
- package/src/CrowdViewWidget/components/Chart/getOption.ts +6 -0
- package/src/CrowdViewWidget/components/Chart/types.ts +1 -0
- package/src/CrowdViewWidget/components/Chart/useResizeObserver.ts +51 -0
- package/src/CrowdViewWidget/components/Legend/LegendBar.tsx +3 -2
- package/src/CrowdViewWidget/constants.ts +24 -5
- package/src/CrowdViewWidget/selectConfig.ts +4 -4
- package/src/translations/sources/en.json +10 -9
- package/test/components/Chart/utils/chartUtils.test.ts +16 -4
- package/test/components/Chart/utils/handleLabelUpdate.test.ts +32 -16
- 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
|
-
|
|
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
|
};
|
|
@@ -59,11 +70,17 @@ const formatCandleData = (
|
|
|
59
70
|
labelCallback: (key: string) => string
|
|
60
71
|
): string => {
|
|
61
72
|
const [, open, close, low, high] = candleData;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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>
|
|
67
84
|
`;
|
|
68
85
|
};
|
|
69
86
|
|
|
@@ -73,14 +90,16 @@ const formatBookData = ({
|
|
|
73
90
|
bucketDisplayPrecision,
|
|
74
91
|
bookType,
|
|
75
92
|
labelCallback,
|
|
93
|
+
isDesktop,
|
|
76
94
|
}: {
|
|
77
95
|
matchedBucket: Bucket;
|
|
78
96
|
bucketWidth: number;
|
|
79
97
|
bucketDisplayPrecision: number;
|
|
80
98
|
bookType: BookType;
|
|
81
99
|
labelCallback: (key: string) => string;
|
|
100
|
+
isDesktop: boolean;
|
|
82
101
|
}): string => {
|
|
83
|
-
const bookLabelKey = bookType === BookType.Order ? 'orders' : '
|
|
102
|
+
const bookLabelKey = bookType === BookType.Order ? 'orders' : 'trades';
|
|
84
103
|
const priceRangeStart = matchedBucket.price.toFixed(bucketDisplayPrecision);
|
|
85
104
|
const priceRangeEnd = (matchedBucket.price + bucketWidth).toFixed(
|
|
86
105
|
bucketDisplayPrecision
|
|
@@ -89,13 +108,17 @@ const formatBookData = ({
|
|
|
89
108
|
matchedBucket.sentiment,
|
|
90
109
|
bookType
|
|
91
110
|
);
|
|
92
|
-
const sentimentValue = Math.abs(matchedBucket.sentiment)
|
|
93
|
-
bucketDisplayPrecision
|
|
94
|
-
);
|
|
111
|
+
const sentimentValue = Math.abs(matchedBucket.sentiment);
|
|
95
112
|
|
|
96
|
-
return
|
|
97
|
-
|
|
98
|
-
<
|
|
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>`;
|
|
99
122
|
};
|
|
100
123
|
|
|
101
124
|
const formatSentimentData = (
|
|
@@ -103,9 +126,9 @@ const formatSentimentData = (
|
|
|
103
126
|
sentimentShort: number,
|
|
104
127
|
labelCallback: (key: string) => string
|
|
105
128
|
): string => {
|
|
106
|
-
return `<
|
|
107
|
-
<
|
|
108
|
-
<
|
|
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>`;
|
|
109
132
|
};
|
|
110
133
|
|
|
111
134
|
const hasValidCandleData = (
|
|
@@ -142,6 +165,9 @@ export const getTooltipFormatter = ({
|
|
|
142
165
|
labelCallback,
|
|
143
166
|
sentimentLongs,
|
|
144
167
|
sentimentShorts,
|
|
168
|
+
isDesktop,
|
|
169
|
+
locale,
|
|
170
|
+
isDark,
|
|
145
171
|
}: {
|
|
146
172
|
params: TooltipParam[];
|
|
147
173
|
buckets: Bucket[][];
|
|
@@ -151,6 +177,9 @@ export const getTooltipFormatter = ({
|
|
|
151
177
|
labelCallback: (key: string) => string;
|
|
152
178
|
sentimentLongs: (number | null)[];
|
|
153
179
|
sentimentShorts: (number | null)[];
|
|
180
|
+
isDesktop: boolean;
|
|
181
|
+
locale: string;
|
|
182
|
+
isDark: boolean;
|
|
154
183
|
}) => {
|
|
155
184
|
if (!params || !Array.isArray(params) || params.length === 0) {
|
|
156
185
|
return '';
|
|
@@ -185,7 +214,9 @@ export const getTooltipFormatter = ({
|
|
|
185
214
|
return '';
|
|
186
215
|
}
|
|
187
216
|
|
|
188
|
-
const timeFormatted = time
|
|
217
|
+
const timeFormatted = time
|
|
218
|
+
.toLocaleString(locale, DATE_FORMAT_OPTIONS)
|
|
219
|
+
.replace(/\//g, '.');
|
|
189
220
|
const candleSection = showCandles
|
|
190
221
|
? formatCandleData(candleData, labelCallback)
|
|
191
222
|
: '';
|
|
@@ -197,6 +228,7 @@ export const getTooltipFormatter = ({
|
|
|
197
228
|
bucketDisplayPrecision,
|
|
198
229
|
bookType,
|
|
199
230
|
labelCallback,
|
|
231
|
+
isDesktop,
|
|
200
232
|
})
|
|
201
233
|
: '';
|
|
202
234
|
|
|
@@ -205,8 +237,73 @@ export const getTooltipFormatter = ({
|
|
|
205
237
|
? formatSentimentData(sentimentLong, sentimentShort, labelCallback)
|
|
206
238
|
: '';
|
|
207
239
|
|
|
208
|
-
|
|
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};">
|
|
209
299
|
${candleSection}
|
|
300
|
+
</div>
|
|
301
|
+
<div style="width: ${columnWidth};">
|
|
302
|
+
${sentimentSection}
|
|
303
|
+
</div>
|
|
304
|
+
<div style="width: ${columnWidth};">
|
|
210
305
|
${bookSection}
|
|
211
|
-
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
</div>`;
|
|
212
309
|
};
|
|
@@ -11,7 +11,8 @@ export const handleLabelUpdate = (
|
|
|
11
11
|
instance: EChartsType,
|
|
12
12
|
mainData: ChartProps['mainData'],
|
|
13
13
|
labelTimerRef: MutableRefObject<NodeJS.Timeout | null>,
|
|
14
|
-
isGreaterThanTwoWeeksRef: MutableRefObject<boolean | null
|
|
14
|
+
isGreaterThanTwoWeeksRef: MutableRefObject<boolean | null>,
|
|
15
|
+
locale: string
|
|
15
16
|
): void => {
|
|
16
17
|
if (labelTimerRef.current) {
|
|
17
18
|
clearTimeout(labelTimerRef.current);
|
|
@@ -55,7 +56,7 @@ export const handleLabelUpdate = (
|
|
|
55
56
|
id: 'main-xAxis',
|
|
56
57
|
axisLabel: {
|
|
57
58
|
formatter: (value: string) =>
|
|
58
|
-
formatXAxisLabel(value, isGreaterThanTwoWeeks),
|
|
59
|
+
formatXAxisLabel(value, isGreaterThanTwoWeeks, locale),
|
|
59
60
|
},
|
|
60
61
|
},
|
|
61
62
|
{
|
|
@@ -63,7 +64,11 @@ export const handleLabelUpdate = (
|
|
|
63
64
|
axisLabel: {
|
|
64
65
|
customValues: labelsData,
|
|
65
66
|
formatter: (value: unknown) =>
|
|
66
|
-
formatXAxisAdditionalLabel(
|
|
67
|
+
formatXAxisAdditionalLabel(
|
|
68
|
+
value,
|
|
69
|
+
isGreaterThanTwoWeeks,
|
|
70
|
+
locale
|
|
71
|
+
),
|
|
67
72
|
},
|
|
68
73
|
},
|
|
69
74
|
],
|
|
@@ -40,6 +40,7 @@ export const getOption: GetOptionType = ({
|
|
|
40
40
|
isDark,
|
|
41
41
|
isDesktop,
|
|
42
42
|
labelCallback,
|
|
43
|
+
locale,
|
|
43
44
|
}) => {
|
|
44
45
|
const styles = getChartStyles(isDark);
|
|
45
46
|
const selectedPriceRef = { current: 0 };
|
|
@@ -94,13 +95,18 @@ export const getOption: GetOptionType = ({
|
|
|
94
95
|
tooltipLinesColor: styles.tooltipLinesColor,
|
|
95
96
|
sentimentLongs,
|
|
96
97
|
sentimentShorts,
|
|
98
|
+
isDesktop,
|
|
99
|
+
isDark,
|
|
100
|
+
locale,
|
|
97
101
|
}),
|
|
98
102
|
grid: getGridConfig({ isDesktop }),
|
|
99
103
|
xAxis: getXAxisConfig({
|
|
100
104
|
dates,
|
|
101
105
|
isGreaterThanTwoWeeks,
|
|
106
|
+
locale,
|
|
102
107
|
}),
|
|
103
108
|
yAxis: getYAxisConfig({
|
|
109
|
+
isDesktop,
|
|
104
110
|
bucketWidth,
|
|
105
111
|
displayPrecision,
|
|
106
112
|
}),
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
interface UseResizeObserverOptions {
|
|
4
|
+
debounceDelay?: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const useResizeObserver = (
|
|
8
|
+
options: UseResizeObserverOptions = {}
|
|
9
|
+
): [React.RefObject<HTMLDivElement>, boolean] => {
|
|
10
|
+
const { debounceDelay = 150 } = options;
|
|
11
|
+
const [isResizing, setIsResizing] = useState(false);
|
|
12
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
13
|
+
const resizeTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const container = containerRef.current;
|
|
17
|
+
if (!container) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
22
|
+
console.warn('ResizeObserver is not supported in this browser');
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
27
|
+
setIsResizing(true);
|
|
28
|
+
|
|
29
|
+
if (resizeTimeoutRef.current) {
|
|
30
|
+
clearTimeout(resizeTimeoutRef.current);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
resizeTimeoutRef.current = setTimeout(() => {
|
|
34
|
+
setIsResizing(false);
|
|
35
|
+
resizeTimeoutRef.current = null;
|
|
36
|
+
}, debounceDelay);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
resizeObserver.observe(container);
|
|
40
|
+
|
|
41
|
+
return () => {
|
|
42
|
+
resizeObserver.disconnect();
|
|
43
|
+
if (resizeTimeoutRef.current) {
|
|
44
|
+
clearTimeout(resizeTimeoutRef.current);
|
|
45
|
+
resizeTimeoutRef.current = null;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}, [debounceDelay]);
|
|
49
|
+
|
|
50
|
+
return [containerRef, isResizing];
|
|
51
|
+
};
|
|
@@ -12,14 +12,15 @@ interface LegendBarProps {
|
|
|
12
12
|
|
|
13
13
|
export const LegendBar = ({ type, isDark }: LegendBarProps) => {
|
|
14
14
|
const colorPalette = isDark ? COLOR_MAP.dark : COLOR_MAP.light;
|
|
15
|
+
|
|
15
16
|
const startColor =
|
|
16
17
|
type === 'long' ? colorPalette.long[1] : colorPalette.short[0];
|
|
17
18
|
const endColor =
|
|
18
19
|
type === 'long' ? colorPalette.long[0] : colorPalette.short[1];
|
|
19
20
|
const endGradient =
|
|
20
21
|
type === 'long'
|
|
21
|
-
? `linear-gradient(90deg,${endColor} 0%,
|
|
22
|
-
: `linear-gradient(270deg,${startColor} 0%,
|
|
22
|
+
? `linear-gradient(90deg,${endColor} 0%, ${colorPalette.backgroundColor} 100%)`
|
|
23
|
+
: `linear-gradient(270deg,${startColor} 0%, ${colorPalette.backgroundColor} 100%)`;
|
|
23
24
|
|
|
24
25
|
return (
|
|
25
26
|
<div
|
|
@@ -15,9 +15,9 @@ export const TIME_THRESHOLDS = {
|
|
|
15
15
|
TWO_WEEKS_MS: 14 * 24 * 60 * 60 * 1000,
|
|
16
16
|
} as const;
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const CHART_CONFIG_STATIC = {
|
|
19
|
+
MAIN_HEIGHT_DESKTOP: 410,
|
|
20
|
+
MAIN_HEIGHT_MOBILE: 330,
|
|
21
21
|
MARGIN_BETWEEN: 50,
|
|
22
22
|
SENTIMENT_HEIGHT: 120,
|
|
23
23
|
WIDTH: 9999,
|
|
@@ -33,8 +33,25 @@ export const CHART_CONFIG = {
|
|
|
33
33
|
SENTIMENT_MIN: 0,
|
|
34
34
|
SENTIMENT_MAX: 100,
|
|
35
35
|
TOP_MARGIN_DESKTOP: 0,
|
|
36
|
-
TOP_MARGIN_MOBILE:
|
|
37
|
-
|
|
36
|
+
TOP_MARGIN_MOBILE: 24,
|
|
37
|
+
MOBILE_TOOLTIP_HEIGHT: 85,
|
|
38
|
+
TOOLTIP_OFFSET: 8,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const CHART_CONFIG_CALCULATED = {
|
|
42
|
+
HEIGHT_DESKTOP:
|
|
43
|
+
CHART_CONFIG_STATIC.MAIN_HEIGHT_DESKTOP +
|
|
44
|
+
CHART_CONFIG_STATIC.TOP_MARGIN_DESKTOP +
|
|
45
|
+
CHART_CONFIG_STATIC.X_LABEL_SIZE,
|
|
46
|
+
HEIGHT_MOBILE:
|
|
47
|
+
CHART_CONFIG_STATIC.MAIN_HEIGHT_MOBILE +
|
|
48
|
+
CHART_CONFIG_STATIC.X_LABEL_SIZE +
|
|
49
|
+
CHART_CONFIG_STATIC.TOP_MARGIN_MOBILE +
|
|
50
|
+
CHART_CONFIG_STATIC.MOBILE_TOOLTIP_HEIGHT,
|
|
51
|
+
};
|
|
52
|
+
export const CHART_CONFIG = {
|
|
53
|
+
...CHART_CONFIG_STATIC,
|
|
54
|
+
...CHART_CONFIG_CALCULATED,
|
|
38
55
|
} as const;
|
|
39
56
|
|
|
40
57
|
export const COLOR_MAP = {
|
|
@@ -47,6 +64,7 @@ export const COLOR_MAP = {
|
|
|
47
64
|
chroma(colorPalette.darkYellow10).shade(0.5).hex(),
|
|
48
65
|
chroma(colorPalette.darkYellow90).shade(0.5).hex(),
|
|
49
66
|
],
|
|
67
|
+
backgroundColor: colorPalette.black,
|
|
50
68
|
},
|
|
51
69
|
light: {
|
|
52
70
|
long: [
|
|
@@ -57,6 +75,7 @@ export const COLOR_MAP = {
|
|
|
57
75
|
chroma(colorPalette.lightYellow10).tint(0.5).hex(),
|
|
58
76
|
chroma(colorPalette.lightYellow90).tint(0.5).hex(),
|
|
59
77
|
],
|
|
78
|
+
backgroundColor: colorPalette.white,
|
|
60
79
|
},
|
|
61
80
|
} as const;
|
|
62
81
|
|
|
@@ -13,6 +13,10 @@ const navigationConfig = [
|
|
|
13
13
|
];
|
|
14
14
|
|
|
15
15
|
const instrumentSelectConfigOC = [
|
|
16
|
+
{
|
|
17
|
+
id: InstrumentId.EUR_USD,
|
|
18
|
+
label: 'EUR/USD',
|
|
19
|
+
},
|
|
16
20
|
{
|
|
17
21
|
id: InstrumentId.EUR_AUD,
|
|
18
22
|
label: 'EUR/AUD',
|
|
@@ -25,10 +29,6 @@ const instrumentSelectConfigOC = [
|
|
|
25
29
|
id: InstrumentId.EUR_JPY,
|
|
26
30
|
label: 'EUR/JPY',
|
|
27
31
|
},
|
|
28
|
-
{
|
|
29
|
-
id: InstrumentId.EUR_USD,
|
|
30
|
-
label: 'EUR/USD',
|
|
31
|
-
},
|
|
32
32
|
{
|
|
33
33
|
id: InstrumentId.EUR_CHF,
|
|
34
34
|
label: 'EUR/CHF',
|
|
@@ -3,31 +3,32 @@
|
|
|
3
3
|
"15_minutes": "15 minutes",
|
|
4
4
|
"4_hours": "4 hours",
|
|
5
5
|
"5_minutes": "5 minutes",
|
|
6
|
-
"buy_overbalance": "Buy overbalance",
|
|
7
6
|
"buy": "Buy",
|
|
7
|
+
"buy_overbalance": "Buy overbalance",
|
|
8
8
|
"candle": "Candle",
|
|
9
|
-
"
|
|
9
|
+
"close": "Close",
|
|
10
10
|
"data_unavailable": "Data unavailable",
|
|
11
|
+
"even_market_demand": "Even market demand",
|
|
11
12
|
"granularity": "Granularity",
|
|
12
13
|
"high": "High",
|
|
13
14
|
"instrument": "Instrument",
|
|
14
|
-
"long_overbalance": "Long overbalance",
|
|
15
15
|
"long": "Long",
|
|
16
|
+
"long_overbalance": "Long overbalance",
|
|
16
17
|
"low": "Low",
|
|
17
18
|
"no_matching_results": "No matching results",
|
|
18
|
-
"
|
|
19
|
+
"open": "Open",
|
|
19
20
|
"order_book": "Order book",
|
|
20
21
|
"orders": "Orders",
|
|
21
22
|
"pagination_entries_range": "{{firstItemOnPage}}-{{lastItemOnPage}} of {{itemCount}} entries",
|
|
22
23
|
"position_book": "Position book",
|
|
23
|
-
"
|
|
24
|
+
"price": "Price",
|
|
24
25
|
"price_range": "Price range",
|
|
25
26
|
"search": "Search",
|
|
26
|
-
"sell_overbalance": "Sell overbalance",
|
|
27
27
|
"sell": "Sell",
|
|
28
|
+
"sell_overbalance": "Sell overbalance",
|
|
28
29
|
"sentiment": "Sentiment",
|
|
29
|
-
"short_overbalance": "Short overbalance",
|
|
30
30
|
"short": "Short",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
31
|
+
"short_overbalance": "Short overbalance",
|
|
32
|
+
"tap_chart_to_see_more_details": "Tap chart to see more details",
|
|
33
|
+
"trades": "Trades"
|
|
33
34
|
}
|
|
@@ -110,13 +110,13 @@ describe('chartUtils', () => {
|
|
|
110
110
|
const sampleIso = '2025-03-15T10:30:00Z';
|
|
111
111
|
|
|
112
112
|
it('formats time when flag is true', () => {
|
|
113
|
-
const result = formatXAxisLabel(sampleIso, true);
|
|
113
|
+
const result = formatXAxisLabel(sampleIso, true, 'en-US');
|
|
114
114
|
expect(typeof result).toBe('string');
|
|
115
115
|
expect(result).toContain(':');
|
|
116
116
|
});
|
|
117
117
|
|
|
118
118
|
it('formats day when flag is false', () => {
|
|
119
|
-
const result = formatXAxisLabel(sampleIso, false);
|
|
119
|
+
const result = formatXAxisLabel(sampleIso, false, 'en-US');
|
|
120
120
|
expect(typeof result).toBe('string');
|
|
121
121
|
// Contains day of month (15) with surrounding spaces per implementation
|
|
122
122
|
expect(result).toMatch(/\s15\s/);
|
|
@@ -189,11 +189,14 @@ describe('chartUtils', () => {
|
|
|
189
189
|
labelCallback,
|
|
190
190
|
sentimentLongs: [],
|
|
191
191
|
sentimentShorts: [],
|
|
192
|
+
isDesktop: true,
|
|
193
|
+
locale: 'en-US',
|
|
194
|
+
isDark: false,
|
|
192
195
|
});
|
|
193
196
|
expect(html).toBeDefined();
|
|
194
197
|
expect(html).toContain('candle');
|
|
195
|
-
expect(html).toContain('
|
|
196
|
-
expect(html).toContain('
|
|
198
|
+
expect(html).toContain('open');
|
|
199
|
+
expect(html).toContain('close');
|
|
197
200
|
expect(html).toContain('low');
|
|
198
201
|
expect(html).toContain('high');
|
|
199
202
|
expect(html).toContain('orders');
|
|
@@ -227,6 +230,9 @@ describe('chartUtils', () => {
|
|
|
227
230
|
labelCallback,
|
|
228
231
|
sentimentLongs: [30.5],
|
|
229
232
|
sentimentShorts: [69.5],
|
|
233
|
+
isDesktop: true,
|
|
234
|
+
locale: 'en-US',
|
|
235
|
+
isDark: false,
|
|
230
236
|
});
|
|
231
237
|
expect(html).toBeDefined();
|
|
232
238
|
expect(html).toContain('candle');
|
|
@@ -257,6 +263,9 @@ describe('chartUtils', () => {
|
|
|
257
263
|
labelCallback,
|
|
258
264
|
sentimentLongs: [],
|
|
259
265
|
sentimentShorts: [],
|
|
266
|
+
isDesktop: true,
|
|
267
|
+
locale: 'en-US',
|
|
268
|
+
isDark: false,
|
|
260
269
|
});
|
|
261
270
|
expect(html).toBe('');
|
|
262
271
|
});
|
|
@@ -271,6 +280,9 @@ describe('chartUtils', () => {
|
|
|
271
280
|
labelCallback,
|
|
272
281
|
sentimentLongs: [],
|
|
273
282
|
sentimentShorts: [],
|
|
283
|
+
isDesktop: true,
|
|
284
|
+
locale: 'en-US',
|
|
285
|
+
isDark: false,
|
|
274
286
|
});
|
|
275
287
|
expect(html).toBe('');
|
|
276
288
|
});
|