1
1
import * as React from 'react' ;
2
- import { BoardsService , Board } from '../../common/protocol/boards-service' ;
2
+ import { BoardsService , Board , AttachedSerialBoard } from '../../common/protocol/boards-service' ;
3
3
import { ContextMenuRenderer } from '@theia/core/lib/browser' ;
4
- import { ArduinoToolbarContextMenu } from '../arduino-file-menu' ;
5
4
import { BoardsNotificationService } from '../boards-notification-service' ;
5
+ import { Command , CommandRegistry } from '@theia/core' ;
6
+ import { ArduinoCommands } from '../arduino-commands' ;
7
+ import ReactDOM = require( 'react-dom' ) ;
8
+
9
+ export interface BoardsDropdownItem {
10
+ label : string ;
11
+ commandExecutor : ( ) => void ;
12
+ isSelected : ( ) => boolean ;
13
+ }
14
+
15
+ export interface BoardsDropDownListCoord {
16
+ top : number ;
17
+ left : number ;
18
+ width : number ;
19
+ paddingTop : number ;
20
+ }
21
+
22
+ export namespace BoardsDropdownItemComponent {
23
+ export interface Props {
24
+ label : string ;
25
+ onClick : ( ) => void ;
26
+ isSelected : boolean ;
27
+ }
28
+ }
29
+
30
+ export class BoardsDropdownItemComponent extends React . Component < BoardsDropdownItemComponent . Props > {
31
+ render ( ) {
32
+ return < div className = { `arduino-boards-dropdown-item ${ this . props . isSelected ? 'selected' : '' } ` } onClick = { this . props . onClick } >
33
+ < div > { this . props . label } </ div >
34
+ { this . props . isSelected ? < span className = 'fa fa-check' > </ span > : '' }
35
+ </ div > ;
36
+ }
37
+ }
38
+
39
+ export namespace BoardsDropDown {
40
+ export interface Props {
41
+ readonly coords : BoardsDropDownListCoord ;
42
+ readonly isOpen : boolean ;
43
+ readonly dropDownItems : BoardsDropdownItem [ ] ;
44
+ readonly openDialog : ( ) => void ;
45
+ }
46
+ }
47
+
48
+ export class BoardsDropDown extends React . Component < BoardsDropDown . Props > {
49
+ protected dropdownId : string = 'boards-dropdown-container' ;
50
+ protected dropdownElement : HTMLElement ;
51
+
52
+ constructor ( props : BoardsDropDown . Props ) {
53
+ super ( props ) ;
54
+
55
+ let list = document . getElementById ( this . dropdownId ) ;
56
+ if ( ! list ) {
57
+ list = document . createElement ( 'div' ) ;
58
+ list . id = this . dropdownId ;
59
+ document . body . appendChild ( list ) ;
60
+ this . dropdownElement = list ;
61
+ }
62
+ }
63
+
64
+ render ( ) : React . ReactNode {
65
+ return ReactDOM . createPortal ( this . renderNode ( ) , this . dropdownElement ) ;
66
+ }
67
+
68
+ renderNode ( ) : React . ReactNode {
69
+ if ( this . props . isOpen ) {
70
+ return < div className = 'arduino-boards-dropdown-list'
71
+ style = { {
72
+ position : 'absolute' ,
73
+ top : this . props . coords . top ,
74
+ left : this . props . coords . left ,
75
+ width : this . props . coords . width ,
76
+ paddingTop : this . props . coords . paddingTop
77
+ } } >
78
+ {
79
+ this . props . dropDownItems . map ( item => {
80
+ return < React . Fragment key = { item . label } >
81
+ < BoardsDropdownItemComponent isSelected = { item . isSelected ( ) } label = { item . label } onClick = { item . commandExecutor } > </ BoardsDropdownItemComponent >
82
+ </ React . Fragment > ;
83
+ } )
84
+ }
85
+ < BoardsDropdownItemComponent isSelected = { false } label = { 'Select Other Board & Port' } onClick = { this . props . openDialog } > </ BoardsDropdownItemComponent >
86
+ </ div >
87
+ } else {
88
+ return '' ;
89
+ }
90
+ }
91
+ }
6
92
7
93
export namespace BoardsToolBarItem {
8
94
export interface Props {
9
95
readonly contextMenuRenderer : ContextMenuRenderer ;
10
96
readonly boardsNotificationService : BoardsNotificationService ;
11
97
readonly boardService : BoardsService ;
98
+ readonly commands : CommandRegistry ;
12
99
}
13
100
14
101
export interface State {
15
102
selectedBoard ?: Board ;
16
- selectedIsAttached : boolean
103
+ selectedIsAttached : boolean ;
104
+ boardItems : BoardsDropdownItem [ ] ;
105
+ isOpen : boolean ;
17
106
}
18
107
}
19
108
20
109
export class BoardsToolBarItem extends React . Component < BoardsToolBarItem . Props , BoardsToolBarItem . State > {
21
110
22
111
protected attachedBoards : Board [ ] ;
112
+ protected dropDownListCoord : BoardsDropDownListCoord ;
23
113
24
114
constructor ( props : BoardsToolBarItem . Props ) {
25
115
super ( props ) ;
26
116
27
117
this . state = {
28
118
selectedBoard : undefined ,
29
- selectedIsAttached : true
119
+ selectedIsAttached : true ,
120
+ boardItems : [ ] ,
121
+ isOpen : false
30
122
} ;
123
+
124
+ document . addEventListener ( 'click' , ( ) => {
125
+ this . setState ( { isOpen : false } ) ;
126
+ } ) ;
31
127
}
32
128
33
129
componentDidMount ( ) {
34
130
this . setAttachedBoards ( ) ;
35
131
}
36
132
133
+ setSelectedBoard ( board : Board ) {
134
+ if ( this . attachedBoards && this . attachedBoards . length ) {
135
+ this . setState ( { selectedIsAttached : ! ! this . attachedBoards . find ( attachedBoard => attachedBoard . name === board . name ) } ) ;
136
+ }
137
+ this . setState ( { selectedBoard : board } ) ;
138
+ }
139
+
37
140
protected async setAttachedBoards ( ) {
38
141
const { boards } = await this . props . boardService . getAttachedBoards ( ) ;
39
142
this . attachedBoards = boards ;
40
143
if ( this . attachedBoards . length ) {
144
+ await this . createBoardDropdownItems ( ) ;
41
145
await this . props . boardService . selectBoard ( this . attachedBoards [ 0 ] ) ;
42
146
this . setSelectedBoard ( this . attachedBoards [ 0 ] ) ;
43
- }
147
+ }
44
148
}
45
149
46
- setSelectedBoard ( board : Board ) {
47
- if ( this . attachedBoards && this . attachedBoards . length ) {
48
- this . setState ( { selectedIsAttached : ! ! this . attachedBoards . find ( attachedBoard => attachedBoard . name === board . name ) } ) ;
150
+ protected createBoardDropdownItems ( ) {
151
+ const boardItems : BoardsDropdownItem [ ] = [ ] ;
152
+ this . attachedBoards . forEach ( board => {
153
+ const { commands } = this . props ;
154
+ const port = this . getPort ( board ) ;
155
+ const command : Command = {
156
+ id : 'selectBoard' + port
157
+ }
158
+ commands . registerCommand ( command , {
159
+ execute : ( ) => {
160
+ commands . executeCommand ( ArduinoCommands . SELECT_BOARD . id , board ) ;
161
+ this . setState ( { isOpen : false , selectedBoard : board } ) ;
162
+ }
163
+ } ) ;
164
+ boardItems . push ( {
165
+ commandExecutor : ( ) => commands . executeCommand ( command . id ) ,
166
+ label : board . name + ' at ' + port ,
167
+ isSelected : ( ) => this . doIsSelectedBoard ( board )
168
+ } ) ;
169
+ } ) ;
170
+ this . setState ( { boardItems } ) ;
171
+ }
172
+
173
+ protected doIsSelectedBoard = ( board : Board ) => this . isSelectedBoard ( board ) ;
174
+ protected isSelectedBoard ( board : Board ) : boolean {
175
+ return AttachedSerialBoard . is ( board ) &&
176
+ ! ! this . state . selectedBoard &&
177
+ AttachedSerialBoard . is ( this . state . selectedBoard ) &&
178
+ board . port === this . state . selectedBoard . port &&
179
+ board . fqbn === this . state . selectedBoard . fqbn ;
180
+ }
181
+
182
+ protected getPort ( board : Board ) : string {
183
+ if ( AttachedSerialBoard . is ( board ) ) {
184
+ return board . port ;
49
185
}
50
- this . setState ( { selectedBoard : board } ) ;
186
+ return '' ;
51
187
}
52
188
53
- protected readonly doShowSelectBoardsMenu = ( event : React . MouseEvent < HTMLElement > ) => this . showSelectBoardsMenu ( event ) ;
189
+ protected readonly doShowSelectBoardsMenu = ( event : React . MouseEvent < HTMLElement > ) => {
190
+ this . showSelectBoardsMenu ( event ) ;
191
+ event . stopPropagation ( ) ;
192
+ event . nativeEvent . stopImmediatePropagation ( ) ;
193
+ } ;
54
194
protected showSelectBoardsMenu ( event : React . MouseEvent < HTMLElement > ) {
55
- const el = ( event . target as HTMLElement ) . parentElement ;
195
+ const el = ( event . currentTarget as HTMLElement ) ;
56
196
if ( el ) {
57
- this . props . contextMenuRenderer . render ( {
58
- menuPath : ArduinoToolbarContextMenu . SELECT_BOARDS_PATH ,
59
- anchor : {
60
- x : el . getBoundingClientRect ( ) . left ,
61
- y : el . getBoundingClientRect ( ) . top + el . offsetHeight
62
- }
63
- } )
197
+ this . dropDownListCoord = {
198
+ top : el . getBoundingClientRect ( ) . top ,
199
+ left : el . getBoundingClientRect ( ) . left ,
200
+ paddingTop : el . getBoundingClientRect ( ) . height ,
201
+ width : el . getBoundingClientRect ( ) . width
202
+ }
203
+ this . setState ( { isOpen : ! this . state . isOpen } ) ;
64
204
}
65
205
}
66
206
67
207
render ( ) : React . ReactNode {
208
+ const selectedBoard = this . state . selectedBoard ;
209
+ const port = selectedBoard ? this . getPort ( selectedBoard ) : undefined ;
68
210
return < React . Fragment >
69
- < div className = 'arduino-boards-toolbar-item-container' onClick = { this . doShowSelectBoardsMenu } >
70
- < div className = 'arduino-boards-toolbar-item' >
71
- < div className = 'inner-container' >
72
- < span className = { ! this . state . selectedBoard || ! this . state . selectedIsAttached ? 'fa fa-times notAttached' : '' } > </ span >
73
- < div className = 'label' > { this . state . selectedBoard ? this . state . selectedBoard . name : 'no board selected' } </ div >
74
- < span className = 'fa fa-caret-down' > </ span >
211
+ < div className = 'arduino-boards-toolbar-item-container' >
212
+ < div className = 'arduino-boards-toolbar-item' title = { selectedBoard && `${ selectedBoard . name } ${ port ? ' at ' + port : '' } ` } >
213
+ < div className = 'inner-container' onClick = { this . doShowSelectBoardsMenu } >
214
+ < span className = { ! selectedBoard || ! this . state . selectedIsAttached ? 'fa fa-times notAttached' : '' } > </ span >
215
+ < div className = 'label noWrapInfo' >
216
+ < div className = 'noWrapInfo' >
217
+ { selectedBoard ? `${ selectedBoard . name } ${ port ? ' at ' + port : '' } ` : 'no board selected' }
218
+ </ div >
219
+ </ div >
220
+ < span className = 'fa fa-caret-down caret' > </ span >
75
221
</ div >
76
222
</ div >
77
223
</ div >
224
+ < BoardsDropDown
225
+ isOpen = { this . state . isOpen }
226
+ coords = { this . dropDownListCoord }
227
+ dropDownItems = { this . state . boardItems }
228
+ openDialog = { this . openDialog } >
229
+ </ BoardsDropDown >
78
230
</ React . Fragment > ;
79
231
}
232
+
233
+ protected openDialog = ( ) => {
234
+ this . props . commands . executeCommand ( ArduinoCommands . OPEN_BOARDS_DIALOG . id ) ;
235
+ this . setState ( { isOpen : false } ) ;
236
+ } ;
80
237
}
0 commit comments