@jbrowse/plugin-data-management 2.3.3 → 2.4.0

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 (200) hide show
  1. package/dist/AddConnectionWidget/components/ConnectionTypeSelect.js +1 -1
  2. package/dist/AddConnectionWidget/components/ConnectionTypeSelect.js.map +1 -1
  3. package/dist/AddTrackWidget/components/DefaultAddTrackWorkflow.js +1 -4
  4. package/dist/AddTrackWidget/components/DefaultAddTrackWorkflow.js.map +1 -1
  5. package/dist/AddTrackWidget/components/TextIndexingConfig.js +2 -2
  6. package/dist/AddTrackWidget/components/TextIndexingConfig.js.map +1 -1
  7. package/dist/AddTrackWidget/components/TrackSourceSelect.js +1 -4
  8. package/dist/AddTrackWidget/components/TrackSourceSelect.js.map +1 -1
  9. package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalFab.d.ts +6 -0
  10. package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalFab.js +71 -0
  11. package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalFab.js.map +1 -0
  12. package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js +10 -104
  13. package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js.map +1 -1
  14. package/dist/HierarchicalTrackSelectorWidget/components/ShoppingCart.d.ts +6 -0
  15. package/dist/HierarchicalTrackSelectorWidget/components/ShoppingCart.js +70 -0
  16. package/dist/HierarchicalTrackSelectorWidget/components/ShoppingCart.js.map +1 -0
  17. package/dist/HierarchicalTrackSelectorWidget/components/dialogs/CloseConnectionDialog.js.map +1 -0
  18. package/dist/HierarchicalTrackSelectorWidget/components/dialogs/DeleteConnectionDialog.js.map +1 -0
  19. package/dist/HierarchicalTrackSelectorWidget/components/dialogs/ManageConnectionsDialog.js.map +1 -0
  20. package/dist/HierarchicalTrackSelectorWidget/components/dialogs/ToggleConnectionsDialog.js.map +1 -0
  21. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.d.ts +13 -0
  22. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js +99 -0
  23. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js.map +1 -0
  24. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.d.ts +8 -0
  25. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.js +18 -0
  26. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.js.map +1 -0
  27. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.d.ts +11 -0
  28. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js +71 -0
  29. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js.map +1 -0
  30. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.d.ts +12 -0
  31. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js +211 -0
  32. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js.map +1 -0
  33. package/dist/HierarchicalTrackSelectorWidget/components/faceted/util.d.ts +1 -0
  34. package/dist/HierarchicalTrackSelectorWidget/components/faceted/util.js +10 -0
  35. package/dist/HierarchicalTrackSelectorWidget/components/faceted/util.js.map +1 -0
  36. package/dist/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.d.ts +7 -0
  37. package/dist/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js +136 -0
  38. package/dist/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js.map +1 -0
  39. package/dist/HierarchicalTrackSelectorWidget/components/{Header.d.ts → tree/HierarchicalHeader.d.ts} +2 -3
  40. package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js +65 -0
  41. package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js.map +1 -0
  42. package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.d.ts +8 -0
  43. package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js +91 -0
  44. package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js.map +1 -0
  45. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.d.ts +7 -0
  46. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js +89 -0
  47. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js.map +1 -0
  48. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.d.ts +11 -0
  49. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js +86 -0
  50. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js.map +1 -0
  51. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.d.ts +10 -0
  52. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.js +54 -0
  53. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.js.map +1 -0
  54. package/dist/HierarchicalTrackSelectorWidget/components/util.d.ts +17 -1
  55. package/dist/HierarchicalTrackSelectorWidget/components/util.js +16 -1
  56. package/dist/HierarchicalTrackSelectorWidget/components/util.js.map +1 -1
  57. package/dist/HierarchicalTrackSelectorWidget/model.d.ts +4 -1
  58. package/dist/HierarchicalTrackSelectorWidget/model.js +12 -8
  59. package/dist/HierarchicalTrackSelectorWidget/model.js.map +1 -1
  60. package/dist/PluginStoreWidget/components/InstalledPluginsList.js.map +1 -1
  61. package/dist/PluginStoreWidget/components/PluginCard.d.ts +2 -3
  62. package/dist/PluginStoreWidget/components/PluginCard.js +5 -6
  63. package/dist/PluginStoreWidget/components/PluginCard.js.map +1 -1
  64. package/dist/PluginStoreWidget/components/PluginStoreWidget.js +3 -3
  65. package/dist/PluginStoreWidget/components/PluginStoreWidget.js.map +1 -1
  66. package/dist/ucsc-trackhub/ucscTrackHub.js.map +1 -1
  67. package/esm/AddConnectionWidget/components/ConnectionTypeSelect.js +1 -1
  68. package/esm/AddConnectionWidget/components/ConnectionTypeSelect.js.map +1 -1
  69. package/esm/AddTrackWidget/components/DefaultAddTrackWorkflow.js +1 -4
  70. package/esm/AddTrackWidget/components/DefaultAddTrackWorkflow.js.map +1 -1
  71. package/esm/AddTrackWidget/components/TextIndexingConfig.js +2 -2
  72. package/esm/AddTrackWidget/components/TextIndexingConfig.js.map +1 -1
  73. package/esm/AddTrackWidget/components/TrackSourceSelect.js +1 -4
  74. package/esm/AddTrackWidget/components/TrackSourceSelect.js.map +1 -1
  75. package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalFab.d.ts +6 -0
  76. package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalFab.js +43 -0
  77. package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalFab.js.map +1 -0
  78. package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js +9 -103
  79. package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.js.map +1 -1
  80. package/esm/HierarchicalTrackSelectorWidget/components/ShoppingCart.d.ts +6 -0
  81. package/esm/HierarchicalTrackSelectorWidget/components/ShoppingCart.js +42 -0
  82. package/esm/HierarchicalTrackSelectorWidget/components/ShoppingCart.js.map +1 -0
  83. package/esm/HierarchicalTrackSelectorWidget/components/dialogs/CloseConnectionDialog.js.map +1 -0
  84. package/esm/HierarchicalTrackSelectorWidget/components/dialogs/DeleteConnectionDialog.js.map +1 -0
  85. package/esm/HierarchicalTrackSelectorWidget/components/dialogs/ManageConnectionsDialog.js.map +1 -0
  86. package/esm/HierarchicalTrackSelectorWidget/components/dialogs/ToggleConnectionsDialog.js.map +1 -0
  87. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.d.ts +13 -0
  88. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js +70 -0
  89. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js.map +1 -0
  90. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.d.ts +8 -0
  91. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.js +13 -0
  92. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.js.map +1 -0
  93. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.d.ts +11 -0
  94. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js +42 -0
  95. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js.map +1 -0
  96. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.d.ts +12 -0
  97. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js +183 -0
  98. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js.map +1 -0
  99. package/esm/HierarchicalTrackSelectorWidget/components/faceted/util.d.ts +1 -0
  100. package/esm/HierarchicalTrackSelectorWidget/components/faceted/util.js +6 -0
  101. package/esm/HierarchicalTrackSelectorWidget/components/faceted/util.js.map +1 -0
  102. package/esm/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.d.ts +7 -0
  103. package/esm/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js +108 -0
  104. package/esm/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js.map +1 -0
  105. package/esm/HierarchicalTrackSelectorWidget/components/{Header.d.ts → tree/HierarchicalHeader.d.ts} +2 -3
  106. package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js +37 -0
  107. package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js.map +1 -0
  108. package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.d.ts +8 -0
  109. package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js +63 -0
  110. package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js.map +1 -0
  111. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.d.ts +7 -0
  112. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js +60 -0
  113. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js.map +1 -0
  114. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.d.ts +11 -0
  115. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js +57 -0
  116. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js.map +1 -0
  117. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.d.ts +10 -0
  118. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.js +48 -0
  119. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.js.map +1 -0
  120. package/esm/HierarchicalTrackSelectorWidget/components/util.d.ts +17 -1
  121. package/esm/HierarchicalTrackSelectorWidget/components/util.js +13 -0
  122. package/esm/HierarchicalTrackSelectorWidget/components/util.js.map +1 -1
  123. package/esm/HierarchicalTrackSelectorWidget/model.d.ts +4 -1
  124. package/esm/HierarchicalTrackSelectorWidget/model.js +10 -7
  125. package/esm/HierarchicalTrackSelectorWidget/model.js.map +1 -1
  126. package/esm/PluginStoreWidget/components/InstalledPluginsList.js.map +1 -1
  127. package/esm/PluginStoreWidget/components/PluginCard.d.ts +2 -3
  128. package/esm/PluginStoreWidget/components/PluginCard.js +5 -6
  129. package/esm/PluginStoreWidget/components/PluginCard.js.map +1 -1
  130. package/esm/PluginStoreWidget/components/PluginStoreWidget.js +3 -3
  131. package/esm/PluginStoreWidget/components/PluginStoreWidget.js.map +1 -1
  132. package/esm/ucsc-trackhub/ucscTrackHub.js.map +1 -1
  133. package/package.json +3 -2
  134. package/src/AddConnectionWidget/components/ConnectionTypeSelect.tsx +0 -1
  135. package/src/AddConnectionWidget/components/__snapshots__/AddConnectionWidget.test.tsx.snap +1 -1
  136. package/src/AddTrackWidget/components/DefaultAddTrackWorkflow.tsx +1 -4
  137. package/src/AddTrackWidget/components/TextIndexingConfig.tsx +0 -2
  138. package/src/AddTrackWidget/components/TrackSourceSelect.tsx +3 -5
  139. package/src/HierarchicalTrackSelectorWidget/components/HierarchicalFab.tsx +94 -0
  140. package/src/HierarchicalTrackSelectorWidget/components/HierarchicalTrackSelector.tsx +47 -228
  141. package/src/HierarchicalTrackSelectorWidget/components/ShoppingCart.tsx +73 -0
  142. package/src/HierarchicalTrackSelectorWidget/components/__snapshots__/HierarchicalTrackSelector.test.tsx.snap +24 -42
  143. package/src/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.tsx +138 -0
  144. package/src/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.tsx +29 -0
  145. package/src/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.tsx +86 -0
  146. package/src/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.tsx +339 -0
  147. package/src/HierarchicalTrackSelectorWidget/components/faceted/util.ts +5 -0
  148. package/src/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.tsx +197 -0
  149. package/src/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.tsx +85 -0
  150. package/src/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.tsx +101 -0
  151. package/src/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.tsx +92 -0
  152. package/src/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.tsx +106 -0
  153. package/src/HierarchicalTrackSelectorWidget/components/tree/TrackListNode.tsx +84 -0
  154. package/src/HierarchicalTrackSelectorWidget/components/util.ts +31 -1
  155. package/src/HierarchicalTrackSelectorWidget/model.ts +12 -9
  156. package/src/PluginStoreWidget/components/InstalledPluginsList.tsx +1 -1
  157. package/src/PluginStoreWidget/components/PluginCard.tsx +6 -7
  158. package/src/PluginStoreWidget/components/PluginStoreWidget.tsx +3 -7
  159. package/src/PluginStoreWidget/components/__snapshots__/PluginStoreWidget.test.tsx.snap +1 -1
  160. package/src/ucsc-trackhub/ucscTrackHub.ts +1 -1
  161. package/dist/HierarchicalTrackSelectorWidget/components/CloseConnectionDialog.js.map +0 -1
  162. package/dist/HierarchicalTrackSelectorWidget/components/DeleteConnectionDialog.js.map +0 -1
  163. package/dist/HierarchicalTrackSelectorWidget/components/Header.js +0 -174
  164. package/dist/HierarchicalTrackSelectorWidget/components/Header.js.map +0 -1
  165. package/dist/HierarchicalTrackSelectorWidget/components/ManageConnectionsDialog.js.map +0 -1
  166. package/dist/HierarchicalTrackSelectorWidget/components/Node.d.ts +0 -29
  167. package/dist/HierarchicalTrackSelectorWidget/components/Node.js +0 -173
  168. package/dist/HierarchicalTrackSelectorWidget/components/Node.js.map +0 -1
  169. package/dist/HierarchicalTrackSelectorWidget/components/ToggleConnectionsDialog.js.map +0 -1
  170. package/esm/HierarchicalTrackSelectorWidget/components/CloseConnectionDialog.js.map +0 -1
  171. package/esm/HierarchicalTrackSelectorWidget/components/DeleteConnectionDialog.js.map +0 -1
  172. package/esm/HierarchicalTrackSelectorWidget/components/Header.js +0 -146
  173. package/esm/HierarchicalTrackSelectorWidget/components/Header.js.map +0 -1
  174. package/esm/HierarchicalTrackSelectorWidget/components/ManageConnectionsDialog.js.map +0 -1
  175. package/esm/HierarchicalTrackSelectorWidget/components/Node.d.ts +0 -29
  176. package/esm/HierarchicalTrackSelectorWidget/components/Node.js +0 -144
  177. package/esm/HierarchicalTrackSelectorWidget/components/Node.js.map +0 -1
  178. package/esm/HierarchicalTrackSelectorWidget/components/ToggleConnectionsDialog.js.map +0 -1
  179. package/src/HierarchicalTrackSelectorWidget/components/Header.tsx +0 -286
  180. package/src/HierarchicalTrackSelectorWidget/components/Node.tsx +0 -282
  181. /package/dist/HierarchicalTrackSelectorWidget/components/{CloseConnectionDialog.d.ts → dialogs/CloseConnectionDialog.d.ts} +0 -0
  182. /package/dist/HierarchicalTrackSelectorWidget/components/{CloseConnectionDialog.js → dialogs/CloseConnectionDialog.js} +0 -0
  183. /package/dist/HierarchicalTrackSelectorWidget/components/{DeleteConnectionDialog.d.ts → dialogs/DeleteConnectionDialog.d.ts} +0 -0
  184. /package/dist/HierarchicalTrackSelectorWidget/components/{DeleteConnectionDialog.js → dialogs/DeleteConnectionDialog.js} +0 -0
  185. /package/dist/HierarchicalTrackSelectorWidget/components/{ManageConnectionsDialog.d.ts → dialogs/ManageConnectionsDialog.d.ts} +0 -0
  186. /package/dist/HierarchicalTrackSelectorWidget/components/{ManageConnectionsDialog.js → dialogs/ManageConnectionsDialog.js} +0 -0
  187. /package/dist/HierarchicalTrackSelectorWidget/components/{ToggleConnectionsDialog.d.ts → dialogs/ToggleConnectionsDialog.d.ts} +0 -0
  188. /package/dist/HierarchicalTrackSelectorWidget/components/{ToggleConnectionsDialog.js → dialogs/ToggleConnectionsDialog.js} +0 -0
  189. /package/esm/HierarchicalTrackSelectorWidget/components/{CloseConnectionDialog.d.ts → dialogs/CloseConnectionDialog.d.ts} +0 -0
  190. /package/esm/HierarchicalTrackSelectorWidget/components/{CloseConnectionDialog.js → dialogs/CloseConnectionDialog.js} +0 -0
  191. /package/esm/HierarchicalTrackSelectorWidget/components/{DeleteConnectionDialog.d.ts → dialogs/DeleteConnectionDialog.d.ts} +0 -0
  192. /package/esm/HierarchicalTrackSelectorWidget/components/{DeleteConnectionDialog.js → dialogs/DeleteConnectionDialog.js} +0 -0
  193. /package/esm/HierarchicalTrackSelectorWidget/components/{ManageConnectionsDialog.d.ts → dialogs/ManageConnectionsDialog.d.ts} +0 -0
  194. /package/esm/HierarchicalTrackSelectorWidget/components/{ManageConnectionsDialog.js → dialogs/ManageConnectionsDialog.js} +0 -0
  195. /package/esm/HierarchicalTrackSelectorWidget/components/{ToggleConnectionsDialog.d.ts → dialogs/ToggleConnectionsDialog.d.ts} +0 -0
  196. /package/esm/HierarchicalTrackSelectorWidget/components/{ToggleConnectionsDialog.js → dialogs/ToggleConnectionsDialog.js} +0 -0
  197. /package/src/HierarchicalTrackSelectorWidget/components/{CloseConnectionDialog.tsx → dialogs/CloseConnectionDialog.tsx} +0 -0
  198. /package/src/HierarchicalTrackSelectorWidget/components/{DeleteConnectionDialog.tsx → dialogs/DeleteConnectionDialog.tsx} +0 -0
  199. /package/src/HierarchicalTrackSelectorWidget/components/{ManageConnectionsDialog.tsx → dialogs/ManageConnectionsDialog.tsx} +0 -0
  200. /package/src/HierarchicalTrackSelectorWidget/components/{ToggleConnectionsDialog.tsx → dialogs/ToggleConnectionsDialog.tsx} +0 -0
@@ -0,0 +1,138 @@
1
+ import React, { useState } from 'react'
2
+ import {
3
+ Typography,
4
+ FormControl,
5
+ Select,
6
+ IconButton,
7
+ Tooltip,
8
+ } from '@mui/material'
9
+ import { makeStyles } from 'tss-react/mui'
10
+
11
+ // icon
12
+ import ClearIcon from '@mui/icons-material/Clear'
13
+ import MinimizeIcon from '@mui/icons-material/Minimize'
14
+ import AddIcon from '@mui/icons-material/Add'
15
+
16
+ const useStyles = makeStyles()(theme => ({
17
+ facet: {
18
+ margin: 0,
19
+ marginLeft: theme.spacing(2),
20
+ },
21
+ select: {
22
+ marginBottom: theme.spacing(2),
23
+ },
24
+ }))
25
+
26
+ function FacetFilter({
27
+ column,
28
+ vals,
29
+ width,
30
+ dispatch,
31
+ filters,
32
+ }: {
33
+ column: { field: string }
34
+ vals: [string, number][]
35
+ width: number
36
+ dispatch: (arg: { key: string; val: string[] }) => void
37
+ filters: Record<string, string[]>
38
+ }) {
39
+ const { classes } = useStyles()
40
+ const [visible, setVisible] = useState(true)
41
+ return (
42
+ <FormControl key={column.field} className={classes.facet} style={{ width }}>
43
+ <div style={{ display: 'flex' }}>
44
+ <Typography>{column.field}</Typography>
45
+ <Tooltip title="Clear selection on this facet filter">
46
+ <IconButton
47
+ onClick={() => {
48
+ dispatch({ key: column.field, val: [] })
49
+ }}
50
+ size="small"
51
+ >
52
+ <ClearIcon />
53
+ </IconButton>
54
+ </Tooltip>
55
+ <Tooltip title="Minimize/expand this facet filter">
56
+ <IconButton onClick={() => setVisible(!visible)} size="small">
57
+ {visible ? <MinimizeIcon /> : <AddIcon />}
58
+ </IconButton>
59
+ </Tooltip>
60
+ </div>
61
+ {visible ? (
62
+ <Select
63
+ multiple
64
+ native
65
+ className={classes.select}
66
+ value={filters[column.field]}
67
+ onChange={event => {
68
+ // @ts-ignore
69
+ const { options } = event.target
70
+ const val: string[] = []
71
+ const len = options.length
72
+ for (let i = 0; i < len; i++) {
73
+ if (options[i].selected) {
74
+ val.push(options[i].value)
75
+ }
76
+ }
77
+ dispatch({ key: column.field, val })
78
+ }}
79
+ >
80
+ {vals
81
+ .sort((a, b) => a[0].localeCompare(b[0]))
82
+ .map(([name, count]) => (
83
+ <option key={name} value={name}>
84
+ {name} ({count})
85
+ </option>
86
+ ))}
87
+ </Select>
88
+ ) : null}
89
+ </FormControl>
90
+ )
91
+ }
92
+
93
+ export default function FacetFilters({
94
+ rows,
95
+ columns,
96
+ dispatch,
97
+ filters,
98
+ width,
99
+ }: {
100
+ rows: Record<string, unknown>[]
101
+ filters: Record<string, string[]>
102
+ columns: { field: string }[]
103
+ dispatch: (arg: { key: string; val: string[] }) => void
104
+ width: number
105
+ }) {
106
+ const facets = columns.slice(1)
107
+ const uniqs = facets.map(() => new Map<string, number>())
108
+ rows.forEach(row => {
109
+ facets.forEach((column, index) => {
110
+ const elt = uniqs[index]
111
+ const key = `${row[column.field] || ''}`
112
+ const val = elt.get(key)
113
+ // we don't allow filtering on empty yet
114
+ if (key) {
115
+ if (val !== undefined) {
116
+ elt.set(key, val + 1)
117
+ } else {
118
+ elt.set(key, 1)
119
+ }
120
+ }
121
+ })
122
+ })
123
+
124
+ return (
125
+ <div>
126
+ {facets.map((column, index) => (
127
+ <FacetFilter
128
+ key={column.field}
129
+ vals={Array.from(uniqs[index])}
130
+ column={column}
131
+ width={width}
132
+ dispatch={dispatch}
133
+ filters={filters}
134
+ />
135
+ ))}
136
+ </div>
137
+ )
138
+ }
@@ -0,0 +1,29 @@
1
+ import React from 'react'
2
+ import { DialogContent } from '@mui/material'
3
+ import { Dialog } from '@jbrowse/core/ui'
4
+ import { observer } from 'mobx-react'
5
+
6
+ // locals
7
+ import { HierarchicalTrackSelectorModel } from '../../model'
8
+ import FacetedSelector from './FacetedSelector'
9
+
10
+ function FacetedDlg(props: {
11
+ handleClose: () => void
12
+ model: HierarchicalTrackSelectorModel
13
+ }) {
14
+ const { handleClose } = props
15
+ return (
16
+ <Dialog
17
+ open
18
+ onClose={handleClose}
19
+ maxWidth="xl"
20
+ title="Faceted track selector"
21
+ >
22
+ <DialogContent>
23
+ <FacetedSelector {...props} />
24
+ </DialogContent>
25
+ </Dialog>
26
+ )
27
+ }
28
+
29
+ export default observer(FacetedDlg)
@@ -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
+ }