@apollo-annotation/jbrowse-plugin-apollo 0.3.7 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.js +2371 -1642
- package/dist/index.esm.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.development.js +2384 -1641
- package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.development.js +4387 -2952
- package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
- package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
- package/package.json +15 -15
- package/src/ApolloInternetAccount/model.ts +48 -13
- package/src/BackendDrivers/CollaborationServerDriver.ts +23 -2
- package/src/ChangeManager.ts +33 -13
- package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +64 -5
- package/src/FeatureDetailsWidget/TranscriptSequence.tsx +70 -73
- package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +33 -31
- package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +60 -72
- package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +50 -194
- package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +441 -180
- package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +53 -34
- package/src/LinearApolloDisplay/glyphs/Glyph.ts +7 -9
- package/src/LinearApolloDisplay/stateModel/base.ts +34 -43
- package/src/LinearApolloDisplay/stateModel/layouts.ts +3 -2
- package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +32 -261
- package/src/LinearApolloDisplay/stateModel/rendering.ts +43 -343
- package/src/LinearApolloReferenceSequenceDisplay/components/LinearApolloReferenceSequenceDisplay.tsx +87 -0
- package/src/LinearApolloReferenceSequenceDisplay/components/index.ts +1 -0
- package/src/LinearApolloReferenceSequenceDisplay/configSchema.ts +7 -0
- package/src/LinearApolloReferenceSequenceDisplay/index.ts +3 -0
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +227 -0
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/index.ts +25 -0
- package/src/LinearApolloReferenceSequenceDisplay/stateModel/rendering.ts +481 -0
- package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +95 -38
- package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +221 -201
- package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +12 -8
- package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +42 -4
- package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +4 -8
- package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +73 -97
- package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +49 -61
- package/src/TabularEditor/HybridGrid/Feature.tsx +16 -14
- package/src/TabularEditor/HybridGrid/HybridGrid.tsx +7 -5
- package/src/components/AddAssembly.tsx +1 -1
- package/src/components/AddAssemblyAliases.tsx +1 -1
- package/src/components/AddChildFeature.tsx +5 -2
- package/src/components/AddFeature.tsx +9 -3
- package/src/components/AddRefSeqAliases.tsx +9 -9
- package/src/components/CopyFeature.tsx +3 -1
- package/src/components/CreateApolloAnnotation.tsx +1 -0
- package/src/components/DeleteAssembly.tsx +1 -1
- package/src/components/EditZoomThresholdDialog.tsx +69 -0
- package/src/components/FilterFeatures.tsx +7 -7
- package/src/components/FilterTranscripts.tsx +6 -6
- package/src/components/ImportFeatures.tsx +1 -1
- package/src/components/ManageChecks.tsx +1 -1
- package/src/components/MergeTranscripts.tsx +12 -15
- package/src/components/OntologyTermMultiSelect.tsx +11 -11
- package/src/components/OpenLocalFile.tsx +11 -7
- package/src/components/ViewCheckResults.tsx +1 -1
- package/src/components/index.ts +1 -0
- package/src/config.ts +6 -0
- package/src/index.ts +42 -105
- package/src/makeDisplayComponent.tsx +0 -1
- package/src/menus/index.ts +1 -0
- package/src/{ApolloInternetAccount/addMenuItems.ts → menus/topLevelMenu.ts} +56 -47
- package/src/menus/topLevelMenuAdmin.ts +154 -0
- package/src/session/session.ts +162 -116
- package/src/util/annotationFeatureUtils.ts +15 -21
- package/src/util/displayUtils.ts +149 -0
- package/src/util/glyphUtils.ts +152 -0
- package/src/util/mouseEventsUtils.ts +32 -0
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { type AnnotationFeature } from '@apollo-annotation/mst'
|
|
2
2
|
import { type MenuItem } from '@jbrowse/core/ui'
|
|
3
|
-
import {
|
|
4
|
-
import { type Theme, alpha } from '@mui/material'
|
|
3
|
+
import { alpha } from '@mui/material'
|
|
5
4
|
|
|
6
|
-
import { AddChildFeature, CopyFeature, DeleteFeature } from '../../components'
|
|
7
|
-
import { type LinearApolloDisplay } from '../stateModel'
|
|
8
5
|
import {
|
|
9
|
-
type LinearApolloDisplayMouseEvents,
|
|
10
6
|
type MousePosition,
|
|
11
|
-
type
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
type MousePositionWithFeature,
|
|
8
|
+
getContextMenuItemsForFeature,
|
|
9
|
+
isMousePositionWithFeature,
|
|
10
|
+
isSelectedFeature,
|
|
11
|
+
} from '../../util'
|
|
12
|
+
import { type LinearApolloDisplay } from '../stateModel'
|
|
13
|
+
import { type LinearApolloDisplayMouseEvents } from '../stateModel/mouseEvents'
|
|
14
14
|
import { type LinearApolloDisplayRendering } from '../stateModel/rendering'
|
|
15
15
|
import { type CanvasMouseEvent } from '../types'
|
|
16
16
|
|
|
@@ -42,20 +42,6 @@ function drawBoxFill(
|
|
|
42
42
|
drawBox(ctx, x + 1, y + 1, width - 2, height - 2, color)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function drawBoxText(
|
|
46
|
-
ctx: CanvasRenderingContext2D,
|
|
47
|
-
x: number,
|
|
48
|
-
y: number,
|
|
49
|
-
width: number,
|
|
50
|
-
color: string,
|
|
51
|
-
text: string,
|
|
52
|
-
) {
|
|
53
|
-
ctx.fillStyle = color
|
|
54
|
-
const textStart = Math.max(x + 1, 0)
|
|
55
|
-
const textWidth = x - 1 + width - textStart
|
|
56
|
-
ctx.fillText(text, textStart, y + 11, textWidth)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
45
|
function draw(
|
|
60
46
|
ctx: CanvasRenderingContext2D,
|
|
61
47
|
feature: AnnotationFeature,
|
|
@@ -63,7 +49,7 @@ function draw(
|
|
|
63
49
|
stateModel: LinearApolloDisplayRendering,
|
|
64
50
|
displayedRegionIndex: number,
|
|
65
51
|
) {
|
|
66
|
-
const { apolloRowHeight: heightPx, lgv,
|
|
52
|
+
const { apolloRowHeight: heightPx, lgv, selectedFeature, theme } = stateModel
|
|
67
53
|
const { bpPerPx, displayedRegions, offsetPx } = lgv
|
|
68
54
|
const displayedRegion = displayedRegions[displayedRegionIndex]
|
|
69
55
|
const minX =
|
|
@@ -73,13 +59,11 @@ function draw(
|
|
|
73
59
|
regionNumber: displayedRegionIndex,
|
|
74
60
|
})?.offsetPx ?? 0) - offsetPx
|
|
75
61
|
const { reversed } = displayedRegion
|
|
76
|
-
const { apolloSelectedFeature } = session
|
|
77
62
|
const widthPx = feature.length / bpPerPx
|
|
78
63
|
const startPx = reversed ? minX - widthPx : minX
|
|
79
64
|
const top = row * heightPx
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
const textColor = getTextColor(theme, isSelected)
|
|
65
|
+
const backgroundColor = theme.palette.background.default
|
|
66
|
+
const textColor = theme.palette.text.primary
|
|
83
67
|
const featureBox: [number, number, number, number] = [
|
|
84
68
|
startPx,
|
|
85
69
|
top,
|
|
@@ -93,7 +77,9 @@ function draw(
|
|
|
93
77
|
}
|
|
94
78
|
|
|
95
79
|
drawBoxFill(ctx, startPx, top, widthPx, heightPx, backgroundColor)
|
|
96
|
-
|
|
80
|
+
if (isSelectedFeature(feature, selectedFeature)) {
|
|
81
|
+
drawHighlight(stateModel, ctx, feature, true)
|
|
82
|
+
}
|
|
97
83
|
}
|
|
98
84
|
|
|
99
85
|
function drawDragPreview(
|
|
@@ -119,22 +105,20 @@ function drawDragPreview(
|
|
|
119
105
|
const rectY = row * apolloRowHeight
|
|
120
106
|
const rectWidth = Math.abs(current.x - featureEdgePx)
|
|
121
107
|
const rectHeight = apolloRowHeight * rowCount
|
|
122
|
-
overlayCtx.strokeStyle = theme
|
|
108
|
+
overlayCtx.strokeStyle = theme.palette.info.main
|
|
123
109
|
overlayCtx.setLineDash([6])
|
|
124
110
|
overlayCtx.strokeRect(rectX, rectY, rectWidth, rectHeight)
|
|
125
|
-
overlayCtx.fillStyle = alpha(theme
|
|
111
|
+
overlayCtx.fillStyle = alpha(theme.palette.info.main, 0.2)
|
|
126
112
|
overlayCtx.fillRect(rectX, rectY, rectWidth, rectHeight)
|
|
127
113
|
}
|
|
128
114
|
|
|
129
|
-
function
|
|
130
|
-
stateModel:
|
|
115
|
+
function drawHighlight(
|
|
116
|
+
stateModel: LinearApolloDisplayRendering,
|
|
131
117
|
ctx: CanvasRenderingContext2D,
|
|
118
|
+
feature: AnnotationFeature,
|
|
119
|
+
selected = false,
|
|
132
120
|
) {
|
|
133
|
-
const {
|
|
134
|
-
if (!apolloHover) {
|
|
135
|
-
return
|
|
136
|
-
}
|
|
137
|
-
const { feature } = apolloHover
|
|
121
|
+
const { apolloRowHeight, lgv, theme } = stateModel
|
|
138
122
|
const position = stateModel.getFeatureLayoutPosition(feature)
|
|
139
123
|
if (!position) {
|
|
140
124
|
return
|
|
@@ -152,19 +136,32 @@ function drawHover(
|
|
|
152
136
|
})?.offsetPx ?? 0) - offsetPx
|
|
153
137
|
const top = layoutRow * apolloRowHeight
|
|
154
138
|
const widthPx = length / bpPerPx
|
|
155
|
-
ctx.fillStyle =
|
|
139
|
+
ctx.fillStyle = selected
|
|
140
|
+
? theme.palette.action.disabled
|
|
141
|
+
: theme.palette.action.focus
|
|
156
142
|
ctx.fillRect(startPx, top, widthPx, apolloRowHeight)
|
|
157
143
|
}
|
|
158
144
|
|
|
145
|
+
function drawHover(
|
|
146
|
+
stateModel: LinearApolloDisplay,
|
|
147
|
+
ctx: CanvasRenderingContext2D,
|
|
148
|
+
) {
|
|
149
|
+
const { hoveredFeature } = stateModel
|
|
150
|
+
if (!hoveredFeature) {
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
drawHighlight(stateModel, ctx, hoveredFeature.feature)
|
|
154
|
+
}
|
|
155
|
+
|
|
159
156
|
function drawTooltip(
|
|
160
157
|
display: LinearApolloDisplayMouseEvents,
|
|
161
158
|
context: CanvasRenderingContext2D,
|
|
162
159
|
): void {
|
|
163
|
-
const {
|
|
164
|
-
if (!
|
|
160
|
+
const { hoveredFeature, apolloRowHeight, lgv, theme } = display
|
|
161
|
+
if (!hoveredFeature) {
|
|
165
162
|
return
|
|
166
163
|
}
|
|
167
|
-
const { feature } =
|
|
164
|
+
const { feature } = hoveredFeature
|
|
168
165
|
const position = display.getFeatureLayoutPosition(feature)
|
|
169
166
|
if (!position) {
|
|
170
167
|
return
|
|
@@ -201,14 +198,14 @@ function drawTooltip(
|
|
|
201
198
|
const maxWidth = Math.max(...textWidth)
|
|
202
199
|
|
|
203
200
|
startPx = startPx + widthPx + 5
|
|
204
|
-
context.fillStyle = alpha(theme
|
|
201
|
+
context.fillStyle = alpha(theme.palette.text.primary, 0.7)
|
|
205
202
|
context.fillRect(startPx, top, maxWidth + 4, textWidth.length === 3 ? 45 : 35)
|
|
206
203
|
context.beginPath()
|
|
207
204
|
context.moveTo(startPx, top)
|
|
208
205
|
context.lineTo(startPx - 5, top + 5)
|
|
209
206
|
context.lineTo(startPx, top + 10)
|
|
210
207
|
context.fill()
|
|
211
|
-
context.fillStyle = theme
|
|
208
|
+
context.fillStyle = theme.palette.background.default
|
|
212
209
|
let textTop = top + 12
|
|
213
210
|
context.fillText(featureType, startPx + 2, textTop)
|
|
214
211
|
if (featureName) {
|
|
@@ -219,26 +216,6 @@ function drawTooltip(
|
|
|
219
216
|
context.fillText(location, startPx + 2, textTop)
|
|
220
217
|
}
|
|
221
218
|
|
|
222
|
-
export function isSelectedFeature(
|
|
223
|
-
feature: AnnotationFeature,
|
|
224
|
-
selectedFeature: AnnotationFeature | undefined,
|
|
225
|
-
) {
|
|
226
|
-
return Boolean(selectedFeature && feature._id === selectedFeature._id)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function getBackgroundColor(theme: Theme | undefined, selected: boolean) {
|
|
230
|
-
return selected
|
|
231
|
-
? theme?.palette.text.primary ?? 'black'
|
|
232
|
-
: theme?.palette.background.default ?? 'white'
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function getTextColor(theme: Theme | undefined, selected: boolean) {
|
|
236
|
-
return selected
|
|
237
|
-
? theme?.palette.getContrastText(getBackgroundColor(theme, selected)) ??
|
|
238
|
-
'white'
|
|
239
|
-
: theme?.palette.text.primary ?? 'black'
|
|
240
|
-
}
|
|
241
|
-
|
|
242
219
|
export function drawBox(
|
|
243
220
|
ctx: CanvasRenderingContext2D,
|
|
244
221
|
x: number,
|
|
@@ -254,129 +231,11 @@ export function drawBox(
|
|
|
254
231
|
function getContextMenuItems(
|
|
255
232
|
display: LinearApolloDisplayMouseEvents,
|
|
256
233
|
): MenuItem[] {
|
|
257
|
-
const {
|
|
258
|
-
if (!
|
|
234
|
+
const { hoveredFeature } = display
|
|
235
|
+
if (!hoveredFeature) {
|
|
259
236
|
return []
|
|
260
237
|
}
|
|
261
|
-
|
|
262
|
-
return getContextMenuItemsForFeature(display, sourceFeature)
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function makeFeatureLabel(feature: AnnotationFeature) {
|
|
266
|
-
let name: string | undefined
|
|
267
|
-
if (feature.attributes.get('gff_name')) {
|
|
268
|
-
name = feature.attributes.get('gff_name')?.join(',')
|
|
269
|
-
} else if (feature.attributes.get('gff_id')) {
|
|
270
|
-
name = feature.attributes.get('gff_id')?.join(',')
|
|
271
|
-
} else {
|
|
272
|
-
name = feature._id
|
|
273
|
-
}
|
|
274
|
-
const coords = `(${(feature.min + 1).toLocaleString('en')}..${feature.max.toLocaleString('en')})`
|
|
275
|
-
const maxLen = 60
|
|
276
|
-
if (name && name.length + coords.length > maxLen + 5) {
|
|
277
|
-
const trim = maxLen - coords.length
|
|
278
|
-
name = trim > 0 ? name.slice(0, trim) : ''
|
|
279
|
-
name = `${name}[...]`
|
|
280
|
-
}
|
|
281
|
-
return `${name} ${coords}`
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function getContextMenuItemsForFeature(
|
|
285
|
-
display: LinearApolloDisplayMouseEvents,
|
|
286
|
-
sourceFeature: AnnotationFeature,
|
|
287
|
-
): MenuItem[] {
|
|
288
|
-
const {
|
|
289
|
-
apolloInternetAccount: internetAccount,
|
|
290
|
-
changeManager,
|
|
291
|
-
regions,
|
|
292
|
-
selectedFeature,
|
|
293
|
-
session,
|
|
294
|
-
} = display
|
|
295
|
-
const menuItems: MenuItem[] = []
|
|
296
|
-
const role = internetAccount ? internetAccount.role : 'admin'
|
|
297
|
-
const admin = role === 'admin'
|
|
298
|
-
const readOnly = !(role && ['admin', 'user'].includes(role))
|
|
299
|
-
const [region] = regions
|
|
300
|
-
const sourceAssemblyId = display.getAssemblyId(region.assemblyName)
|
|
301
|
-
const currentAssemblyId = display.getAssemblyId(region.assemblyName)
|
|
302
|
-
const { featureTypeOntology } = session.apolloDataStore.ontologyManager
|
|
303
|
-
if (!featureTypeOntology) {
|
|
304
|
-
throw new Error('featureTypeOntology is undefined')
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Add only relevant options
|
|
308
|
-
menuItems.push(
|
|
309
|
-
{
|
|
310
|
-
label: makeFeatureLabel(sourceFeature),
|
|
311
|
-
type: 'subHeader',
|
|
312
|
-
},
|
|
313
|
-
{
|
|
314
|
-
label: 'Add child feature',
|
|
315
|
-
disabled: readOnly,
|
|
316
|
-
onClick: () => {
|
|
317
|
-
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
318
|
-
(doneCallback) => [
|
|
319
|
-
AddChildFeature,
|
|
320
|
-
{
|
|
321
|
-
session,
|
|
322
|
-
handleClose: () => {
|
|
323
|
-
doneCallback()
|
|
324
|
-
},
|
|
325
|
-
changeManager,
|
|
326
|
-
sourceFeature,
|
|
327
|
-
sourceAssemblyId,
|
|
328
|
-
internetAccount,
|
|
329
|
-
},
|
|
330
|
-
],
|
|
331
|
-
)
|
|
332
|
-
},
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
label: 'Copy features and annotations',
|
|
336
|
-
disabled: readOnly,
|
|
337
|
-
onClick: () => {
|
|
338
|
-
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
339
|
-
(doneCallback) => [
|
|
340
|
-
CopyFeature,
|
|
341
|
-
{
|
|
342
|
-
session,
|
|
343
|
-
handleClose: () => {
|
|
344
|
-
doneCallback()
|
|
345
|
-
},
|
|
346
|
-
changeManager,
|
|
347
|
-
sourceFeature,
|
|
348
|
-
sourceAssemblyId: currentAssemblyId,
|
|
349
|
-
},
|
|
350
|
-
],
|
|
351
|
-
)
|
|
352
|
-
},
|
|
353
|
-
},
|
|
354
|
-
{
|
|
355
|
-
label: 'Delete feature',
|
|
356
|
-
disabled: !admin,
|
|
357
|
-
onClick: () => {
|
|
358
|
-
;(session as unknown as AbstractSessionModel).queueDialog(
|
|
359
|
-
(doneCallback) => [
|
|
360
|
-
DeleteFeature,
|
|
361
|
-
{
|
|
362
|
-
session,
|
|
363
|
-
handleClose: () => {
|
|
364
|
-
doneCallback()
|
|
365
|
-
},
|
|
366
|
-
changeManager,
|
|
367
|
-
sourceFeature,
|
|
368
|
-
sourceAssemblyId: currentAssemblyId,
|
|
369
|
-
selectedFeature,
|
|
370
|
-
setSelectedFeature: (feature?: AnnotationFeature) => {
|
|
371
|
-
display.setSelectedFeature(feature)
|
|
372
|
-
},
|
|
373
|
-
},
|
|
374
|
-
],
|
|
375
|
-
)
|
|
376
|
-
},
|
|
377
|
-
},
|
|
378
|
-
)
|
|
379
|
-
return menuItems
|
|
238
|
+
return getContextMenuItemsForFeature(display, hoveredFeature.feature)
|
|
380
239
|
}
|
|
381
240
|
|
|
382
241
|
function getFeatureFromLayout(
|
|
@@ -400,13 +259,12 @@ function getRowForFeature(
|
|
|
400
259
|
|
|
401
260
|
function onMouseDown(
|
|
402
261
|
stateModel: LinearApolloDisplay,
|
|
403
|
-
currentMousePosition:
|
|
262
|
+
currentMousePosition: MousePositionWithFeature,
|
|
404
263
|
event: CanvasMouseEvent,
|
|
405
264
|
) {
|
|
406
|
-
const {
|
|
265
|
+
const { feature } = currentMousePosition
|
|
407
266
|
// swallow the mouseDown if we are on the edge of the feature so that we
|
|
408
267
|
// don't start dragging the view if we try to drag the feature edge
|
|
409
|
-
const { feature } = featureAndGlyphUnderMouse
|
|
410
268
|
const edge = isMouseOnFeatureEdge(currentMousePosition, feature, stateModel)
|
|
411
269
|
if (edge) {
|
|
412
270
|
event.stopPropagation()
|
|
@@ -422,10 +280,9 @@ function onMouseMove(
|
|
|
422
280
|
stateModel: LinearApolloDisplay,
|
|
423
281
|
mousePosition: MousePosition,
|
|
424
282
|
) {
|
|
425
|
-
if (
|
|
426
|
-
const {
|
|
427
|
-
stateModel.
|
|
428
|
-
const { feature } = featureAndGlyphUnderMouse
|
|
283
|
+
if (isMousePositionWithFeature(mousePosition)) {
|
|
284
|
+
const { feature, bp } = mousePosition
|
|
285
|
+
stateModel.setHoveredFeature({ feature, bp })
|
|
429
286
|
const edge = isMouseOnFeatureEdge(mousePosition, feature, stateModel)
|
|
430
287
|
if (edge) {
|
|
431
288
|
stateModel.setCursor('col-resize')
|
|
@@ -442,11 +299,10 @@ function onMouseUp(
|
|
|
442
299
|
if (stateModel.apolloDragging) {
|
|
443
300
|
return
|
|
444
301
|
}
|
|
445
|
-
const {
|
|
446
|
-
if (!
|
|
302
|
+
const { feature } = mousePosition
|
|
303
|
+
if (!feature) {
|
|
447
304
|
return
|
|
448
305
|
}
|
|
449
|
-
const { feature } = featureAndGlyphUnderMouse
|
|
450
306
|
stateModel.setSelectedFeature(feature)
|
|
451
307
|
stateModel.showFeatureDetailsWidget(feature)
|
|
452
308
|
}
|