@jbrowse/plugin-data-management 1.7.8 → 1.7.11
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/AddConnectionWidget/components/AddConnectionWidget.test.js +25 -16
- package/dist/AddConnectionWidget/model.d.ts +1 -1
- package/dist/AddTrackWidget/index.test.js +1 -0
- package/dist/AddTrackWidget/model.js +1 -1
- package/dist/HierarchicalTrackSelectorWidget/components/CloseConnectionDialog.d.ts +7 -1
- package/dist/HierarchicalTrackSelectorWidget/components/CloseConnectionDialog.js +2 -5
- package/dist/HierarchicalTrackSelectorWidget/components/DeleteConnectionDialog.d.ts +1 -1
- package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.d.ts +9 -5
- package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js +67 -51
- package/dist/HierarchicalTrackSelectorWidget/components/ManageConnectionsDialog.d.ts +2 -1
- package/dist/HierarchicalTrackSelectorWidget/components/ToggleConnectionsDialog.d.ts +2 -1
- package/dist/HierarchicalTrackSelectorWidget/components/ToggleConnectionsDialog.js +10 -11
- package/dist/HierarchicalTrackSelectorWidget/index.d.ts +2 -2
- package/dist/HierarchicalTrackSelectorWidget/model.d.ts +38 -16
- package/dist/HierarchicalTrackSelectorWidget/model.js +89 -97
- package/dist/ucsc-trackhub/configSchema.d.ts +1 -1
- package/dist/ucsc-trackhub/ucscAssemblies.d.ts +1 -1
- package/package.json +2 -2
- package/src/AddConnectionWidget/components/AddConnectionWidget.test.js +2 -1
- package/src/AddTrackWidget/index.test.jsx +1 -0
- package/src/AddTrackWidget/model.ts +1 -1
- package/src/HierarchicalTrackSelectorWidget/components/CloseConnectionDialog.tsx +7 -11
- package/src/HierarchicalTrackSelectorWidget/components/DeleteConnectionDialog.tsx +1 -1
- package/src/HierarchicalTrackSelectorWidget/components/{HierarchicalTrackSelector.js → HierarchicalTrackSelector.tsx} +247 -135
- package/src/HierarchicalTrackSelectorWidget/components/ManageConnectionsDialog.tsx +5 -2
- package/src/HierarchicalTrackSelectorWidget/components/ToggleConnectionsDialog.tsx +24 -26
- package/src/HierarchicalTrackSelectorWidget/{index.js → index.ts} +0 -0
- package/src/HierarchicalTrackSelectorWidget/{model.js → model.ts} +113 -105
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable react/prop-types */
|
|
2
1
|
import React, {
|
|
3
2
|
Suspense,
|
|
4
3
|
lazy,
|
|
@@ -34,11 +33,23 @@ import { Cable } from '@jbrowse/core/ui/Icons'
|
|
|
34
33
|
// other
|
|
35
34
|
import AutoSizer from 'react-virtualized-auto-sizer'
|
|
36
35
|
import JBrowseMenu from '@jbrowse/core/ui/Menu'
|
|
37
|
-
import {
|
|
38
|
-
|
|
36
|
+
import {
|
|
37
|
+
getSession,
|
|
38
|
+
isSessionModelWithWidgets,
|
|
39
|
+
isSessionModelWithConnections,
|
|
40
|
+
isSessionWithAddTracks,
|
|
41
|
+
} from '@jbrowse/core/util'
|
|
42
|
+
import {
|
|
43
|
+
AnyConfigurationModel,
|
|
44
|
+
readConfObject,
|
|
45
|
+
} from '@jbrowse/core/configuration'
|
|
39
46
|
import { observer } from 'mobx-react'
|
|
40
47
|
import { VariableSizeTree } from 'react-vtree'
|
|
41
48
|
|
|
49
|
+
// locals
|
|
50
|
+
import { TreeNode, HierarchicalTrackSelectorModel } from '../model'
|
|
51
|
+
|
|
52
|
+
// lazy components
|
|
42
53
|
const CloseConnectionDialog = lazy(() => import('./CloseConnectionDialog'))
|
|
43
54
|
const DeleteConnectionDialog = lazy(() => import('./DeleteConnectionDialog'))
|
|
44
55
|
const ManageConnectionsDialog = lazy(() => import('./ManageConnectionsDialog'))
|
|
@@ -101,9 +112,31 @@ const useStyles = makeStyles(theme => ({
|
|
|
101
112
|
},
|
|
102
113
|
}))
|
|
103
114
|
|
|
115
|
+
interface MoreInfoArgs {
|
|
116
|
+
target: HTMLElement
|
|
117
|
+
id: string
|
|
118
|
+
conf: AnyConfigurationModel
|
|
119
|
+
}
|
|
120
|
+
|
|
104
121
|
// An individual node in the track selector. Note: manually sets cursor:
|
|
105
122
|
// pointer improves usability for what can be clicked
|
|
106
|
-
|
|
123
|
+
function Node(props: {
|
|
124
|
+
data: {
|
|
125
|
+
isLeaf: boolean
|
|
126
|
+
nestingLevel: number
|
|
127
|
+
checked: boolean
|
|
128
|
+
id: string
|
|
129
|
+
name: string
|
|
130
|
+
onChange: Function
|
|
131
|
+
toggleCollapse: (arg: string) => void
|
|
132
|
+
conf: AnyConfigurationModel
|
|
133
|
+
onMoreInfo: (arg: MoreInfoArgs) => void
|
|
134
|
+
drawerPosition: unknown
|
|
135
|
+
}
|
|
136
|
+
isOpen: boolean
|
|
137
|
+
style?: { height: number }
|
|
138
|
+
setOpen: (arg: boolean) => void
|
|
139
|
+
}) {
|
|
107
140
|
const { data, isOpen, style, setOpen } = props
|
|
108
141
|
const {
|
|
109
142
|
isLeaf,
|
|
@@ -122,7 +155,7 @@ const Node = props => {
|
|
|
122
155
|
const width = 10
|
|
123
156
|
const marginLeft = nestingLevel * width + (isLeaf ? width : 0)
|
|
124
157
|
const unsupported =
|
|
125
|
-
name
|
|
158
|
+
name?.endsWith('(Unsupported)') || name?.endsWith('(Unknown)')
|
|
126
159
|
const description = (conf && readConfObject(conf, ['description'])) || ''
|
|
127
160
|
|
|
128
161
|
return (
|
|
@@ -130,7 +163,7 @@ const Node = props => {
|
|
|
130
163
|
{new Array(nestingLevel).fill(0).map((_, idx) => (
|
|
131
164
|
<div
|
|
132
165
|
key={`mark-${idx}`}
|
|
133
|
-
style={{ left: idx * width + 4, height: style
|
|
166
|
+
style={{ left: idx * width + 4, height: style?.height }}
|
|
134
167
|
className={classes.nestingLevelMarker}
|
|
135
168
|
/>
|
|
136
169
|
))}
|
|
@@ -170,6 +203,7 @@ const Node = props => {
|
|
|
170
203
|
color="primary"
|
|
171
204
|
disabled={unsupported}
|
|
172
205
|
inputProps={{
|
|
206
|
+
// @ts-ignore
|
|
173
207
|
'data-testid': `htsTrackEntry-${id}`,
|
|
174
208
|
}}
|
|
175
209
|
/>
|
|
@@ -192,7 +226,11 @@ const Node = props => {
|
|
|
192
226
|
)
|
|
193
227
|
}
|
|
194
228
|
|
|
195
|
-
|
|
229
|
+
function getNodeData(
|
|
230
|
+
node: TreeNode,
|
|
231
|
+
nestingLevel: number,
|
|
232
|
+
extra: Record<string, unknown>,
|
|
233
|
+
) {
|
|
196
234
|
const isLeaf = !!node.conf
|
|
197
235
|
return {
|
|
198
236
|
data: {
|
|
@@ -208,74 +246,99 @@ const getNodeData = (node, nestingLevel, extra) => {
|
|
|
208
246
|
}
|
|
209
247
|
}
|
|
210
248
|
|
|
249
|
+
type NodeData = ReturnType<typeof getNodeData>
|
|
250
|
+
|
|
211
251
|
// this is the main tree component for the hierarchical track selector in note:
|
|
212
252
|
// in jbrowse-web the toolbar is position="sticky" which means the autosizer
|
|
213
253
|
// includes the height of the toolbar, so we subtract the given offsets
|
|
214
|
-
const HierarchicalTree = observer(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
254
|
+
const HierarchicalTree = observer(
|
|
255
|
+
({
|
|
256
|
+
height,
|
|
257
|
+
tree,
|
|
258
|
+
model,
|
|
259
|
+
}: {
|
|
260
|
+
height: number
|
|
261
|
+
tree: TreeNode
|
|
262
|
+
model: HierarchicalTrackSelectorModel
|
|
263
|
+
}) => {
|
|
264
|
+
const { filterText, view } = model
|
|
265
|
+
const treeRef = useRef<NodeData>(null)
|
|
266
|
+
const [info, setMoreInfo] = useState<MoreInfoArgs>()
|
|
267
|
+
const session = getSession(model)
|
|
268
|
+
const { drawerPosition } = session
|
|
269
|
+
|
|
270
|
+
const extra = useMemo(
|
|
271
|
+
() => ({
|
|
272
|
+
onChange: (trackId: string) => view.toggleTrack(trackId),
|
|
273
|
+
toggleCollapse: (pathName: string) => model.toggleCategory(pathName),
|
|
274
|
+
onMoreInfo: setMoreInfo,
|
|
275
|
+
drawerPosition,
|
|
276
|
+
}),
|
|
277
|
+
[view, model, drawerPosition],
|
|
278
|
+
)
|
|
279
|
+
const treeWalker = useCallback(
|
|
280
|
+
function* treeWalker() {
|
|
281
|
+
for (let i = 0; i < tree.children.length; i++) {
|
|
282
|
+
const r = tree.children[i]
|
|
283
|
+
yield getNodeData(r, 0, extra)
|
|
284
|
+
}
|
|
235
285
|
|
|
236
|
-
|
|
237
|
-
|
|
286
|
+
while (true) {
|
|
287
|
+
// @ts-ignore
|
|
288
|
+
const parentMeta = yield
|
|
238
289
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
290
|
+
for (let i = 0; i < parentMeta.node.children.length; i++) {
|
|
291
|
+
const curr = parentMeta.node.children[i]
|
|
292
|
+
yield getNodeData(curr, parentMeta.nestingLevel + 1, extra)
|
|
293
|
+
}
|
|
242
294
|
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
)
|
|
295
|
+
},
|
|
296
|
+
[tree, extra],
|
|
297
|
+
)
|
|
247
298
|
|
|
248
|
-
|
|
249
|
-
|
|
299
|
+
const conf = info?.conf
|
|
300
|
+
const menuItems = (conf && session.getTrackActionMenuItems?.(conf)) || []
|
|
250
301
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
{
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
302
|
+
useEffect(() => {
|
|
303
|
+
// @ts-ignore
|
|
304
|
+
treeRef.current.recomputeTree({
|
|
305
|
+
refreshNodes: true,
|
|
306
|
+
useDefaultHeight: true,
|
|
307
|
+
})
|
|
308
|
+
}, [tree, filterText])
|
|
309
|
+
return (
|
|
310
|
+
<>
|
|
311
|
+
{/* @ts-ignore */}
|
|
312
|
+
<VariableSizeTree ref={treeRef} treeWalker={treeWalker} height={height}>
|
|
313
|
+
{/* @ts-ignore */}
|
|
314
|
+
{Node}
|
|
315
|
+
</VariableSizeTree>
|
|
316
|
+
<JBrowseMenu
|
|
317
|
+
anchorEl={info?.target}
|
|
318
|
+
menuItems={menuItems}
|
|
319
|
+
onMenuItemClick={(_event, callback) => {
|
|
320
|
+
callback()
|
|
321
|
+
setMoreInfo(undefined)
|
|
322
|
+
}}
|
|
323
|
+
open={Boolean(info)}
|
|
324
|
+
onClose={() => setMoreInfo(undefined)}
|
|
325
|
+
/>
|
|
326
|
+
</>
|
|
327
|
+
)
|
|
328
|
+
},
|
|
329
|
+
)
|
|
275
330
|
|
|
276
331
|
// Don't use autosizer in jest and instead hardcode a height, otherwise fails
|
|
277
332
|
// jest tests
|
|
278
|
-
const AutoSizedHierarchicalTree = ({
|
|
333
|
+
const AutoSizedHierarchicalTree = ({
|
|
334
|
+
tree,
|
|
335
|
+
model,
|
|
336
|
+
offset,
|
|
337
|
+
}: {
|
|
338
|
+
tree: TreeNode
|
|
339
|
+
model: HierarchicalTrackSelectorModel
|
|
340
|
+
offset: number
|
|
341
|
+
}) => {
|
|
279
342
|
return typeof jest === 'undefined' ? (
|
|
280
343
|
<AutoSizer disableWidth>
|
|
281
344
|
{({ height }) => {
|
|
@@ -293,7 +356,13 @@ const AutoSizedHierarchicalTree = ({ tree, model, offset }) => {
|
|
|
293
356
|
)
|
|
294
357
|
}
|
|
295
358
|
|
|
296
|
-
const Wrapper = ({
|
|
359
|
+
const Wrapper = ({
|
|
360
|
+
overrideDimensions,
|
|
361
|
+
children,
|
|
362
|
+
}: {
|
|
363
|
+
overrideDimensions?: { width: number; height: number }
|
|
364
|
+
children: React.ReactNode
|
|
365
|
+
}) => {
|
|
297
366
|
return overrideDimensions ? (
|
|
298
367
|
<div style={{ ...overrideDimensions }}>{children}</div>
|
|
299
368
|
) : (
|
|
@@ -301,29 +370,36 @@ const Wrapper = ({ overrideDimensions, children }) => {
|
|
|
301
370
|
)
|
|
302
371
|
}
|
|
303
372
|
const HierarchicalTrackSelectorContainer = observer(
|
|
304
|
-
({
|
|
373
|
+
({
|
|
374
|
+
model,
|
|
375
|
+
toolbarHeight,
|
|
376
|
+
overrideDimensions,
|
|
377
|
+
}: {
|
|
378
|
+
model: HierarchicalTrackSelectorModel
|
|
379
|
+
toolbarHeight: number
|
|
380
|
+
overrideDimensions?: { width: number; height: number }
|
|
381
|
+
}) => {
|
|
305
382
|
const classes = useStyles()
|
|
306
383
|
const session = getSession(model)
|
|
307
|
-
const [anchorEl, setAnchorEl] = useState(null)
|
|
384
|
+
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null)
|
|
308
385
|
|
|
309
386
|
function handleFabClose() {
|
|
310
387
|
setAnchorEl(null)
|
|
311
388
|
}
|
|
389
|
+
const hasConnections = isSessionModelWithConnections(session)
|
|
390
|
+
const hasAddTrack = isSessionWithAddTracks(session)
|
|
312
391
|
return (
|
|
313
392
|
<Wrapper overrideDimensions={overrideDimensions}>
|
|
314
393
|
<HierarchicalTrackSelector
|
|
315
394
|
model={model}
|
|
316
395
|
toolbarHeight={toolbarHeight}
|
|
317
|
-
overrideDimensions={overrideDimensions}
|
|
318
396
|
/>
|
|
319
|
-
{
|
|
397
|
+
{hasAddTrack || hasConnections ? (
|
|
320
398
|
<>
|
|
321
399
|
<Fab
|
|
322
400
|
color="secondary"
|
|
323
401
|
className={classes.fab}
|
|
324
|
-
onClick={event =>
|
|
325
|
-
setAnchorEl(event.currentTarget)
|
|
326
|
-
}}
|
|
402
|
+
onClick={event => setAnchorEl(event.currentTarget)}
|
|
327
403
|
>
|
|
328
404
|
<AddIcon />
|
|
329
405
|
</Fab>
|
|
@@ -332,32 +408,34 @@ const HierarchicalTrackSelectorContainer = observer(
|
|
|
332
408
|
open={Boolean(anchorEl)}
|
|
333
409
|
onClose={() => setAnchorEl(null)}
|
|
334
410
|
>
|
|
335
|
-
{
|
|
411
|
+
{hasConnections ? (
|
|
336
412
|
<MenuItem
|
|
337
413
|
onClick={() => {
|
|
338
414
|
handleFabClose()
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
415
|
+
if (isSessionModelWithWidgets(session)) {
|
|
416
|
+
session.showWidget(
|
|
417
|
+
session.addWidget(
|
|
418
|
+
'AddConnectionWidget',
|
|
419
|
+
'addConnectionWidget',
|
|
420
|
+
),
|
|
421
|
+
)
|
|
422
|
+
}
|
|
344
423
|
}}
|
|
345
424
|
>
|
|
346
425
|
Add connection
|
|
347
426
|
</MenuItem>
|
|
348
427
|
) : null}
|
|
349
|
-
{
|
|
428
|
+
{hasAddTrack ? (
|
|
350
429
|
<MenuItem
|
|
351
430
|
onClick={() => {
|
|
352
431
|
handleFabClose()
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
session.showWidget(widget)
|
|
432
|
+
if (isSessionModelWithWidgets(session)) {
|
|
433
|
+
session.showWidget(
|
|
434
|
+
session.addWidget('AddTrackWidget', 'addTrackWidget', {
|
|
435
|
+
view: model.view.id,
|
|
436
|
+
}),
|
|
437
|
+
)
|
|
438
|
+
}
|
|
361
439
|
}}
|
|
362
440
|
>
|
|
363
441
|
Add track
|
|
@@ -372,20 +450,44 @@ const HierarchicalTrackSelectorContainer = observer(
|
|
|
372
450
|
)
|
|
373
451
|
|
|
374
452
|
const HierarchicalTrackSelectorHeader = observer(
|
|
375
|
-
({
|
|
453
|
+
({
|
|
454
|
+
model,
|
|
455
|
+
setHeaderHeight,
|
|
456
|
+
setAssemblyIdx,
|
|
457
|
+
assemblyIdx,
|
|
458
|
+
}: {
|
|
459
|
+
model: HierarchicalTrackSelectorModel
|
|
460
|
+
setHeaderHeight: (n: number) => void
|
|
461
|
+
setAssemblyIdx: (n: number) => void
|
|
462
|
+
assemblyIdx: number
|
|
463
|
+
}) => {
|
|
376
464
|
const classes = useStyles()
|
|
377
465
|
const session = getSession(model)
|
|
378
|
-
const [connectionAnchorEl, setConnectionAnchorEl] =
|
|
379
|
-
|
|
380
|
-
const [
|
|
381
|
-
const [
|
|
466
|
+
const [connectionAnchorEl, setConnectionAnchorEl] =
|
|
467
|
+
useState<HTMLButtonElement>()
|
|
468
|
+
const [menuAnchorEl, setMenuAnchorEl] = useState<HTMLButtonElement>()
|
|
469
|
+
const [modalInfo, setModalInfo] = useState<{
|
|
470
|
+
connectionConf: AnyConfigurationModel
|
|
471
|
+
safelyBreakConnection: Function
|
|
472
|
+
dereferenceTypeCount: { [key: string]: number }
|
|
473
|
+
name: string
|
|
474
|
+
}>()
|
|
475
|
+
const [deleteDialogDetails, setDeleteDialogDetails] = useState<{
|
|
476
|
+
name: string
|
|
477
|
+
connectionConf: AnyConfigurationModel
|
|
478
|
+
}>()
|
|
382
479
|
const [connectionManagerOpen, setConnectionManagerOpen] = useState(false)
|
|
383
480
|
const [connectionToggleOpen, setConnectionToggleOpen] = useState(false)
|
|
384
481
|
const { assemblyNames } = model
|
|
385
482
|
const assemblyName = assemblyNames[assemblyIdx]
|
|
386
483
|
|
|
387
|
-
function breakConnection(
|
|
484
|
+
function breakConnection(
|
|
485
|
+
connectionConf: AnyConfigurationModel,
|
|
486
|
+
deletingConnection?: boolean,
|
|
487
|
+
) {
|
|
388
488
|
const name = readConfObject(connectionConf, 'name')
|
|
489
|
+
|
|
490
|
+
// @ts-ignore
|
|
389
491
|
const result = session.prepareToBreakConnection(connectionConf)
|
|
390
492
|
if (result) {
|
|
391
493
|
const [safelyBreakConnection, dereferenceTypeCount] = result
|
|
@@ -412,13 +514,17 @@ const HierarchicalTrackSelectorHeader = observer(
|
|
|
412
514
|
},
|
|
413
515
|
]
|
|
414
516
|
|
|
415
|
-
if (session
|
|
517
|
+
if (isSessionModelWithConnections(session)) {
|
|
416
518
|
connectionMenuItems.unshift({
|
|
417
519
|
label: 'Add connection',
|
|
418
520
|
onClick: () => {
|
|
419
|
-
session
|
|
420
|
-
session.addWidget(
|
|
421
|
-
|
|
521
|
+
if (isSessionModelWithWidgets(session)) {
|
|
522
|
+
const widget = session.addWidget(
|
|
523
|
+
'AddConnectionWidget',
|
|
524
|
+
'addConnectionWidget',
|
|
525
|
+
)
|
|
526
|
+
session.showWidget(widget)
|
|
527
|
+
}
|
|
422
528
|
},
|
|
423
529
|
})
|
|
424
530
|
|
|
@@ -435,9 +541,7 @@ const HierarchicalTrackSelectorHeader = observer(
|
|
|
435
541
|
label: 'Select assembly...',
|
|
436
542
|
subMenu: assemblyNames.map((name, idx) => ({
|
|
437
543
|
label: name,
|
|
438
|
-
onClick: () =>
|
|
439
|
-
setAssemblyIdx(idx)
|
|
440
|
-
},
|
|
544
|
+
onClick: () => setAssemblyIdx(idx),
|
|
441
545
|
})),
|
|
442
546
|
},
|
|
443
547
|
]
|
|
@@ -447,11 +551,16 @@ const HierarchicalTrackSelectorHeader = observer(
|
|
|
447
551
|
{
|
|
448
552
|
label: 'Add track...',
|
|
449
553
|
onClick: () => {
|
|
450
|
-
session
|
|
451
|
-
session.addWidget(
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
554
|
+
if (isSessionModelWithWidgets(session)) {
|
|
555
|
+
const widget = session.addWidget(
|
|
556
|
+
'AddTrackWidget',
|
|
557
|
+
'addTrackWidget',
|
|
558
|
+
{
|
|
559
|
+
view: model.view.id,
|
|
560
|
+
},
|
|
561
|
+
)
|
|
562
|
+
session.showWidget(widget)
|
|
563
|
+
}
|
|
455
564
|
},
|
|
456
565
|
},
|
|
457
566
|
|
|
@@ -464,12 +573,10 @@ const HierarchicalTrackSelectorHeader = observer(
|
|
|
464
573
|
data-testid="hierarchical_track_selector"
|
|
465
574
|
>
|
|
466
575
|
<div style={{ display: 'flex' }}>
|
|
467
|
-
{session
|
|
576
|
+
{isSessionWithAddTracks(session) && (
|
|
468
577
|
<IconButton
|
|
469
578
|
className={classes.menuIcon}
|
|
470
|
-
onClick={event =>
|
|
471
|
-
setMenuAnchorEl(event.currentTarget)
|
|
472
|
-
}}
|
|
579
|
+
onClick={event => setMenuAnchorEl(event.currentTarget)}
|
|
473
580
|
>
|
|
474
581
|
<MenuIcon />
|
|
475
582
|
</IconButton>
|
|
@@ -478,9 +585,7 @@ const HierarchicalTrackSelectorHeader = observer(
|
|
|
478
585
|
{session.makeConnection && (
|
|
479
586
|
<IconButton
|
|
480
587
|
className={classes.menuIcon}
|
|
481
|
-
onClick={event =>
|
|
482
|
-
setConnectionAnchorEl(event.currentTarget)
|
|
483
|
-
}}
|
|
588
|
+
onClick={event => setConnectionAnchorEl(event.currentTarget)}
|
|
484
589
|
>
|
|
485
590
|
<Cable />
|
|
486
591
|
</IconButton>
|
|
@@ -528,7 +633,6 @@ const HierarchicalTrackSelectorHeader = observer(
|
|
|
528
633
|
<CloseConnectionDialog
|
|
529
634
|
modalInfo={modalInfo}
|
|
530
635
|
setModalInfo={setModalInfo}
|
|
531
|
-
session={session}
|
|
532
636
|
/>
|
|
533
637
|
) : deleteDialogDetails ? (
|
|
534
638
|
<DeleteConnectionDialog
|
|
@@ -557,27 +661,35 @@ const HierarchicalTrackSelectorHeader = observer(
|
|
|
557
661
|
)
|
|
558
662
|
},
|
|
559
663
|
)
|
|
560
|
-
const HierarchicalTrackSelector = observer(
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
664
|
+
const HierarchicalTrackSelector = observer(
|
|
665
|
+
({
|
|
666
|
+
model,
|
|
667
|
+
toolbarHeight = 0,
|
|
668
|
+
}: {
|
|
669
|
+
model: HierarchicalTrackSelectorModel
|
|
670
|
+
toolbarHeight?: number
|
|
671
|
+
}) => {
|
|
672
|
+
const [assemblyIdx, setAssemblyIdx] = useState(0)
|
|
673
|
+
const [headerHeight, setHeaderHeight] = useState(0)
|
|
674
|
+
|
|
675
|
+
const { assemblyNames } = model
|
|
676
|
+
const assemblyName = assemblyNames[assemblyIdx]
|
|
677
|
+
return assemblyName ? (
|
|
678
|
+
<>
|
|
679
|
+
<HierarchicalTrackSelectorHeader
|
|
680
|
+
model={model}
|
|
681
|
+
setHeaderHeight={setHeaderHeight}
|
|
682
|
+
setAssemblyIdx={setAssemblyIdx}
|
|
683
|
+
assemblyIdx={assemblyIdx}
|
|
684
|
+
/>
|
|
685
|
+
<AutoSizedHierarchicalTree
|
|
686
|
+
tree={model.hierarchy(assemblyName)}
|
|
687
|
+
model={model}
|
|
688
|
+
offset={toolbarHeight + headerHeight}
|
|
689
|
+
/>
|
|
690
|
+
</>
|
|
691
|
+
) : null
|
|
692
|
+
},
|
|
693
|
+
)
|
|
582
694
|
|
|
583
695
|
export default HierarchicalTrackSelectorContainer
|
|
@@ -12,7 +12,10 @@ import {
|
|
|
12
12
|
} from '@material-ui/core'
|
|
13
13
|
import CloseIcon from '@material-ui/icons/Close'
|
|
14
14
|
import { observer } from 'mobx-react'
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
AnyConfigurationModel,
|
|
17
|
+
readConfObject,
|
|
18
|
+
} from '@jbrowse/core/configuration'
|
|
16
19
|
import { AbstractSessionModel } from '@jbrowse/core/util'
|
|
17
20
|
|
|
18
21
|
const useStyles = makeStyles(theme => ({
|
|
@@ -36,7 +39,7 @@ function ManageConnectionsDlg({
|
|
|
36
39
|
}: {
|
|
37
40
|
handleClose: () => void
|
|
38
41
|
session: AbstractSessionModel
|
|
39
|
-
breakConnection:
|
|
42
|
+
breakConnection: (conf: AnyConfigurationModel, arg: boolean) => void
|
|
40
43
|
}) {
|
|
41
44
|
const classes = useStyles()
|
|
42
45
|
const { adminMode, connections, sessionConnections } = session
|
|
@@ -13,7 +13,10 @@ import {
|
|
|
13
13
|
} from '@material-ui/core'
|
|
14
14
|
import CloseIcon from '@material-ui/icons/Close'
|
|
15
15
|
import { observer } from 'mobx-react'
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
AnyConfigurationModel,
|
|
18
|
+
readConfObject,
|
|
19
|
+
} from '@jbrowse/core/configuration'
|
|
17
20
|
import { AbstractSessionModel } from '@jbrowse/core/util'
|
|
18
21
|
|
|
19
22
|
const useStyles = makeStyles(theme => ({
|
|
@@ -39,10 +42,10 @@ function ToggleConnectionDialog({
|
|
|
39
42
|
handleClose: () => void
|
|
40
43
|
session: AbstractSessionModel
|
|
41
44
|
assemblyName: string
|
|
42
|
-
breakConnection:
|
|
45
|
+
breakConnection: (arg: AnyConfigurationModel) => void
|
|
43
46
|
}) {
|
|
44
47
|
const classes = useStyles()
|
|
45
|
-
const { connections, connectionInstances } = session
|
|
48
|
+
const { connections, connectionInstances: instances = [] } = session
|
|
46
49
|
const assemblySpecificConnections = connections.filter(c => {
|
|
47
50
|
const configAssemblyNames = readConfObject(c, 'assemblyNames')
|
|
48
51
|
if (configAssemblyNames.length === 0) {
|
|
@@ -50,6 +53,7 @@ function ToggleConnectionDialog({
|
|
|
50
53
|
}
|
|
51
54
|
return configAssemblyNames.includes(assemblyName)
|
|
52
55
|
})
|
|
56
|
+
|
|
53
57
|
return (
|
|
54
58
|
<Dialog open onClose={handleClose} maxWidth="lg">
|
|
55
59
|
<DialogTitle>
|
|
@@ -66,31 +70,25 @@ function ToggleConnectionDialog({
|
|
|
66
70
|
<div className={classes.connectionContainer}>
|
|
67
71
|
{assemblySpecificConnections.map(conf => {
|
|
68
72
|
const name = readConfObject(conf, 'name')
|
|
73
|
+
const found = instances.find(conn => name === conn.name)
|
|
69
74
|
return (
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
<FormControlLabel
|
|
76
|
+
key={conf.connectionId}
|
|
77
|
+
control={
|
|
78
|
+
<Checkbox
|
|
79
|
+
checked={!!found}
|
|
80
|
+
onChange={() => {
|
|
81
|
+
if (found) {
|
|
82
|
+
breakConnection(conf)
|
|
83
|
+
} else {
|
|
84
|
+
session.makeConnection?.(conf)
|
|
76
85
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
breakConnection(conf)
|
|
84
|
-
} else {
|
|
85
|
-
session.makeConnection?.(conf)
|
|
86
|
-
}
|
|
87
|
-
}}
|
|
88
|
-
color="primary"
|
|
89
|
-
/>
|
|
90
|
-
}
|
|
91
|
-
label={name}
|
|
92
|
-
/>
|
|
93
|
-
</div>
|
|
86
|
+
}}
|
|
87
|
+
color="primary"
|
|
88
|
+
/>
|
|
89
|
+
}
|
|
90
|
+
label={name}
|
|
91
|
+
/>
|
|
94
92
|
)
|
|
95
93
|
})}
|
|
96
94
|
{!assemblySpecificConnections.length ? (
|
|
File without changes
|