@@ -11,7 +11,7 @@ import {
11
11
} from 'echarts/components' ;
12
12
import { ECharts , EChartsOption } from 'echarts' ;
13
13
import { colors , GetHeatmapAppData } from '@pinpoint-fe/ui/src/constants' ;
14
- import { capitalize , debounce } from 'lodash' ;
14
+ import { capitalize , debounce , max } from 'lodash' ;
15
15
import { defaultTickFormatter } from '@pinpoint-fe/ui/src/components/ReChart' ;
16
16
import { HeatmapSettingType } from './HeatmapSetting' ;
17
17
@@ -26,10 +26,15 @@ echarts.use([
26
26
27
27
echarts . use ( [ HeatmapChartEcharts , CanvasRenderer ] ) ;
28
28
29
+ const colorSteps = 10 ;
29
30
export const HeatmapColor = {
30
- success : '#34b994' ,
31
- failed : '#eb4748' ,
32
- selected : 'blue' ,
31
+ success : Array . from ( { length : colorSteps } , ( _ , i ) => {
32
+ return colors . green [ ( i + 1 ) * 100 === 1000 ? 950 : ( i + 1 ) * 100 ] ;
33
+ } ) ,
34
+ fail : Array . from ( { length : colorSteps } , ( _ , i ) => {
35
+ return colors . red [ ( i + 1 ) * 100 === 1000 ? 950 : ( i + 1 ) * 100 ] ;
36
+ } ) ,
37
+ selected : colors . yellow [ 200 ] ,
33
38
} ;
34
39
35
40
type HeatmapChartCoreProps = {
@@ -38,6 +43,14 @@ type HeatmapChartCoreProps = {
38
43
setting : HeatmapSettingType ;
39
44
} ;
40
45
46
+ type DataForRender = {
47
+ value : [ string , string , number ] ;
48
+ itemStyle ?: {
49
+ color : string ;
50
+ opacity : number ;
51
+ } ;
52
+ } ;
53
+
41
54
const HeatmapChartCore = React . forwardRef (
42
55
( { data, setting } : HeatmapChartCoreProps , ref : React . Ref < ReactEChartsCore > ) => {
43
56
const containerRef = React . useRef < HTMLDivElement > ( null ) ;
@@ -88,39 +101,54 @@ const HeatmapChartCore = React.forwardRef(
88
101
[ startCell , endCell ] ,
89
102
) ;
90
103
104
+ const maxCount = React . useMemo ( ( ) => {
105
+ let success = 0 ;
106
+ let fail = 0 ;
107
+
108
+ const { heatmapData } = data || { } ;
109
+
110
+ if ( heatmapData ) {
111
+ for ( const data of heatmapData ) {
112
+ for ( const cell of data . cellDataList ) {
113
+ success = Math . max ( success , cell . successCount ) ;
114
+ fail = Math . max ( fail , cell . failCount ) ;
115
+ }
116
+ }
117
+ }
118
+
119
+ return { success, fail } ;
120
+ } , [ data ] ) ;
121
+
91
122
const dataForRender = React . useMemo ( ( ) => {
92
- const successData : [ string , string , number ] [ ] = [ ] ;
93
- const failData : {
94
- value : [ string , string , number ] ;
95
- itemStyle ?: {
96
- color : string ;
97
- opacity : number ;
98
- } ;
99
- } [ ] = [ ] ;
100
- let maxFailCount = 0 ;
101
- let maxSuccessCount = 0 ;
123
+ const successData : DataForRender [ 'value' ] [ ] = [ ] ;
124
+ const failData : DataForRender [ 'value' ] [ ] = [ ] ;
125
+ const coverData : DataForRender [ ] = [ ] ; // 가장 위에 덮어져서 tooltip, select 이벤트를 받기 위한 것
102
126
103
127
const { heatmapData } = data || { } ;
104
128
heatmapData ?. forEach ( ( row ) => {
105
129
row ?. cellDataList ?. forEach ( ( cell ) => {
106
- successData . push ( [ String ( row . timestamp ) , String ( cell . elapsedTime ) , cell . successCount ] ) ;
107
- failData . push ( {
108
- value : [ String ( row . timestamp ) , String ( cell . elapsedTime ) , cell . failCount ] ,
109
- itemStyle : isSelectedCell ( row . timestamp , cell . elapsedTime )
110
- ? {
111
- color : HeatmapColor . selected ,
112
- opacity : 0.8 ,
113
- }
114
- : undefined ,
130
+ coverData . push ( {
131
+ value : [ String ( row . timestamp ) , String ( cell . elapsedTime ) , 0 ] ,
132
+ itemStyle : {
133
+ color : isSelectedCell ( row . timestamp , cell . elapsedTime )
134
+ ? HeatmapColor . selected
135
+ : 'transparent' ,
136
+ opacity : 1 ,
137
+ } ,
115
138
} ) ;
116
139
117
- maxSuccessCount = Math . max ( maxSuccessCount , cell . successCount ) ;
118
- maxFailCount = Math . max ( maxFailCount , cell . failCount ) ;
140
+ if ( cell ?. successCount ) {
141
+ successData . push ( [ String ( row . timestamp ) , String ( cell . elapsedTime ) , cell . successCount ] ) ;
142
+ }
143
+
144
+ if ( cell ?. failCount ) {
145
+ failData . push ( [ String ( row . timestamp ) , String ( cell . elapsedTime ) , cell . failCount ] ) ;
146
+ }
119
147
} ) ;
120
148
} ) ;
121
149
122
- return { successData, failData, maxFailCount , maxSuccessCount } ;
123
- } , [ data , startCell , endCell ] ) ;
150
+ return { successData, failData, coverData } ;
151
+ } , [ data , startCell , endCell , maxCount ] ) ;
124
152
125
153
const xAxisData = React . useMemo ( ( ) => {
126
154
return data ?. heatmapData ?. map ( ( row ) => String ( row . timestamp ) ) || [ ] ;
@@ -146,24 +174,30 @@ const HeatmapChartCore = React.forwardRef(
146
174
} ,
147
175
formatter : ( params : any ) => {
148
176
const { data } = params ;
149
- const [ timestamp , elapsedTime , failedCount ] = data ?. value ;
177
+ const [ timestamp , elapsedTime ] = data ?. value ;
178
+
179
+ const failedCount = dataForRender ?. failData . find ( ( item : [ string , string , number ] ) => {
180
+ return item [ 0 ] === timestamp && item [ 1 ] === elapsedTime ;
181
+ } ) ?. [ 2 ] ;
182
+
150
183
const successCount = dataForRender ?. successData . find ( ( item : [ string , string , number ] ) => {
151
184
return item [ 0 ] === timestamp && item [ 1 ] === elapsedTime ;
152
185
} ) ?. [ 2 ] ;
186
+
153
187
return `
154
188
<div style="display: flex; flex-direction: column; gap: 5px; padding: 2px;">
155
189
<div style="margin-bottom: 5px;"><strong>${ defaultTickFormatter ( Number ( timestamp ) ) } </strong></div>
156
- ${ [ 'success' , 'failed ' ]
190
+ ${ [ 'success' , 'fail ' ]
157
191
. map ( ( type ) => {
158
192
const count = type === 'success' ? successCount : failedCount ;
159
- const color = type === 'success' ? HeatmapColor . success : HeatmapColor . failed ;
193
+ const color = HeatmapColor [ type as 'success' | 'fail' ] [ 5 ] ;
160
194
161
195
return `
162
196
<div style="display: flex; justify-content: space-between; gap: 5px;">
163
197
<div style="display: flex; gap: 6px; align-items: center;">
164
- <div style="width: 8px; height: 8px; background: ${ color } "></div>${ capitalize ( type ) }
198
+ <div style="width: 8px; height: 8px; background: ${ color } ; border: ${ count === 0 ? '1px solid black' : 'none' } ; "></div>${ capitalize ( type ) }
165
199
</div>
166
- <div>${ count === undefined ? 'N/A' : Number ( count ) . toLocaleString ( ) } </div>
200
+ <div>${ count === undefined ? 0 : Number ( count ) . toLocaleString ( ) } </div>
167
201
</div>
168
202
` ;
169
203
} )
@@ -195,6 +229,7 @@ const HeatmapChartCore = React.forwardRef(
195
229
data : yAxisData . filter (
196
230
( yValue ) => Number ( yValue ) >= setting . yMin && Number ( yValue ) <= setting . yMax ,
197
231
) ,
232
+ offset : 1 ,
198
233
axisLabel : {
199
234
interval : ( index : number , value : string ) => {
200
235
if ( yAxisData . length <= 5 ) {
@@ -223,7 +258,7 @@ const HeatmapChartCore = React.forwardRef(
223
258
{
224
259
id : 'success' ,
225
260
min : 0 ,
226
- max : dataForRender ?. maxSuccessCount ,
261
+ max : maxCount . success ,
227
262
calculable : true ,
228
263
seriesIndex : 0 ,
229
264
orient : 'horizontal' ,
@@ -239,14 +274,14 @@ const HeatmapChartCore = React.forwardRef(
239
274
return Math . floor ( Number ( value ) ) . toLocaleString ( ) ;
240
275
} ,
241
276
inRange : {
242
- color : [ '#ffffff' , dataForRender ?. maxFailCount ? HeatmapColor . success : '#ffffff' ] ,
277
+ color : HeatmapColor . success ,
243
278
} ,
244
279
range : successRange ,
245
280
} ,
246
281
{
247
282
id : 'fail' ,
248
283
min : 0 ,
249
- max : dataForRender ?. maxFailCount ,
284
+ max : maxCount . fail ,
250
285
calculable : true ,
251
286
seriesIndex : 1 ,
252
287
orient : 'horizontal' ,
@@ -259,10 +294,15 @@ const HeatmapChartCore = React.forwardRef(
259
294
return Math . floor ( Number ( value ) ) . toLocaleString ( ) ;
260
295
} ,
261
296
inRange : {
262
- color : [ '#ffffff' , dataForRender ?. maxFailCount ? HeatmapColor . failed : '#ffffff' ] ,
297
+ color : HeatmapColor . fail ,
263
298
} ,
264
299
range : failRange ,
265
300
} ,
301
+ {
302
+ id : 'cover' ,
303
+ show : false ,
304
+ seriesIndex : 2 ,
305
+ } ,
266
306
] ,
267
307
graphic : [
268
308
{
@@ -276,11 +316,11 @@ const HeatmapChartCore = React.forwardRef(
276
316
rich : {
277
317
boldSuccess : {
278
318
fontWeight : 'bold' ,
279
- fill : HeatmapColor . success ,
319
+ fill : HeatmapColor . success [ 5 ] ,
280
320
} ,
281
321
boldFailed : {
282
322
fontWeight : 'bold' ,
283
- fill : HeatmapColor . failed ,
323
+ fill : HeatmapColor . fail [ 5 ] ,
284
324
} ,
285
325
} ,
286
326
} ,
@@ -293,12 +333,14 @@ const HeatmapChartCore = React.forwardRef(
293
333
data : dataForRender ?. successData ,
294
334
} ,
295
335
{
296
- name : 'failed ' ,
336
+ name : 'fail ' ,
297
337
type : 'heatmap' ,
298
338
data : dataForRender ?. failData ,
299
- itemStyle : {
300
- opacity : 0.5 ,
301
- } ,
339
+ } ,
340
+ {
341
+ name : 'cover' ,
342
+ type : 'heatmap' ,
343
+ data : dataForRender ?. coverData ,
302
344
emphasis : {
303
345
itemStyle : {
304
346
borderColor : '#333' ,
@@ -316,36 +358,36 @@ const HeatmapChartCore = React.forwardRef(
316
358
echarts = { echarts }
317
359
option = { option }
318
360
style = { { height : '100%' , width : '100%' } }
319
- onEvents = { {
320
- mousedown : ( params : any , echartsInstance : ECharts ) => {
321
- console . log ( 'mousedown' , params ) ;
322
- setStartCell ( `${ params . value [ 0 ] } -${ params . value [ 1 ] } ` ) ;
323
- setEndCell ( `${ params . value [ 0 ] } -${ params . value [ 1 ] } ` ) ;
324
- } ,
325
- mousemove : ( params : any ) => {
326
- if ( ! startCell ) {
327
- return ;
328
- }
329
- setEndCell ( `${ params . value [ 0 ] } -${ params . value [ 1 ] } ` ) ;
330
- } ,
331
- mouseup : ( params : any ) => {
332
- console . log ( 'mouseup' , params , startCell , endCell ) ;
333
- setStartCell ( '' ) ;
334
- setEndCell ( '' ) ;
335
- } ,
336
- datarangeselected : debounce ( ( params : any ) => {
337
- if ( params . visualMapId === 'success' ) {
338
- setSuccessRange ( params . selected ) ;
339
- } else if ( params . visualMapId === 'fail' ) {
340
- setFailRange ( params . selected ) ;
341
- }
342
- } , 300 ) ,
343
- // click: (params: any, echartsInstance: ECharts) => {
344
- // console.log('click', params);
345
- // setStartCell(`${params.value[0]}-${params.value[1]}`);
346
- // // setRange([1000, 3000]);
347
- // },
348
- } }
361
+ // onEvents={{
362
+ // mousedown: (params: any, echartsInstance: ECharts) => {
363
+ // console.log('mousedown', params);
364
+ // setStartCell(`${params.value[0]}-${params.value[1]}`);
365
+ // setEndCell(`${params.value[0]}-${params.value[1]}`);
366
+ // },
367
+ // mousemove: (params: any) => {
368
+ // if (!startCell) {
369
+ // return;
370
+ // }
371
+ // setEndCell(`${params.value[0]}-${params.value[1]}`);
372
+ // },
373
+ // mouseup: (params: any) => {
374
+ // console.log('mouseup', params, startCell, endCell);
375
+ // setStartCell('');
376
+ // setEndCell('');
377
+ // },
378
+ // datarangeselected: debounce((params: any) => {
379
+ // if (params.visualMapId === 'success') {
380
+ // setSuccessRange(params.selected);
381
+ // } else if (params.visualMapId === 'fail') {
382
+ // setFailRange(params.selected);
383
+ // }
384
+ // }, 300),
385
+ // // click: (params: any, echartsInstance: ECharts) => {
386
+ // // console.log('click', params);
387
+ // // setStartCell(`${params.value[0]}-${params.value[1]}`);
388
+ // // // setRange([1000, 3000]);
389
+ // // },
390
+ // }}
349
391
/>
350
392
</ div >
351
393
) ;
0 commit comments