1
1
import * as React from '@theia/core/shared/react' ;
2
- import { Installable } from '../../../common/protocol/installable' ;
2
+ import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer' ;
3
+ import {
4
+ CellMeasurer ,
5
+ CellMeasurerCache ,
6
+ } from 'react-virtualized/dist/commonjs/CellMeasurer' ;
7
+ import type {
8
+ ListRowProps ,
9
+ ListRowRenderer ,
10
+ } from 'react-virtualized/dist/commonjs/List' ;
11
+ import List from 'react-virtualized/dist/commonjs/List' ;
3
12
import { ArduinoComponent } from '../../../common/protocol/arduino-component' ;
13
+ import { Installable } from '../../../common/protocol/installable' ;
4
14
import { ComponentListItem } from './component-list-item' ;
5
15
import { ListItemRenderer } from './list-item-renderer' ;
6
16
7
17
export class ComponentList < T extends ArduinoComponent > extends React . Component <
8
- ComponentList . Props < T >
18
+ ComponentList . Props < T > ,
19
+ ComponentList . State
9
20
> {
10
- protected container ?: HTMLElement ;
21
+ private readonly cache : CellMeasurerCache ;
22
+ private resizeAllFlag : boolean ;
23
+ private list : List | undefined ;
24
+ private mostRecentWidth : number | undefined ;
25
+
26
+ constructor ( props : ComponentList . Props < T > ) {
27
+ super ( props ) ;
28
+ this . state = { focusIndex : 'none' } ;
29
+ this . cache = new CellMeasurerCache ( {
30
+ defaultHeight : 200 ,
31
+ fixedWidth : true ,
32
+ } ) ;
33
+ }
11
34
12
35
override render ( ) : React . ReactNode {
13
36
return (
14
- < div className = { 'items-container' } ref = { this . setRef } >
15
- { this . props . items . map ( ( item ) => this . createItem ( item ) ) }
16
- </ div >
37
+ < AutoSizer >
38
+ { ( { width, height } ) => {
39
+ if ( this . mostRecentWidth && this . mostRecentWidth !== width ) {
40
+ this . resizeAllFlag = true ;
41
+ setTimeout ( this . clearAll , 0 ) ;
42
+ }
43
+ this . mostRecentWidth = width ;
44
+ return (
45
+ < List
46
+ rowRenderer = { this . createItem }
47
+ overscanRowCount = { 100 }
48
+ height = { height }
49
+ width = { width }
50
+ rowCount = { this . props . items . length }
51
+ rowHeight = { this . cache . rowHeight }
52
+ deferredMeasurementCache = { this . cache }
53
+ ref = { this . setListRef }
54
+ />
55
+ ) ;
56
+ } }
57
+ </ AutoSizer >
17
58
) ;
18
59
}
19
60
20
- override componentDidMount ( ) : void {
21
- if ( this . container && this . props . resolveContainer ) {
22
- this . props . resolveContainer ( this . container ) ;
61
+ override componentDidUpdate (
62
+ prevProps : ComponentList . Props < T > ,
63
+ prevState : ComponentList . State
64
+ ) : void {
65
+ if ( this . resizeAllFlag || this . props . items !== prevProps . items ) {
66
+ this . clearAll ( ) ;
67
+ } else if ( this . state . focusIndex !== prevState . focusIndex ) {
68
+ if ( typeof this . state . focusIndex === 'number' ) {
69
+ this . clear ( this . state . focusIndex ) ;
70
+ }
71
+ if ( typeof prevState . focusIndex === 'number' ) {
72
+ this . clear ( prevState . focusIndex ) ;
73
+ }
23
74
}
24
75
}
25
76
26
- protected setRef = ( element : HTMLElement | null ) => {
27
- this . container = element || undefined ;
77
+ private setListRef = ( ref : List | null ) : void => {
78
+ this . list = ref || undefined ;
28
79
} ;
29
80
30
- protected createItem ( item : T ) : React . ReactNode {
81
+ private clearAll ( ) : void {
82
+ this . resizeAllFlag = false ;
83
+ this . cache . clearAll ( ) ;
84
+ if ( this . list ) {
85
+ this . list . recomputeRowHeights ( ) ;
86
+ }
87
+ }
88
+
89
+ private clear ( index : number ) : void {
90
+ this . cache . clear ( index , 0 ) ;
91
+ if ( this . list ) {
92
+ this . list . recomputeRowHeights ( index ) ;
93
+ }
94
+ }
95
+
96
+ private createItem : ListRowRenderer = ( {
97
+ index,
98
+ parent,
99
+ key,
100
+ style,
101
+ } : ListRowProps ) : React . ReactNode => {
102
+ const item = this . props . items [ index ] ;
31
103
return (
32
- < ComponentListItem < T >
33
- key = { this . props . itemLabel ( item ) }
34
- item = { item }
35
- itemRenderer = { this . props . itemRenderer }
36
- install = { this . props . install }
37
- uninstall = { this . props . uninstall }
38
- />
104
+ < CellMeasurer
105
+ cache = { this . cache }
106
+ columnIndex = { 0 }
107
+ key = { key }
108
+ rowIndex = { index }
109
+ parent = { parent }
110
+ >
111
+ < div
112
+ style = { style }
113
+ onMouseEnter = { ( ) => this . setState ( { focusIndex : index } ) }
114
+ onMouseLeave = { ( ) => this . setState ( { focusIndex : 'none' } ) }
115
+ >
116
+ < ComponentListItem < T >
117
+ key = { this . props . itemLabel ( item ) }
118
+ item = { item }
119
+ itemRenderer = { this . props . itemRenderer }
120
+ install = { this . props . install }
121
+ uninstall = { this . props . uninstall }
122
+ />
123
+ </ div >
124
+ </ CellMeasurer >
39
125
) ;
40
- }
126
+ } ;
41
127
}
42
128
43
129
export namespace ComponentList {
@@ -48,6 +134,8 @@ export namespace ComponentList {
48
134
readonly itemRenderer : ListItemRenderer < T > ;
49
135
readonly install : ( item : T , version ?: Installable . Version ) => Promise < void > ;
50
136
readonly uninstall : ( item : T ) => Promise < void > ;
51
- readonly resolveContainer : ( element : HTMLElement ) => void ;
137
+ }
138
+ export interface State {
139
+ focusIndex : number | 'none' ;
52
140
}
53
141
}
0 commit comments