@@ -13,8 +13,13 @@ import {
13
13
ReactWidget ,
14
14
caretDownIcon
15
15
} from '@jupyterlab/ui-components' ;
16
+ import { Message } from '@lumino/messaging' ;
16
17
import { Panel } from '@lumino/widgets' ;
17
- import React , { MouseEvent , useEffect , useState } from 'react' ;
18
+ import React , {
19
+ MouseEvent as ReactMouseEvent ,
20
+ useEffect ,
21
+ useState
22
+ } from 'react' ;
18
23
import { icons } from '../../constants' ;
19
24
import { nonVisibilityIcon , visibilityIcon } from '../../icons' ;
20
25
import { IControlPanelModel } from '../../types' ;
@@ -44,7 +49,7 @@ export namespace LayersPanel {
44
49
type : SelectionType ;
45
50
item : string ;
46
51
nodeId ?: string ;
47
- event : MouseEvent ;
52
+ event : ReactMouseEvent ;
48
53
}
49
54
}
50
55
@@ -55,6 +60,7 @@ export class LayersPanel extends Panel {
55
60
constructor ( options : LayersPanel . IOptions ) {
56
61
super ( ) ;
57
62
this . _model = options . model ;
63
+ this . _lastSelectedNodeId = '' ;
58
64
this . id = 'jupytergis::layerTree' ;
59
65
this . addClass ( LAYERS_PANEL_CLASS ) ;
60
66
@@ -68,6 +74,38 @@ export class LayersPanel extends Panel {
68
74
) ;
69
75
}
70
76
77
+ protected onAfterAttach ( msg : Message ) : void {
78
+ super . onAfterAttach ( msg ) ;
79
+ const node = this . node ;
80
+ node . addEventListener ( 'mouseup' , this ) ;
81
+ }
82
+
83
+ protected onBeforeDetach ( msg : Message ) : void {
84
+ super . onBeforeDetach ( msg ) ;
85
+ const node = this . node ;
86
+ node . removeEventListener ( 'mouseup' , this ) ;
87
+ }
88
+
89
+ handleEvent ( event : Event ) : void {
90
+ switch ( event . type ) {
91
+ case 'mouseup' :
92
+ this . _mouseUpEvent ( event as MouseEvent ) ;
93
+ break ;
94
+ default :
95
+ break ;
96
+ }
97
+ }
98
+
99
+ private _mouseUpEvent ( event : MouseEvent ) : void {
100
+ // If we click on empty space in the layer panel, keep the focus on the last selected element
101
+ const node = document . getElementById ( this . _lastSelectedNodeId ) ;
102
+ if ( ! node ) {
103
+ return ;
104
+ }
105
+
106
+ node . focus ( ) ;
107
+ }
108
+
71
109
/**
72
110
* Function to call when a layer is selected from a component of the panel.
73
111
*
@@ -79,12 +117,20 @@ export class LayersPanel extends Panel {
79
117
nodeId,
80
118
event
81
119
} : LayersPanel . IClickHandlerParams ) => {
82
- if ( ! this . _model ) {
120
+ if ( ! this . _model || ! nodeId ) {
83
121
return ;
84
122
}
85
123
86
124
const { jGISModel } = this . _model ;
87
125
const selectedValue = jGISModel ?. localState ?. selected ?. value ;
126
+ const node = document . getElementById ( nodeId ) ;
127
+
128
+ if ( ! node ) {
129
+ return ;
130
+ }
131
+
132
+ node . tabIndex = 0 ;
133
+ node . focus ( ) ;
88
134
89
135
// Early return if no selection exists
90
136
if ( ! selectedValue ) {
@@ -120,6 +166,7 @@ export class LayersPanel extends Panel {
120
166
...selectedValue ,
121
167
[ item ] : { type, selectedNodeId : nodeId }
122
168
} ;
169
+ this . _lastSelectedNodeId = nodeId ;
123
170
124
171
jGISModel . syncSelected ( updatedSelectedValue , this . id ) ;
125
172
}
@@ -132,11 +179,13 @@ export class LayersPanel extends Panel {
132
179
type,
133
180
selectedNodeId : nodeId
134
181
} ;
182
+ this . _lastSelectedNodeId = nodeId ;
135
183
}
136
184
this . _model ?. jGISModel ?. syncSelected ( selection , this . id ) ;
137
185
}
138
186
139
187
private _model : IControlPanelModel | undefined ;
188
+ private _lastSelectedNodeId : string ;
140
189
}
141
190
142
191
/**
@@ -179,12 +228,10 @@ function LayersBodyComponent(props: IBodyProps): JSX.Element {
179
228
} ;
180
229
model ?. sharedModel . layersChanged . connect ( updateLayers ) ;
181
230
model ?. sharedModel . layerTreeChanged . connect ( updateLayers ) ;
182
- model ?. clientStateChanged . connect ( updateLayers ) ;
183
231
184
232
return ( ) => {
185
233
model ?. sharedModel . layersChanged . disconnect ( updateLayers ) ;
186
234
model ?. sharedModel . layerTreeChanged . disconnect ( updateLayers ) ;
187
- model ?. clientStateChanged . disconnect ( updateLayers ) ;
188
235
} ;
189
236
} , [ model ] ) ;
190
237
@@ -240,12 +287,31 @@ function LayerGroupComponent(props: ILayerGroupProps): JSX.Element {
240
287
const [ open , setOpen ] = useState < boolean > ( false ) ;
241
288
const name = group ?. name ?? 'Undefined group' ;
242
289
const layers = group ?. layers ?? [ ] ;
290
+ const [ selected , setSelected ] = useState < boolean > (
291
+ // TODO Support multi-selection as `model?.jGISModel?.localState?.selected.value` does
292
+ isSelected ( group . name , gisModel )
293
+ ) ;
243
294
244
295
useEffect ( ( ) => {
245
296
setId ( DOMUtils . createDomID ( ) ) ;
246
297
} , [ ] ) ;
247
298
248
- const handleRightClick = ( event : MouseEvent < HTMLElement > ) => {
299
+ /**
300
+ * Listen to the changes on the current layer.
301
+ */
302
+ useEffect ( ( ) => {
303
+ const onClientSharedStateChanged = ( ) => {
304
+ // TODO Support follow mode and remoteUser state
305
+ setSelected ( isSelected ( group . name , gisModel ) ) ;
306
+ } ;
307
+ gisModel ?. clientStateChanged . connect ( onClientSharedStateChanged ) ;
308
+
309
+ return ( ) => {
310
+ gisModel ?. clientStateChanged . disconnect ( onClientSharedStateChanged ) ;
311
+ } ;
312
+ } , [ gisModel ] ) ;
313
+
314
+ const handleRightClick = ( event : ReactMouseEvent < HTMLElement > ) => {
249
315
const childId = event . currentTarget . children . namedItem ( id ) ?. id ;
250
316
onClick ( { type : 'group' , item : name , nodeId : childId , event } ) ;
251
317
} ;
@@ -255,7 +321,7 @@ function LayerGroupComponent(props: ILayerGroupProps): JSX.Element {
255
321
< div
256
322
onClick = { ( ) => setOpen ( ! open ) }
257
323
onContextMenu = { handleRightClick }
258
- className = { LAYER_GROUP_HEADER_CLASS }
324
+ className = { ` ${ LAYER_GROUP_HEADER_CLASS } ${ selected ? ' jp-mod-selected' : '' } ` }
259
325
>
260
326
< LabIcon . resolveReact
261
327
icon = { caretDownIcon }
@@ -355,7 +421,7 @@ function LayerComponent(props: ILayerProps): JSX.Element {
355
421
gisModel ?. sharedModel ?. updateLayer ( layerId , layer ) ;
356
422
} ;
357
423
358
- const setSelection = ( event : MouseEvent < HTMLElement > ) => {
424
+ const setSelection = ( event : ReactMouseEvent < HTMLElement > ) => {
359
425
const childId = event . currentTarget . children . namedItem ( id ) ?. id ;
360
426
onClick ( { type : 'layer' , item : layerId , nodeId : childId , event } ) ;
361
427
} ;
0 commit comments