@jbrowse/plugin-data-management 2.3.3 → 2.3.4

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.
Files changed (170) hide show
  1. package/dist/AddTrackWidget/components/DefaultAddTrackWorkflow.js +1 -4
  2. package/dist/AddTrackWidget/components/DefaultAddTrackWorkflow.js.map +1 -1
  3. package/dist/AddTrackWidget/components/TrackSourceSelect.js +1 -4
  4. package/dist/AddTrackWidget/components/TrackSourceSelect.js.map +1 -1
  5. package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalFab.d.ts +6 -0
  6. package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalFab.js +71 -0
  7. package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalFab.js.map +1 -0
  8. package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js +10 -104
  9. package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js.map +1 -1
  10. package/dist/HierarchicalTrackSelectorWidget/components/ShoppingCart.d.ts +6 -0
  11. package/dist/HierarchicalTrackSelectorWidget/components/ShoppingCart.js +70 -0
  12. package/dist/HierarchicalTrackSelectorWidget/components/ShoppingCart.js.map +1 -0
  13. package/dist/HierarchicalTrackSelectorWidget/components/{CloseConnectionDialog.d.ts → dialogs/CloseConnectionDialog.d.ts} +0 -0
  14. package/dist/HierarchicalTrackSelectorWidget/components/{CloseConnectionDialog.js → dialogs/CloseConnectionDialog.js} +0 -0
  15. package/dist/HierarchicalTrackSelectorWidget/components/dialogs/CloseConnectionDialog.js.map +1 -0
  16. package/dist/HierarchicalTrackSelectorWidget/components/{DeleteConnectionDialog.d.ts → dialogs/DeleteConnectionDialog.d.ts} +0 -0
  17. package/dist/HierarchicalTrackSelectorWidget/components/{DeleteConnectionDialog.js → dialogs/DeleteConnectionDialog.js} +0 -0
  18. package/dist/HierarchicalTrackSelectorWidget/components/dialogs/DeleteConnectionDialog.js.map +1 -0
  19. package/dist/HierarchicalTrackSelectorWidget/components/{ManageConnectionsDialog.d.ts → dialogs/ManageConnectionsDialog.d.ts} +0 -0
  20. package/dist/HierarchicalTrackSelectorWidget/components/{ManageConnectionsDialog.js → dialogs/ManageConnectionsDialog.js} +0 -0
  21. package/dist/HierarchicalTrackSelectorWidget/components/dialogs/ManageConnectionsDialog.js.map +1 -0
  22. package/dist/HierarchicalTrackSelectorWidget/components/{ToggleConnectionsDialog.d.ts → dialogs/ToggleConnectionsDialog.d.ts} +0 -0
  23. package/dist/HierarchicalTrackSelectorWidget/components/{ToggleConnectionsDialog.js → dialogs/ToggleConnectionsDialog.js} +0 -0
  24. package/dist/HierarchicalTrackSelectorWidget/components/dialogs/ToggleConnectionsDialog.js.map +1 -0
  25. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.d.ts +13 -0
  26. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js +99 -0
  27. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js.map +1 -0
  28. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.d.ts +8 -0
  29. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.js +18 -0
  30. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.js.map +1 -0
  31. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.d.ts +11 -0
  32. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js +71 -0
  33. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js.map +1 -0
  34. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.d.ts +12 -0
  35. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js +211 -0
  36. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js.map +1 -0
  37. package/dist/HierarchicalTrackSelectorWidget/components/faceted/util.d.ts +1 -0
  38. package/dist/HierarchicalTrackSelectorWidget/components/faceted/util.js +10 -0
  39. package/dist/HierarchicalTrackSelectorWidget/components/faceted/util.js.map +1 -0
  40. package/dist/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.d.ts +7 -0
  41. package/dist/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js +136 -0
  42. package/dist/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js.map +1 -0
  43. package/dist/HierarchicalTrackSelectorWidget/components/{Header.d.ts → tree/HierarchicalHeader.d.ts} +2 -3
  44. package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js +65 -0
  45. package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js.map +1 -0
  46. package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.d.ts +8 -0
  47. package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js +91 -0
  48. package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js.map +1 -0
  49. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.d.ts +7 -0
  50. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js +89 -0
  51. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js.map +1 -0
  52. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.d.ts +11 -0
  53. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js +86 -0
  54. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js.map +1 -0
  55. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.d.ts +10 -0
  56. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.js +54 -0
  57. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.js.map +1 -0
  58. package/dist/HierarchicalTrackSelectorWidget/components/util.d.ts +17 -1
  59. package/dist/HierarchicalTrackSelectorWidget/components/util.js +16 -1
  60. package/dist/HierarchicalTrackSelectorWidget/components/util.js.map +1 -1
  61. package/dist/HierarchicalTrackSelectorWidget/model.d.ts +4 -1
  62. package/dist/HierarchicalTrackSelectorWidget/model.js +12 -8
  63. package/dist/HierarchicalTrackSelectorWidget/model.js.map +1 -1
  64. package/esm/AddTrackWidget/components/DefaultAddTrackWorkflow.js +1 -4
  65. package/esm/AddTrackWidget/components/DefaultAddTrackWorkflow.js.map +1 -1
  66. package/esm/AddTrackWidget/components/TrackSourceSelect.js +1 -4
  67. package/esm/AddTrackWidget/components/TrackSourceSelect.js.map +1 -1
  68. package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalFab.d.ts +6 -0
  69. package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalFab.js +43 -0
  70. package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalFab.js.map +1 -0
  71. package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js +9 -103
  72. package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js.map +1 -1
  73. package/esm/HierarchicalTrackSelectorWidget/components/ShoppingCart.d.ts +6 -0
  74. package/esm/HierarchicalTrackSelectorWidget/components/ShoppingCart.js +42 -0
  75. package/esm/HierarchicalTrackSelectorWidget/components/ShoppingCart.js.map +1 -0
  76. package/esm/HierarchicalTrackSelectorWidget/components/{CloseConnectionDialog.d.ts → dialogs/CloseConnectionDialog.d.ts} +0 -0
  77. package/esm/HierarchicalTrackSelectorWidget/components/{CloseConnectionDialog.js → dialogs/CloseConnectionDialog.js} +0 -0
  78. package/esm/HierarchicalTrackSelectorWidget/components/dialogs/CloseConnectionDialog.js.map +1 -0
  79. package/esm/HierarchicalTrackSelectorWidget/components/{DeleteConnectionDialog.d.ts → dialogs/DeleteConnectionDialog.d.ts} +0 -0
  80. package/esm/HierarchicalTrackSelectorWidget/components/{DeleteConnectionDialog.js → dialogs/DeleteConnectionDialog.js} +0 -0
  81. package/esm/HierarchicalTrackSelectorWidget/components/dialogs/DeleteConnectionDialog.js.map +1 -0
  82. package/esm/HierarchicalTrackSelectorWidget/components/{ManageConnectionsDialog.d.ts → dialogs/ManageConnectionsDialog.d.ts} +0 -0
  83. package/esm/HierarchicalTrackSelectorWidget/components/{ManageConnectionsDialog.js → dialogs/ManageConnectionsDialog.js} +0 -0
  84. package/esm/HierarchicalTrackSelectorWidget/components/dialogs/ManageConnectionsDialog.js.map +1 -0
  85. package/esm/HierarchicalTrackSelectorWidget/components/{ToggleConnectionsDialog.d.ts → dialogs/ToggleConnectionsDialog.d.ts} +0 -0
  86. package/esm/HierarchicalTrackSelectorWidget/components/{ToggleConnectionsDialog.js → dialogs/ToggleConnectionsDialog.js} +0 -0
  87. package/esm/HierarchicalTrackSelectorWidget/components/dialogs/ToggleConnectionsDialog.js.map +1 -0
  88. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.d.ts +13 -0
  89. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js +70 -0
  90. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js.map +1 -0
  91. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.d.ts +8 -0
  92. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.js +13 -0
  93. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.js.map +1 -0
  94. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.d.ts +11 -0
  95. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js +42 -0
  96. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js.map +1 -0
  97. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.d.ts +12 -0
  98. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js +183 -0
  99. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js.map +1 -0
  100. package/esm/HierarchicalTrackSelectorWidget/components/faceted/util.d.ts +1 -0
  101. package/esm/HierarchicalTrackSelectorWidget/components/faceted/util.js +6 -0
  102. package/esm/HierarchicalTrackSelectorWidget/components/faceted/util.js.map +1 -0
  103. package/esm/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.d.ts +7 -0
  104. package/esm/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js +108 -0
  105. package/esm/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js.map +1 -0
  106. package/esm/HierarchicalTrackSelectorWidget/components/{Header.d.ts → tree/HierarchicalHeader.d.ts} +2 -3
  107. package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js +37 -0
  108. package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js.map +1 -0
  109. package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.d.ts +8 -0
  110. package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js +63 -0
  111. package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js.map +1 -0
  112. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.d.ts +7 -0
  113. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js +60 -0
  114. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js.map +1 -0
  115. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.d.ts +11 -0
  116. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js +57 -0
  117. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js.map +1 -0
  118. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.d.ts +10 -0
  119. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.js +48 -0
  120. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.js.map +1 -0
  121. package/esm/HierarchicalTrackSelectorWidget/components/util.d.ts +17 -1
  122. package/esm/HierarchicalTrackSelectorWidget/components/util.js +13 -0
  123. package/esm/HierarchicalTrackSelectorWidget/components/util.js.map +1 -1
  124. package/esm/HierarchicalTrackSelectorWidget/model.d.ts +4 -1
  125. package/esm/HierarchicalTrackSelectorWidget/model.js +10 -7
  126. package/esm/HierarchicalTrackSelectorWidget/model.js.map +1 -1
  127. package/package.json +3 -2
  128. package/src/AddTrackWidget/components/DefaultAddTrackWorkflow.tsx +1 -4
  129. package/src/AddTrackWidget/components/TrackSourceSelect.tsx +3 -5
  130. package/src/HierarchicalTrackSelectorWidget/components/HierarchicalFab.tsx +94 -0
  131. package/src/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.tsx +47 -228
  132. package/src/HierarchicalTrackSelectorWidget/components/ShoppingCart.tsx +73 -0
  133. package/src/HierarchicalTrackSelectorWidget/components/__snapshots__/HierarchicalTrackSelector.test.tsx.snap +20 -38
  134. package/src/HierarchicalTrackSelectorWidget/components/{CloseConnectionDialog.tsx → dialogs/CloseConnectionDialog.tsx} +0 -0
  135. package/src/HierarchicalTrackSelectorWidget/components/{DeleteConnectionDialog.tsx → dialogs/DeleteConnectionDialog.tsx} +0 -0
  136. package/src/HierarchicalTrackSelectorWidget/components/{ManageConnectionsDialog.tsx → dialogs/ManageConnectionsDialog.tsx} +0 -0
  137. package/src/HierarchicalTrackSelectorWidget/components/{ToggleConnectionsDialog.tsx → dialogs/ToggleConnectionsDialog.tsx} +0 -0
  138. package/src/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.tsx +138 -0
  139. package/src/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.tsx +29 -0
  140. package/src/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.tsx +86 -0
  141. package/src/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.tsx +339 -0
  142. package/src/HierarchicalTrackSelectorWidget/components/faceted/util.ts +5 -0
  143. package/src/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.tsx +197 -0
  144. package/src/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.tsx +88 -0
  145. package/src/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.tsx +101 -0
  146. package/src/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.tsx +92 -0
  147. package/src/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.tsx +107 -0
  148. package/src/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.tsx +84 -0
  149. package/src/HierarchicalTrackSelectorWidget/components/util.ts +31 -1
  150. package/src/HierarchicalTrackSelectorWidget/model.ts +12 -9
  151. package/dist/HierarchicalTrackSelectorWidget/components/CloseConnectionDialog.js.map +0 -1
  152. package/dist/HierarchicalTrackSelectorWidget/components/DeleteConnectionDialog.js.map +0 -1
  153. package/dist/HierarchicalTrackSelectorWidget/components/Header.js +0 -174
  154. package/dist/HierarchicalTrackSelectorWidget/components/Header.js.map +0 -1
  155. package/dist/HierarchicalTrackSelectorWidget/components/ManageConnectionsDialog.js.map +0 -1
  156. package/dist/HierarchicalTrackSelectorWidget/components/Node.d.ts +0 -29
  157. package/dist/HierarchicalTrackSelectorWidget/components/Node.js +0 -173
  158. package/dist/HierarchicalTrackSelectorWidget/components/Node.js.map +0 -1
  159. package/dist/HierarchicalTrackSelectorWidget/components/ToggleConnectionsDialog.js.map +0 -1
  160. package/esm/HierarchicalTrackSelectorWidget/components/CloseConnectionDialog.js.map +0 -1
  161. package/esm/HierarchicalTrackSelectorWidget/components/DeleteConnectionDialog.js.map +0 -1
  162. package/esm/HierarchicalTrackSelectorWidget/components/Header.js +0 -146
  163. package/esm/HierarchicalTrackSelectorWidget/components/Header.js.map +0 -1
  164. package/esm/HierarchicalTrackSelectorWidget/components/ManageConnectionsDialog.js.map +0 -1
  165. package/esm/HierarchicalTrackSelectorWidget/components/Node.d.ts +0 -29
  166. package/esm/HierarchicalTrackSelectorWidget/components/Node.js +0 -144
  167. package/esm/HierarchicalTrackSelectorWidget/components/Node.js.map +0 -1
  168. package/esm/HierarchicalTrackSelectorWidget/components/ToggleConnectionsDialog.js.map +0 -1
  169. package/src/HierarchicalTrackSelectorWidget/components/Header.tsx +0 -286
  170. package/src/HierarchicalTrackSelectorWidget/components/Node.tsx +0 -282
@@ -0,0 +1,86 @@
1
+ import React, { useState } from 'react'
2
+ import { Grid, IconButton, InputAdornment, TextField } from '@mui/material'
3
+ import { Menu } from '@jbrowse/core/ui'
4
+
5
+ // icons
6
+ import ClearIcon from '@mui/icons-material/Clear'
7
+ import MoreVert from '@mui/icons-material/MoreVert'
8
+
9
+ // locals
10
+ import ShoppingCart from '../ShoppingCart'
11
+ import { HierarchicalTrackSelectorModel } from '../../model'
12
+
13
+ export default function FacetedHeader({
14
+ setFilterText,
15
+ setUseShoppingCart,
16
+ setHideSparse,
17
+ hideSparse,
18
+ useShoppingCart,
19
+ filterText,
20
+ model,
21
+ }: {
22
+ setFilterText: (arg: string) => void
23
+ setUseShoppingCart: (arg: boolean) => void
24
+ setHideSparse: (arg: boolean) => void
25
+ filterText: string
26
+ useShoppingCart: boolean
27
+ hideSparse: boolean
28
+ model: HierarchicalTrackSelectorModel
29
+ }) {
30
+ const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
31
+
32
+ return (
33
+ <>
34
+ <Grid container spacing={4} alignItems="center">
35
+ <Grid item>
36
+ <TextField
37
+ label="Search..."
38
+ value={filterText}
39
+ onChange={event => setFilterText(event.target.value)}
40
+ InputProps={{
41
+ endAdornment: (
42
+ <InputAdornment position="end">
43
+ <IconButton onClick={() => setFilterText('')}>
44
+ <ClearIcon />
45
+ </IconButton>
46
+ </InputAdornment>
47
+ ),
48
+ }}
49
+ />
50
+ </Grid>
51
+
52
+ <Grid item>
53
+ <IconButton onClick={event => setAnchorEl(event.currentTarget)}>
54
+ <MoreVert />
55
+ </IconButton>
56
+ </Grid>
57
+ <Grid item>
58
+ <ShoppingCart model={model} />
59
+ </Grid>
60
+ </Grid>
61
+ <Menu
62
+ anchorEl={anchorEl}
63
+ open={!!anchorEl}
64
+ onClose={() => setAnchorEl(null)}
65
+ onMenuItemClick={(_event, callback) => {
66
+ callback()
67
+ setAnchorEl(null)
68
+ }}
69
+ menuItems={[
70
+ {
71
+ label: 'Add tracks to selection instead of turning them on/off',
72
+ onClick: () => setUseShoppingCart(!useShoppingCart),
73
+ type: 'checkbox',
74
+ checked: useShoppingCart,
75
+ },
76
+ {
77
+ label: 'Hide sparse metadata columns',
78
+ onClick: () => setHideSparse(!hideSparse),
79
+ checked: hideSparse,
80
+ type: 'checkbox',
81
+ },
82
+ ]}
83
+ />
84
+ </>
85
+ )
86
+ }
@@ -0,0 +1,339 @@
1
+ import React, { useMemo, useState, useEffect, useReducer } from 'react'
2
+ import { IconButton } from '@mui/material'
3
+ import { transaction } from 'mobx'
4
+ import { observer } from 'mobx-react'
5
+ import { getRoot, resolveIdentifier } from 'mobx-state-tree'
6
+ import { DataGrid, GridCellParams } from '@mui/x-data-grid'
7
+
8
+ // jbrowse
9
+ import { getTrackName } from '@jbrowse/core/util/tracks'
10
+ import { ResizeHandle, SanitizedHTML } from '@jbrowse/core/ui'
11
+ import JBrowseMenu from '@jbrowse/core/ui/Menu'
12
+ import ResizeBar, { useResizeBar } from '@jbrowse/core/ui/ResizeBar'
13
+ import {
14
+ getEnv,
15
+ getSession,
16
+ measureGridWidth,
17
+ useDebounce,
18
+ } from '@jbrowse/core/util'
19
+ import {
20
+ AnyConfigurationModel,
21
+ readConfObject,
22
+ } from '@jbrowse/core/configuration'
23
+
24
+ // icons
25
+ import MoreHoriz from '@mui/icons-material/MoreHoriz'
26
+
27
+ // locals
28
+ import { matches, HierarchicalTrackSelectorModel } from '../../model'
29
+ import FacetedHeader from './FacetedHeader'
30
+ import FacetFilters from './FacetFilters'
31
+ import { getRootKeys } from './util'
32
+
33
+ const nonMetadataKeys = ['category', 'adapter', 'description'] as const
34
+
35
+ export interface InfoArgs {
36
+ target: HTMLElement
37
+ id: string
38
+ conf: AnyConfigurationModel
39
+ }
40
+
41
+ const frac = 0.75
42
+
43
+ export default observer(function FacetedSelector({
44
+ model,
45
+ }: {
46
+ model: HierarchicalTrackSelectorModel
47
+ }) {
48
+ const { assemblyNames, view, selection } = model
49
+ const { pluginManager } = getEnv(model)
50
+ const { ref, scrollLeft } = useResizeBar()
51
+
52
+ const [filterText, setFilterText] = useState('')
53
+ const [info, setInfo] = useState<InfoArgs>()
54
+ const [useShoppingCart, setUseShoppingCart] = useState(false)
55
+ const [hideSparse, setHideSparse] = useState(true)
56
+ const [panelWidth, setPanelWidth] = useState(400)
57
+
58
+ const assemblyName = assemblyNames[0]
59
+ const session = getSession(model)
60
+ const filterDebounced = useDebounce(filterText, 400)
61
+ const [filters, dispatch] = useReducer(
62
+ (
63
+ state: Record<string, string[]>,
64
+ update: { key: string; val: string[] },
65
+ ) => {
66
+ return { ...state, [update.key]: update.val }
67
+ },
68
+ {},
69
+ )
70
+
71
+ const rows = useMemo(() => {
72
+ // metadata is spread onto the object for easier access and sorting
73
+ // by the mui data grid (it's unable to sort by nested objects)
74
+ return model
75
+ .trackConfigurations(assemblyName)
76
+ .filter(conf => matches(filterDebounced, conf, session))
77
+ .map(track => {
78
+ const metadata = readConfObject(track, 'metadata')
79
+ return {
80
+ id: track.trackId,
81
+ conf: track,
82
+ name: getTrackName(track, session),
83
+ category: readConfObject(track, 'category')?.join(', '),
84
+ adapter: readConfObject(track, 'adapter')?.type,
85
+ description: readConfObject(track, 'description'),
86
+ metadata,
87
+ ...metadata,
88
+ }
89
+ })
90
+ }, [assemblyName, model, filterDebounced, session])
91
+
92
+ const filteredNonMetadataKeys = useMemo(
93
+ () =>
94
+ nonMetadataKeys.filter(f =>
95
+ !hideSparse ? true : rows.map(r => r[f]).filter(f => !!f).length > 5,
96
+ ),
97
+ [hideSparse, rows],
98
+ )
99
+
100
+ const filteredMetadataKeys = useMemo(
101
+ () =>
102
+ [...new Set(rows.map(row => getRootKeys(row.metadata)).flat())].filter(
103
+ f =>
104
+ !hideSparse
105
+ ? true
106
+ : rows.map(r => r.metadata[f]).filter(f => !!f).length > 5,
107
+ ),
108
+ [hideSparse, rows],
109
+ )
110
+
111
+ const fields = useMemo(() => {
112
+ return ['name', ...filteredNonMetadataKeys, ...filteredMetadataKeys]
113
+ }, [filteredNonMetadataKeys, filteredMetadataKeys])
114
+
115
+ const [widths, setWidths] = useState({
116
+ name:
117
+ measureGridWidth(
118
+ rows.map(r => r.name),
119
+ { maxWidth: 500, stripHTML: true },
120
+ ) + 15,
121
+ ...Object.fromEntries(
122
+ filteredNonMetadataKeys.map(e => [
123
+ e,
124
+ measureGridWidth(
125
+ rows.map(r => r[e]),
126
+ { maxWidth: 400, stripHTML: true },
127
+ ),
128
+ ]),
129
+ ),
130
+ ...Object.fromEntries(
131
+ filteredMetadataKeys.map(e => [
132
+ e,
133
+ measureGridWidth(
134
+ rows.map(r => r.metadata[e]),
135
+ { maxWidth: 400, stripHTML: true },
136
+ ),
137
+ ]),
138
+ ),
139
+ } as { [key: string]: number })
140
+
141
+ const [visible, setVisible] = useState(
142
+ Object.fromEntries(fields.map(c => [c, true])),
143
+ )
144
+ useEffect(() => {
145
+ setVisible(visible => ({
146
+ ...Object.fromEntries(fields.map(c => [c, true])),
147
+ ...visible,
148
+ }))
149
+ }, [fields])
150
+
151
+ useEffect(() => {
152
+ setWidths(widths => ({
153
+ name: widths.name,
154
+ ...Object.fromEntries(
155
+ filteredNonMetadataKeys
156
+ .filter(f => visible[f])
157
+ .map(e => [
158
+ e,
159
+ measureGridWidth(
160
+ rows.map(r => r[e]),
161
+ { stripHTML: true, maxWidth: 400 },
162
+ ),
163
+ ]),
164
+ ),
165
+ ...Object.fromEntries(
166
+ filteredMetadataKeys
167
+ .filter(f => visible[f])
168
+ .map(e => [
169
+ e,
170
+ measureGridWidth(
171
+ rows.map(r => r.metadata[e]),
172
+ { stripHTML: true, maxWidth: 400 },
173
+ ),
174
+ ]),
175
+ ),
176
+ }))
177
+ }, [filteredMetadataKeys, visible, filteredNonMetadataKeys, hideSparse, rows])
178
+
179
+ const widthsDebounced = useDebounce(widths, 400)
180
+
181
+ const columns = [
182
+ {
183
+ field: 'name',
184
+ hideable: false,
185
+ renderCell: (params: GridCellParams) => {
186
+ const { value, id, row } = params
187
+ return (
188
+ <>
189
+ <SanitizedHTML html={value} />
190
+ <IconButton
191
+ onClick={e =>
192
+ setInfo({
193
+ target: e.currentTarget,
194
+ id: id as string,
195
+ conf: row.conf,
196
+ })
197
+ }
198
+ >
199
+ <MoreHoriz />
200
+ </IconButton>
201
+ </>
202
+ )
203
+ },
204
+ width: widthsDebounced.name || 100, // can be undefined before useEffect update
205
+ },
206
+ ...filteredNonMetadataKeys.map(e => ({
207
+ field: e,
208
+ width: widthsDebounced[e] || 100, // can be undefined before useEffect update
209
+ renderCell: (params: GridCellParams) => {
210
+ const { value } = params
211
+ return value ? <SanitizedHTML html={value} /> : ''
212
+ },
213
+ })),
214
+ ...filteredMetadataKeys.map(e => ({
215
+ field: e,
216
+ width: widthsDebounced[e] || 100, // can be undefined before useEffect update
217
+ renderCell: (params: GridCellParams) => {
218
+ const { value } = params
219
+ return value ? <SanitizedHTML html={value} /> : ''
220
+ },
221
+ })),
222
+ ]
223
+
224
+ const shownTrackIds = view.tracks.map(
225
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
226
+ (t: any) => t.configuration.trackId,
227
+ ) as string[]
228
+
229
+ const arrFilters = Object.entries(filters).filter(f => f[1].length > 0)
230
+ return (
231
+ <>
232
+ {info ? (
233
+ <JBrowseMenu
234
+ anchorEl={info?.target}
235
+ menuItems={session.getTrackActionMenuItems?.(info.conf) || []}
236
+ onMenuItemClick={(_event, callback) => {
237
+ callback()
238
+ setInfo(undefined)
239
+ }}
240
+ open={!!info}
241
+ onClose={() => setInfo(undefined)}
242
+ />
243
+ ) : null}
244
+ <FacetedHeader
245
+ setHideSparse={setHideSparse}
246
+ setFilterText={setFilterText}
247
+ setUseShoppingCart={setUseShoppingCart}
248
+ hideSparse={hideSparse}
249
+ filterText={filterText}
250
+ useShoppingCart={useShoppingCart}
251
+ model={model}
252
+ />
253
+
254
+ <div
255
+ ref={ref}
256
+ style={{
257
+ display: 'flex',
258
+ overflow: 'hidden',
259
+ height: window.innerHeight * frac,
260
+ width: window.innerWidth * frac,
261
+ }}
262
+ >
263
+ <div
264
+ style={{
265
+ height: window.innerHeight * frac,
266
+ width: window.innerWidth * frac - panelWidth,
267
+ }}
268
+ >
269
+ <ResizeBar
270
+ checkbox
271
+ widths={Object.values(widths)}
272
+ setWidths={newWidths =>
273
+ setWidths(
274
+ Object.fromEntries(
275
+ Object.entries(widths).map((entry, idx) => [
276
+ entry[0],
277
+ newWidths[idx],
278
+ ]),
279
+ ),
280
+ )
281
+ }
282
+ scrollLeft={scrollLeft}
283
+ />
284
+ <DataGrid
285
+ rows={rows.filter(row =>
286
+ arrFilters.every(([key, val]) => val.includes(row[key])),
287
+ )}
288
+ columnVisibilityModel={visible}
289
+ onColumnVisibilityModelChange={newModel => setVisible(newModel)}
290
+ headerHeight={35}
291
+ checkboxSelection
292
+ disableSelectionOnClick
293
+ keepNonExistentRowsSelected
294
+ onSelectionModelChange={userSelectedIds => {
295
+ if (!useShoppingCart) {
296
+ const a1 = shownTrackIds
297
+ const a2 = userSelectedIds as string[]
298
+ // synchronize the user selection with the view
299
+ // see share https://stackoverflow.com/a/33034768/2129219
300
+ transaction(() => {
301
+ a1.filter(x => !a2.includes(x)).map(t => view.hideTrack(t))
302
+ a2.filter(x => !a1.includes(x)).map(t => view.showTrack(t))
303
+ })
304
+ } else {
305
+ const root = getRoot(model)
306
+ const schema = pluginManager.pluggableConfigSchemaType('track')
307
+ const tracks = userSelectedIds.map(id =>
308
+ resolveIdentifier(schema, root, id),
309
+ )
310
+ model.setSelection(tracks)
311
+ }
312
+ }}
313
+ selectionModel={
314
+ useShoppingCart ? selection.map(s => s.trackId) : shownTrackIds
315
+ }
316
+ columns={columns}
317
+ rowHeight={25}
318
+ />
319
+ </div>
320
+ <ResizeHandle
321
+ vertical
322
+ onDrag={dist => setPanelWidth(panelWidth - dist)}
323
+ style={{ background: 'grey', width: 5 }}
324
+ />
325
+ <div
326
+ style={{ width: panelWidth, overflowY: 'auto', overflowX: 'hidden' }}
327
+ >
328
+ <FacetFilters
329
+ width={panelWidth - 10}
330
+ rows={rows}
331
+ columns={columns}
332
+ dispatch={dispatch}
333
+ filters={filters}
334
+ />
335
+ </div>
336
+ </div>
337
+ </>
338
+ )
339
+ })
@@ -0,0 +1,5 @@
1
+ export function getRootKeys(obj: Record<string, unknown>) {
2
+ return Object.entries(obj)
3
+ .map(([key, val]) => (typeof val === 'string' ? key : ''))
4
+ .filter(f => !!f)
5
+ }
@@ -0,0 +1,197 @@
1
+ import React, { Suspense, lazy, useState } from 'react'
2
+ import { IconButton } from '@mui/material'
3
+ import { makeStyles } from 'tss-react/mui'
4
+ import { observer } from 'mobx-react'
5
+ import JBrowseMenu from '@jbrowse/core/ui/Menu'
6
+ import {
7
+ getSession,
8
+ isSessionModelWithWidgets,
9
+ isSessionModelWithConnections,
10
+ } from '@jbrowse/core/util'
11
+ import {
12
+ AnyConfigurationModel,
13
+ readConfObject,
14
+ } from '@jbrowse/core/configuration'
15
+
16
+ // icons
17
+ import MenuIcon from '@mui/icons-material/Menu'
18
+
19
+ // locals
20
+ import { HierarchicalTrackSelectorModel } from '../../model'
21
+
22
+ // lazy components
23
+ const CloseConnectionDlg = lazy(
24
+ () => import('../dialogs/CloseConnectionDialog'),
25
+ )
26
+ const DeleteConnectionDlg = lazy(
27
+ () => import('../dialogs/DeleteConnectionDialog'),
28
+ )
29
+ const ManageConnectionsDlg = lazy(
30
+ () => import('../dialogs/ManageConnectionsDialog'),
31
+ )
32
+ const ToggleConnectionsDlg = lazy(
33
+ () => import('../dialogs/ToggleConnectionsDialog'),
34
+ )
35
+
36
+ const useStyles = makeStyles()(theme => ({
37
+ menuIcon: {
38
+ marginRight: theme.spacing(1),
39
+ marginBottom: 0,
40
+ },
41
+ }))
42
+
43
+ interface ModalArgs {
44
+ connectionConf: AnyConfigurationModel
45
+ safelyBreakConnection: Function
46
+ dereferenceTypeCount: { [key: string]: number }
47
+ name: string
48
+ }
49
+
50
+ interface DialogDetails {
51
+ name: string
52
+ connectionConf: AnyConfigurationModel
53
+ }
54
+
55
+ export default observer(function HamburgerMenu({
56
+ model,
57
+ setAssemblyIdx,
58
+ }: {
59
+ model: HierarchicalTrackSelectorModel
60
+ setAssemblyIdx: Function
61
+ }) {
62
+ const session = getSession(model)
63
+ const [menuEl, setMenuEl] = useState<HTMLButtonElement>()
64
+ const [modalInfo, setModalInfo] = useState<ModalArgs>()
65
+ const [deleteDlgDetails, setDeleteDlgDetails] = useState<DialogDetails>()
66
+ const [connectionToggleOpen, setConnectionToggleOpen] = useState(false)
67
+ const [connectionManagerOpen, setConnectionManagerOpen] = useState(false)
68
+ const { classes } = useStyles()
69
+ const { assemblyNames } = model
70
+
71
+ function breakConnection(
72
+ connectionConf: AnyConfigurationModel,
73
+ deletingConnection?: boolean,
74
+ ) {
75
+ const name = readConfObject(connectionConf, 'name')
76
+
77
+ // @ts-ignore
78
+ const result = session.prepareToBreakConnection(connectionConf)
79
+ if (result) {
80
+ const [safelyBreakConnection, dereferenceTypeCount] = result
81
+ if (Object.keys(dereferenceTypeCount).length > 0) {
82
+ setModalInfo({
83
+ connectionConf,
84
+ safelyBreakConnection,
85
+ dereferenceTypeCount,
86
+ name,
87
+ })
88
+ } else {
89
+ safelyBreakConnection()
90
+ }
91
+ }
92
+ if (deletingConnection) {
93
+ setDeleteDlgDetails({ name, connectionConf })
94
+ }
95
+ }
96
+
97
+ const connectionMenuItems = [
98
+ {
99
+ label: 'Turn on/off connections...',
100
+ onClick: () => setConnectionToggleOpen(true),
101
+ },
102
+ ]
103
+
104
+ if (isSessionModelWithConnections(session)) {
105
+ connectionMenuItems.unshift({
106
+ label: 'Add connection...',
107
+ onClick: () => {
108
+ if (isSessionModelWithWidgets(session)) {
109
+ session.showWidget(
110
+ session.addWidget('AddConnectionWidget', 'addConnectionWidget'),
111
+ )
112
+ }
113
+ },
114
+ })
115
+
116
+ connectionMenuItems.push({
117
+ label: 'Delete connections...',
118
+ onClick: () => setConnectionManagerOpen(true),
119
+ })
120
+ }
121
+ return (
122
+ <>
123
+ <IconButton
124
+ className={classes.menuIcon}
125
+ onClick={event => setMenuEl(event.currentTarget)}
126
+ >
127
+ <MenuIcon />
128
+ </IconButton>
129
+
130
+ <JBrowseMenu
131
+ anchorEl={menuEl}
132
+ open={Boolean(menuEl)}
133
+ onMenuItemClick={(_, callback) => {
134
+ callback()
135
+ setMenuEl(undefined)
136
+ }}
137
+ onClose={() => setMenuEl(undefined)}
138
+ menuItems={[
139
+ {
140
+ label: 'Add track...',
141
+ onClick: () => {
142
+ if (isSessionModelWithWidgets(session)) {
143
+ session.showWidget(
144
+ session.addWidget('AddTrackWidget', 'addTrackWidget', {
145
+ view: model.view.id,
146
+ }),
147
+ )
148
+ }
149
+ },
150
+ },
151
+ ...(session.makeConnection ? connectionMenuItems : []),
152
+
153
+ ...(assemblyNames.length > 1
154
+ ? [
155
+ {
156
+ label: 'Select assembly...',
157
+ subMenu: assemblyNames.map((name, idx) => ({
158
+ label: name,
159
+ onClick: () => setAssemblyIdx(idx),
160
+ })),
161
+ },
162
+ ]
163
+ : []),
164
+ ]}
165
+ />
166
+ <Suspense fallback={<div />}>
167
+ {modalInfo ? (
168
+ <CloseConnectionDlg
169
+ modalInfo={modalInfo}
170
+ setModalInfo={setModalInfo}
171
+ />
172
+ ) : null}
173
+ {deleteDlgDetails ? (
174
+ <DeleteConnectionDlg
175
+ handleClose={() => setDeleteDlgDetails(undefined)}
176
+ deleteDialogDetails={deleteDlgDetails}
177
+ session={session}
178
+ />
179
+ ) : null}
180
+ {connectionManagerOpen ? (
181
+ <ManageConnectionsDlg
182
+ handleClose={() => setConnectionManagerOpen(false)}
183
+ breakConnection={breakConnection}
184
+ session={session}
185
+ />
186
+ ) : null}
187
+ {connectionToggleOpen ? (
188
+ <ToggleConnectionsDlg
189
+ handleClose={() => setConnectionToggleOpen(false)}
190
+ session={session}
191
+ breakConnection={breakConnection}
192
+ />
193
+ ) : null}
194
+ </Suspense>
195
+ </>
196
+ )
197
+ })