@scality/core-ui 0.171.0 → 0.173.0
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/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
- package/dist/components/barchartv2/Barchart.component.js +2 -2
- package/dist/components/barchartv2/BarchartTooltip.d.ts +11 -0
- package/dist/components/barchartv2/BarchartTooltip.d.ts.map +1 -0
- package/dist/components/barchartv2/BarchartTooltip.js +27 -0
- package/dist/components/charttooltip/ChartTooltip.d.ts +13 -0
- package/dist/components/charttooltip/ChartTooltip.d.ts.map +1 -0
- package/dist/components/charttooltip/ChartTooltip.js +49 -0
- package/dist/components/globalhealthbar/GlobalHealthBar.component.d.ts +4 -0
- package/dist/components/globalhealthbar/GlobalHealthBar.component.d.ts.map +1 -1
- package/dist/components/globalhealthbar/GlobalHealthBar.component.js +4 -0
- package/dist/components/globalhealthbar/GlobalHealthBarRecharts.component.d.ts +10 -0
- package/dist/components/globalhealthbar/GlobalHealthBarRecharts.component.d.ts.map +1 -0
- package/dist/components/globalhealthbar/GlobalHealthBarRecharts.component.js +78 -0
- package/dist/components/globalhealthbar/components/GlobalHealthBarTooltip.d.ts +18 -0
- package/dist/components/globalhealthbar/components/GlobalHealthBarTooltip.d.ts.map +1 -0
- package/dist/components/globalhealthbar/components/GlobalHealthBarTooltip.js +95 -0
- package/dist/components/globalhealthbar/components/HealthBarXAxis.d.ts +7 -0
- package/dist/components/globalhealthbar/components/HealthBarXAxis.d.ts.map +1 -0
- package/dist/components/globalhealthbar/components/HealthBarXAxis.js +25 -0
- package/dist/components/globalhealthbar/healthBarUtils.d.ts +77 -0
- package/dist/components/globalhealthbar/healthBarUtils.d.ts.map +1 -0
- package/dist/components/globalhealthbar/healthBarUtils.js +196 -0
- package/dist/components/globalhealthbar/healthBarUtils.spec.d.ts +2 -0
- package/dist/components/globalhealthbar/healthBarUtils.spec.d.ts.map +1 -0
- package/dist/components/globalhealthbar/healthBarUtils.spec.js +391 -0
- package/dist/components/globalhealthbar/useHealthBarData.d.ts +18 -0
- package/dist/components/globalhealthbar/useHealthBarData.d.ts.map +1 -0
- package/dist/components/globalhealthbar/useHealthBarData.js +46 -0
- package/dist/components/globalhealthbar/useHealthBarData.spec.d.ts +2 -0
- package/dist/components/globalhealthbar/useHealthBarData.spec.d.ts.map +1 -0
- package/dist/components/globalhealthbar/useHealthBarData.spec.js +207 -0
- package/dist/components/icon/Icon.component.js +2 -2
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts +0 -2
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -1
- package/dist/components/linetimeseriechart/linetimeseriechart.component.js +17 -57
- package/dist/components/sparkline/sparkline.component.d.ts +16 -0
- package/dist/components/sparkline/sparkline.component.d.ts.map +1 -0
- package/dist/components/sparkline/sparkline.component.js +20 -0
- package/dist/components/text/Text.component.d.ts +1 -1
- package/dist/components/text/Text.component.d.ts.map +1 -1
- package/dist/components/text/Text.component.js +6 -1
- package/dist/next.d.ts +4 -1
- package/dist/next.d.ts.map +1 -1
- package/dist/next.js +4 -1
- package/package.json +2 -2
- package/src/lib/components/barchartv2/Barchart.component.tsx +3 -2
- package/src/lib/components/barchartv2/{ChartTooltip.test.tsx → BarchartTooltip.test.tsx} +35 -12
- package/src/lib/components/barchartv2/BarchartTooltip.tsx +89 -0
- package/src/lib/components/charttooltip/ChartTooltip.tsx +83 -0
- package/src/lib/components/globalhealthbar/GlobalHealthBar.component.tsx +4 -1
- package/src/lib/components/globalhealthbar/GlobalHealthBarRecharts.component.tsx +203 -0
- package/src/lib/components/globalhealthbar/components/GlobalHealthBarTooltip.tsx +173 -0
- package/src/lib/components/globalhealthbar/components/HealthBarXAxis.tsx +94 -0
- package/src/lib/components/globalhealthbar/healthBarUtils.spec.ts +701 -0
- package/src/lib/components/globalhealthbar/healthBarUtils.ts +311 -0
- package/src/lib/components/globalhealthbar/useHealthBarData.spec.tsx +487 -0
- package/src/lib/components/globalhealthbar/useHealthBarData.ts +74 -0
- package/src/lib/components/icon/Icon.component.tsx +2 -2
- package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +50 -77
- package/src/lib/components/sparkline/sparkline.component.tsx +54 -0
- package/src/lib/components/text/Text.component.tsx +8 -2
- package/src/lib/next.ts +9 -1
- package/stories/GlobalHealthBar/globalhealthbarRecharts.stories.tsx +145 -0
- package/stories/GlobalHealthBar/globalheathbarrecharts.guideline.mdx +5 -0
- package/stories/InlineInput/InlineInput.stories.tsx +7 -1
- package/stories/globalhealthbar.stories.tsx +25 -5
- package/stories/sparkline.stories.tsx +168 -0
- package/dist/components/barchartv2/ChartTooltip.d.ts +0 -14
- package/dist/components/barchartv2/ChartTooltip.d.ts.map +0 -1
- package/dist/components/barchartv2/ChartTooltip.js +0 -41
- package/src/lib/components/barchartv2/ChartTooltip.tsx +0 -106
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { fontSize } from '../../style/theme';
|
|
2
|
+
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// CONSTANTS
|
|
5
|
+
// =============================================================================
|
|
6
|
+
|
|
7
|
+
export const CHART_CONFIG = {
|
|
8
|
+
RADIUS_SIZE: 4,
|
|
9
|
+
EDGE_THRESHOLD: 8,
|
|
10
|
+
CHART_HEIGHT: 50,
|
|
11
|
+
BAR_SIZE: 8,
|
|
12
|
+
TICK_SIZE: 4,
|
|
13
|
+
TOOLTIP_OFFSET: 24,
|
|
14
|
+
FONT_SIZE: fontSize.smaller,
|
|
15
|
+
TEXT_DY_OFFSET: 12,
|
|
16
|
+
TICK_INTERVAL: 0,
|
|
17
|
+
MARGINS: { left: 28, right: 28, bottom: 4, top: 4 },
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
export const TIME_CONSTANTS = {
|
|
21
|
+
ONE_HOUR: 60 * 60 * 1000,
|
|
22
|
+
ONE_DAY: 24 * 60 * 60 * 1000,
|
|
23
|
+
ONE_WEEK: 7 * 24 * 60 * 60 * 1000,
|
|
24
|
+
MARGIN_HOURS: 6,
|
|
25
|
+
FIFTEEN_MINUTES: 15 * 60 * 1000,
|
|
26
|
+
SIX_HOURS: 6 * 60 * 60 * 1000,
|
|
27
|
+
} as const;
|
|
28
|
+
|
|
29
|
+
export const LABEL_CONFIG = {
|
|
30
|
+
MIN_SPACE_PER_TICK: 80,
|
|
31
|
+
MODULO_CONFIG: {
|
|
32
|
+
[TIME_CONSTANTS.ONE_WEEK]: 2,
|
|
33
|
+
[TIME_CONSTANTS.ONE_DAY]: 3,
|
|
34
|
+
[TIME_CONSTANTS.ONE_HOUR]: 3,
|
|
35
|
+
},
|
|
36
|
+
} as const;
|
|
37
|
+
|
|
38
|
+
// =============================================================================
|
|
39
|
+
// TICK CALCULATIONS
|
|
40
|
+
// =============================================================================
|
|
41
|
+
|
|
42
|
+
const { ONE_HOUR, ONE_DAY, MARGIN_HOURS, FIFTEEN_MINUTES, SIX_HOURS } =
|
|
43
|
+
TIME_CONSTANTS;
|
|
44
|
+
|
|
45
|
+
const generateTickArray = (
|
|
46
|
+
endTimestamp: number,
|
|
47
|
+
count: number,
|
|
48
|
+
interval: number,
|
|
49
|
+
): number[] => {
|
|
50
|
+
return Array.from({ length: count }, (_, i) => endTimestamp - i * interval);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const roundToNearestHalfDay = (timestamp: number): number => {
|
|
54
|
+
const date = new Date(timestamp);
|
|
55
|
+
const hours = date.getHours();
|
|
56
|
+
|
|
57
|
+
if (hours <= 12) {
|
|
58
|
+
return new Date(timestamp).setHours(0, 0, 0, 0);
|
|
59
|
+
} else {
|
|
60
|
+
return new Date(timestamp).setHours(12, 0, 0, 0);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const calculateSevenDayTicks = (endTimestamp: number): number[] => {
|
|
65
|
+
const marginedEnd = endTimestamp - MARGIN_HOURS * ONE_HOUR;
|
|
66
|
+
const roundedEnd = roundToNearestHalfDay(marginedEnd);
|
|
67
|
+
return generateTickArray(roundedEnd, 7, ONE_DAY);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const calculateDayTicks = (endTimestamp: number): number[] => {
|
|
71
|
+
const is6HourTick = endTimestamp % SIX_HOURS === 0;
|
|
72
|
+
const closest6Hours = Math.floor(endTimestamp / SIX_HOURS) * SIX_HOURS;
|
|
73
|
+
const tickCount = is6HourTick ? 5 : 4;
|
|
74
|
+
|
|
75
|
+
return generateTickArray(closest6Hours, tickCount, SIX_HOURS);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const calculateHourTicks = (endTimestamp: number): number[] => {
|
|
79
|
+
const is15MinuteTick = endTimestamp % FIFTEEN_MINUTES === 0;
|
|
80
|
+
const closest15Minutes =
|
|
81
|
+
Math.floor(endTimestamp / FIFTEEN_MINUTES) * FIFTEEN_MINUTES;
|
|
82
|
+
const tickCount = is15MinuteTick ? 5 : 4;
|
|
83
|
+
|
|
84
|
+
return generateTickArray(closest15Minutes, tickCount, FIFTEEN_MINUTES);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const getEdgeMargin = (
|
|
88
|
+
index: number,
|
|
89
|
+
totalTicks: number,
|
|
90
|
+
isDaySpan: boolean,
|
|
91
|
+
): number => {
|
|
92
|
+
if (isDaySpan && totalTicks === 5) {
|
|
93
|
+
return index === 0 ? -8 : index === totalTicks - 1 ? 8 : 0;
|
|
94
|
+
}
|
|
95
|
+
return 0;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const getTicks = (
|
|
99
|
+
startTimestamp: number,
|
|
100
|
+
endTimestamp: number,
|
|
101
|
+
): number[] => {
|
|
102
|
+
const span = endTimestamp - startTimestamp;
|
|
103
|
+
|
|
104
|
+
if (span === 7 * ONE_DAY) {
|
|
105
|
+
return calculateSevenDayTicks(endTimestamp);
|
|
106
|
+
} else if (span === 24 * ONE_HOUR) {
|
|
107
|
+
return calculateDayTicks(endTimestamp);
|
|
108
|
+
} else if (span === ONE_HOUR) {
|
|
109
|
+
return calculateHourTicks(endTimestamp);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return [];
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// =============================================================================
|
|
116
|
+
// LABEL VISIBILITY
|
|
117
|
+
// =============================================================================
|
|
118
|
+
|
|
119
|
+
export interface LabelVisibilityConfig {
|
|
120
|
+
hasEnoughSpace: boolean;
|
|
121
|
+
timeSpan: number;
|
|
122
|
+
tickIndex: number;
|
|
123
|
+
totalTicks: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const calculateLabelVisibility = (
|
|
127
|
+
chartWidth: number,
|
|
128
|
+
totalTicks: number,
|
|
129
|
+
span: number,
|
|
130
|
+
index: number,
|
|
131
|
+
endTimestamp: number,
|
|
132
|
+
): boolean => {
|
|
133
|
+
const hasEnoughSpace =
|
|
134
|
+
chartWidth / totalTicks > LABEL_CONFIG.MIN_SPACE_PER_TICK;
|
|
135
|
+
|
|
136
|
+
// If enough space, show all labels
|
|
137
|
+
if (hasEnoughSpace) return true;
|
|
138
|
+
|
|
139
|
+
// Apply specific rules for each time range
|
|
140
|
+
if (span === TIME_CONSTANTS.ONE_WEEK) {
|
|
141
|
+
return index % 2 === 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (span === TIME_CONSTANTS.ONE_DAY) {
|
|
145
|
+
const isRoundHour = endTimestamp % (60 * 60 * 1000) === 0;
|
|
146
|
+
const roundHourInterval = index % 2 === 0;
|
|
147
|
+
const defaultInterval = index % 3 === 0;
|
|
148
|
+
const result = isRoundHour ? roundHourInterval : defaultInterval;
|
|
149
|
+
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (span === TIME_CONSTANTS.ONE_HOUR) {
|
|
154
|
+
const isRound15Minute = endTimestamp % (15 * 60 * 1000) === 0;
|
|
155
|
+
const round15MinuteInterval = index % 2 === 0;
|
|
156
|
+
const defaultInterval = index % 3 === 0;
|
|
157
|
+
const result = isRound15Minute ? round15MinuteInterval : defaultInterval;
|
|
158
|
+
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return false;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// =============================================================================
|
|
166
|
+
// RECTANGLE PROPERTIES
|
|
167
|
+
// =============================================================================
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Core calculation for alert positioning within a time range
|
|
171
|
+
*/
|
|
172
|
+
export const calculateAlertPosition = (
|
|
173
|
+
alertStartTimestamp: number,
|
|
174
|
+
alertEndTimestamp: number,
|
|
175
|
+
chartStartTimestamp: number,
|
|
176
|
+
chartEndTimestamp: number,
|
|
177
|
+
availableWidth: number,
|
|
178
|
+
baseX: number = 0,
|
|
179
|
+
) => {
|
|
180
|
+
const start = Math.max(alertStartTimestamp, chartStartTimestamp);
|
|
181
|
+
const end = Math.min(alertEndTimestamp, chartEndTimestamp);
|
|
182
|
+
const totalTimeSpan = chartEndTimestamp - chartStartTimestamp;
|
|
183
|
+
const relativeSize = (end - start) / totalTimeSpan;
|
|
184
|
+
|
|
185
|
+
// Calculate start position relative to baseX
|
|
186
|
+
let startX = baseX;
|
|
187
|
+
if (alertStartTimestamp > chartStartTimestamp) {
|
|
188
|
+
const alertStartRelative =
|
|
189
|
+
(alertStartTimestamp - chartStartTimestamp) / totalTimeSpan;
|
|
190
|
+
startX = baseX + alertStartRelative * availableWidth;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const width = relativeSize * availableWidth;
|
|
194
|
+
|
|
195
|
+
return { startX, width, relativeSize };
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// =============================================================================
|
|
199
|
+
// KEYBOARD NAVIGATION UTILS
|
|
200
|
+
// =============================================================================
|
|
201
|
+
|
|
202
|
+
export type NavigationAction =
|
|
203
|
+
| 'previous'
|
|
204
|
+
| 'next'
|
|
205
|
+
| 'first'
|
|
206
|
+
| 'last'
|
|
207
|
+
| 'escape';
|
|
208
|
+
|
|
209
|
+
export interface NavigationState {
|
|
210
|
+
newIndex: number;
|
|
211
|
+
selectedAlert: any | null;
|
|
212
|
+
shouldActivateKeyboard: boolean;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Maps keyboard events to navigation actions
|
|
217
|
+
*/
|
|
218
|
+
export const getNavigationAction = (key: string): NavigationAction | null => {
|
|
219
|
+
switch (key) {
|
|
220
|
+
case 'ArrowLeft':
|
|
221
|
+
case 'ArrowUp':
|
|
222
|
+
return 'previous';
|
|
223
|
+
case 'ArrowRight':
|
|
224
|
+
case 'ArrowDown':
|
|
225
|
+
return 'next';
|
|
226
|
+
case 'Home':
|
|
227
|
+
return 'first';
|
|
228
|
+
case 'End':
|
|
229
|
+
return 'last';
|
|
230
|
+
case 'Escape':
|
|
231
|
+
return 'escape';
|
|
232
|
+
default:
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Calculates new index based on navigation action
|
|
239
|
+
*/
|
|
240
|
+
export const calculateNavigationIndex = (
|
|
241
|
+
action: NavigationAction,
|
|
242
|
+
currentIndex: number,
|
|
243
|
+
arrayLength: number,
|
|
244
|
+
): number => {
|
|
245
|
+
if (arrayLength === 0) return -1;
|
|
246
|
+
|
|
247
|
+
switch (action) {
|
|
248
|
+
case 'previous':
|
|
249
|
+
return currentIndex <= 0 ? arrayLength - 1 : currentIndex - 1;
|
|
250
|
+
case 'next':
|
|
251
|
+
return currentIndex >= arrayLength - 1 ? 0 : currentIndex + 1;
|
|
252
|
+
case 'first':
|
|
253
|
+
return 0;
|
|
254
|
+
case 'last':
|
|
255
|
+
return arrayLength - 1;
|
|
256
|
+
case 'escape':
|
|
257
|
+
return -1;
|
|
258
|
+
default:
|
|
259
|
+
return currentIndex;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Gets complete navigation state update for a given action
|
|
265
|
+
*/
|
|
266
|
+
export const getNavigationStateUpdate = <T>(
|
|
267
|
+
action: NavigationAction,
|
|
268
|
+
currentIndex: number,
|
|
269
|
+
alerts: T[],
|
|
270
|
+
): NavigationState => {
|
|
271
|
+
const newIndex = calculateNavigationIndex(
|
|
272
|
+
action,
|
|
273
|
+
currentIndex,
|
|
274
|
+
alerts.length,
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
newIndex,
|
|
279
|
+
selectedAlert: newIndex >= 0 ? alerts[newIndex] : null,
|
|
280
|
+
shouldActivateKeyboard: action !== 'escape',
|
|
281
|
+
};
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// =============================================================================
|
|
285
|
+
// TOOLTIP UTILS
|
|
286
|
+
// =============================================================================
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Calculates tooltip position for an alert based on its time range
|
|
290
|
+
*/
|
|
291
|
+
export const getTooltipPosition = (
|
|
292
|
+
alert: { startsAt: string; endsAt: string },
|
|
293
|
+
startTimestamp: number,
|
|
294
|
+
endTimestamp: number,
|
|
295
|
+
chartUsableWidth: number,
|
|
296
|
+
) => {
|
|
297
|
+
const alertStart = new Date(alert.startsAt).getTime();
|
|
298
|
+
const alertEnd = new Date(alert.endsAt).getTime();
|
|
299
|
+
|
|
300
|
+
const { startX, width } = calculateAlertPosition(
|
|
301
|
+
alertStart,
|
|
302
|
+
alertEnd,
|
|
303
|
+
startTimestamp,
|
|
304
|
+
endTimestamp,
|
|
305
|
+
chartUsableWidth,
|
|
306
|
+
CHART_CONFIG.MARGINS.left, // Include left margin as baseX
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
// Return center position of the alert
|
|
310
|
+
return startX + width / 2;
|
|
311
|
+
};
|