1
- import React , { useEffect , useRef } from 'react'
1
+ import React , { useCallback , useEffect , useRef } from 'react'
2
2
import cx from 'classnames'
3
3
import { EuiTextColor } from '@elastic/eui'
4
- import { CellMeasurer , List , CellMeasurerCache , ListRowProps } from 'react-virtualized '
4
+ import { ListChildComponentProps , ListOnScrollProps , VariableSizeList as List } from 'react-window '
5
5
6
6
import { DEFAULT_ERROR_MESSAGE , getFormatTime } from 'uiSrc/utils'
7
7
8
8
import styles from 'uiSrc/components/monitor/Monitor/styles.module.scss'
9
- import 'react-virtualized/styles.css'
10
9
11
10
export interface Props {
12
11
compressed : boolean
@@ -18,28 +17,67 @@ export interface Props {
18
17
const PROTRUDING_OFFSET = 2
19
18
const MIDDLE_SCREEN_RESOLUTION = 460
20
19
const SMALL_SCREEN_RESOLUTION = 360
20
+ const MIN_ROW_HEIGHT = 17
21
21
22
22
const MonitorOutputList = ( props : Props ) => {
23
23
const { compressed, items = [ ] , width = 0 , height = 0 } = props
24
24
25
- const cache = new CellMeasurerCache ( {
26
- defaultHeight : 17 ,
27
- fixedWidth : true ,
28
- fixedHeight : false
29
- } )
30
-
25
+ const autoScrollRef = useRef < boolean > ( true )
26
+ const rowHeights = useRef < { [ key : number ] : number } > ( { } )
27
+ const outerRef = useRef < HTMLDivElement > ( null )
31
28
const listRef = useRef < List > ( null )
29
+ const hasMountedRef = useRef < boolean > ( false )
30
+
31
+ useEffect ( ( ) => {
32
+ if ( autoScrollRef . current ) {
33
+ setTimeout ( ( ) => {
34
+ scrollToBottom ( )
35
+ } , 0 )
36
+ }
37
+ } , [ items ] )
38
+
39
+ const getRowHeight = ( index : number ) => (
40
+ rowHeights . current [ index ] > MIN_ROW_HEIGHT ? ( rowHeights . current [ index ] + 2 ) : MIN_ROW_HEIGHT
41
+ )
42
+
43
+ const setRowHeight = ( index : number , size : number ) => {
44
+ listRef . current ?. resetAfterIndex ( 0 )
45
+ if ( size > MIN_ROW_HEIGHT ) {
46
+ rowHeights . current [ index ] = size
47
+ return
48
+ }
49
+
50
+ if ( rowHeights . current [ index ] ) {
51
+ delete rowHeights . current [ index ]
52
+ }
53
+ }
32
54
33
- const clearCacheAndUpdate = ( ) => {
34
- listRef ? .current ?. scrollToRow ( items . length - 1 )
55
+ const scrollToBottom = ( ) => {
56
+ listRef . current ?. scrollToItem ( items . length - 1 , 'end' )
35
57
requestAnimationFrame ( ( ) => {
36
- listRef ? .current ?. scrollToRow ( items . length - 1 )
58
+ listRef . current ?. scrollToItem ( items . length - 1 , 'end' )
37
59
} )
38
60
}
39
61
40
- useEffect ( ( ) => {
41
- clearCacheAndUpdate ( )
42
- } , [ items ] )
62
+ const handleScroll = useCallback ( ( e : ListOnScrollProps ) => {
63
+ if ( ! hasMountedRef . current ) {
64
+ hasMountedRef . current = true
65
+ return
66
+ }
67
+
68
+ if ( ! outerRef . current ) {
69
+ return
70
+ }
71
+
72
+ if ( e . scrollOffset + outerRef . current . offsetHeight === outerRef . current . scrollHeight ) {
73
+ autoScrollRef . current = true
74
+ return
75
+ }
76
+
77
+ if ( ! e . scrollUpdateWasRequested ) {
78
+ autoScrollRef . current = false
79
+ }
80
+ } , [ ] )
43
81
44
82
const getArgs = ( args : string [ ] ) : JSX . Element => (
45
83
< span className = { cx ( styles . itemArgs , { [ styles . itemArgs__compressed ] : compressed } ) } >
@@ -54,51 +92,47 @@ const MonitorOutputList = (props: Props) => {
54
92
</ span >
55
93
)
56
94
57
- const rowRenderer = ( { parent , index, key , style } : ListRowProps ) => {
95
+ const Row = ( { index, style } : ListChildComponentProps ) => {
58
96
const { time = '' , args = [ ] , database = '' , source = '' , isError, message = '' } = items [ index ]
97
+ const rowRef = useRef < HTMLDivElement > ( null )
98
+
99
+ useEffect ( ( ) => {
100
+ if ( ! rowRef . current ) return
101
+ setRowHeight ( index , rowRef . current ?. clientHeight )
102
+ } , [ rowRef ] )
103
+
59
104
return (
60
- < CellMeasurer
61
- cache = { cache }
62
- columnIndex = { 0 }
63
- key = { key }
64
- parent = { parent }
65
- rowIndex = { index }
66
- >
67
- { ( { registerChild, measure } ) => (
68
- < div onLoad = { measure } className = { styles . item } ref = { registerChild } style = { style } >
69
- { ! isError && (
70
- < >
71
- { width > MIDDLE_SCREEN_RESOLUTION && (
72
- < span className = { cx ( styles . time ) } >
73
- { getFormatTime ( time ) }
74
-
75
- </ span >
76
- ) }
77
- { width > SMALL_SCREEN_RESOLUTION && ( < span > { `[${ database } ${ source } ] ` } </ span > ) }
78
- < span > { getArgs ( args ) } </ span >
79
- </ >
80
- ) }
81
- { isError && (
82
- < EuiTextColor color = "danger" > { message ?? DEFAULT_ERROR_MESSAGE } </ EuiTextColor >
105
+ < div style = { style } className = { styles . item } data-testid = { `row-${ index } ` } >
106
+ { ! isError && (
107
+ < div ref = { rowRef } >
108
+ { width > MIDDLE_SCREEN_RESOLUTION && (
109
+ < span className = { cx ( styles . time ) } > { getFormatTime ( time ) } </ span >
83
110
) }
111
+ { width > SMALL_SCREEN_RESOLUTION && ( < span > { `[${ database } ${ source } ] ` } </ span > ) }
112
+ < span > { getArgs ( args ) } </ span >
84
113
</ div >
85
114
) }
86
- </ CellMeasurer >
115
+ { isError && (
116
+ < EuiTextColor color = "danger" > { message ?? DEFAULT_ERROR_MESSAGE } </ EuiTextColor >
117
+ ) }
118
+ </ div >
87
119
)
88
120
}
89
121
90
122
return (
91
123
< List
124
+ height = { height }
125
+ itemCount = { items . length }
126
+ itemSize = { getRowHeight }
92
127
ref = { listRef }
93
128
width = { width - PROTRUDING_OFFSET }
94
- height = { height - PROTRUDING_OFFSET }
95
- rowCount = { items . length }
96
- rowHeight = { cache . rowHeight }
97
- rowRenderer = { rowRenderer }
98
- overscanRowCount = { 30 }
99
129
className = { styles . listWrapper }
100
- deferredMeasurementCache = { cache }
101
- />
130
+ outerRef = { outerRef }
131
+ onScroll = { handleScroll }
132
+ overscanCount = { 30 }
133
+ >
134
+ { Row }
135
+ </ List >
102
136
)
103
137
}
104
138
0 commit comments