@jbrowse/plugin-data-management 2.8.0 → 2.10.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.
- package/dist/AddTrackWidget/components/DefaultAddTrackWorkflow.js +4 -8
- package/dist/AddTrackWidget/model.d.ts +81 -0
- package/dist/AddTrackWidget/model.js +81 -0
- package/dist/HierarchicalTrackSelectorWidget/components/ShoppingCart.d.ts +1 -1
- package/dist/HierarchicalTrackSelectorWidget/components/ShoppingCart.js +11 -53
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.d.ts +5 -7
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.js +12 -11
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.d.ts +5 -7
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js +11 -8
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.d.ts +1 -11
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js +9 -7
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js +40 -135
- package/dist/HierarchicalTrackSelectorWidget/components/tree/DropdownTrackSelector.d.ts +12 -0
- package/dist/HierarchicalTrackSelectorWidget/components/tree/DropdownTrackSelector.js +59 -0
- package/dist/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.d.ts +6 -0
- package/dist/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.js +45 -0
- package/dist/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js +71 -46
- package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js +5 -34
- package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js +8 -3
- package/dist/HierarchicalTrackSelectorWidget/components/tree/RecentlyUsedTracks.d.ts +6 -0
- package/dist/HierarchicalTrackSelectorWidget/components/tree/RecentlyUsedTracks.js +42 -0
- package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js +7 -6
- package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js +9 -28
- package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabelMenu.d.ts +12 -0
- package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabelMenu.js +50 -0
- package/dist/HierarchicalTrackSelectorWidget/components/util.d.ts +3 -0
- package/dist/HierarchicalTrackSelectorWidget/components/util.js +5 -1
- package/dist/HierarchicalTrackSelectorWidget/facetedModel.d.ts +128 -0
- package/dist/HierarchicalTrackSelectorWidget/facetedModel.js +206 -0
- package/dist/HierarchicalTrackSelectorWidget/facetedUtil.d.ts +2 -0
- package/dist/HierarchicalTrackSelectorWidget/{components/faceted/util.js → facetedUtil.js} +5 -1
- package/dist/HierarchicalTrackSelectorWidget/generateHierarchy.d.ts +17 -5
- package/dist/HierarchicalTrackSelectorWidget/generateHierarchy.js +27 -21
- package/dist/HierarchicalTrackSelectorWidget/model.d.ts +193 -15
- package/dist/HierarchicalTrackSelectorWidget/model.js +209 -22
- package/dist/ucsc-trackhub/doConnect.d.ts +1 -0
- package/dist/ucsc-trackhub/doConnect.js +131 -0
- package/dist/ucsc-trackhub/model.d.ts +19 -2
- package/dist/ucsc-trackhub/model.js +16 -71
- package/dist/ucsc-trackhub/ucscTrackHub.d.ts +161 -4
- package/dist/ucsc-trackhub/ucscTrackHub.js +49 -166
- package/esm/AddTrackWidget/components/DefaultAddTrackWorkflow.js +4 -8
- package/esm/AddTrackWidget/model.d.ts +81 -0
- package/esm/AddTrackWidget/model.js +81 -0
- package/esm/HierarchicalTrackSelectorWidget/components/ShoppingCart.d.ts +1 -1
- package/esm/HierarchicalTrackSelectorWidget/components/ShoppingCart.js +12 -31
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.d.ts +5 -7
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.js +13 -11
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.d.ts +5 -7
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js +12 -8
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.d.ts +1 -11
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js +9 -7
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js +41 -113
- package/esm/HierarchicalTrackSelectorWidget/components/tree/DropdownTrackSelector.d.ts +12 -0
- package/esm/HierarchicalTrackSelectorWidget/components/tree/DropdownTrackSelector.js +31 -0
- package/esm/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.d.ts +6 -0
- package/esm/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.js +40 -0
- package/esm/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js +71 -46
- package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js +6 -12
- package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js +8 -3
- package/esm/HierarchicalTrackSelectorWidget/components/tree/RecentlyUsedTracks.d.ts +6 -0
- package/esm/HierarchicalTrackSelectorWidget/components/tree/RecentlyUsedTracks.js +37 -0
- package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js +7 -6
- package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js +8 -27
- package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabelMenu.d.ts +12 -0
- package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabelMenu.js +45 -0
- package/esm/HierarchicalTrackSelectorWidget/components/util.d.ts +3 -0
- package/esm/HierarchicalTrackSelectorWidget/components/util.js +5 -1
- package/esm/HierarchicalTrackSelectorWidget/facetedModel.d.ts +128 -0
- package/esm/HierarchicalTrackSelectorWidget/facetedModel.js +202 -0
- package/esm/HierarchicalTrackSelectorWidget/facetedUtil.d.ts +2 -0
- package/esm/HierarchicalTrackSelectorWidget/{components/faceted/util.js → facetedUtil.js} +3 -0
- package/esm/HierarchicalTrackSelectorWidget/generateHierarchy.d.ts +17 -5
- package/esm/HierarchicalTrackSelectorWidget/generateHierarchy.js +27 -21
- package/esm/HierarchicalTrackSelectorWidget/model.d.ts +193 -15
- package/esm/HierarchicalTrackSelectorWidget/model.js +211 -24
- package/esm/ucsc-trackhub/doConnect.d.ts +1 -0
- package/esm/ucsc-trackhub/doConnect.js +127 -0
- package/esm/ucsc-trackhub/model.d.ts +19 -2
- package/esm/ucsc-trackhub/model.js +17 -72
- package/esm/ucsc-trackhub/ucscTrackHub.d.ts +161 -4
- package/esm/ucsc-trackhub/ucscTrackHub.js +48 -141
- package/package.json +3 -4
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/util.d.ts +0 -1
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/util.d.ts +0 -1
|
@@ -1,126 +1,50 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import { IconButton } from '@mui/material';
|
|
1
|
+
import React from 'react';
|
|
3
2
|
import { transaction } from 'mobx';
|
|
4
3
|
import { observer } from 'mobx-react';
|
|
5
4
|
import { getRoot, resolveIdentifier } from 'mobx-state-tree';
|
|
6
5
|
import { DataGrid, GridToolbar } from '@mui/x-data-grid';
|
|
7
6
|
// jbrowse
|
|
8
|
-
import { getTrackName } from '@jbrowse/core/util/tracks';
|
|
9
7
|
import { ResizeHandle } from '@jbrowse/core/ui';
|
|
10
8
|
import SanitizedHTML from '@jbrowse/core/ui/SanitizedHTML';
|
|
11
|
-
import JBrowseMenu from '@jbrowse/core/ui/Menu';
|
|
12
9
|
import ResizeBar from '@jbrowse/core/ui/ResizeBar';
|
|
13
|
-
import { getEnv,
|
|
14
|
-
import { readConfObject, } from '@jbrowse/core/configuration';
|
|
10
|
+
import { getEnv, useDebounce } from '@jbrowse/core/util';
|
|
15
11
|
import { useResizeBar } from '@jbrowse/core/ui/useResizeBar';
|
|
16
12
|
import { makeStyles } from 'tss-react/mui';
|
|
17
|
-
// icons
|
|
18
|
-
import MoreHoriz from '@mui/icons-material/MoreHoriz';
|
|
19
|
-
import { matches } from '../../util';
|
|
20
13
|
import FacetedHeader from './FacetedHeader';
|
|
21
14
|
import FacetFilters from './FacetFilters';
|
|
22
|
-
import
|
|
23
|
-
const nonMetadataKeys = ['category', 'adapter', 'description'];
|
|
15
|
+
import TrackLabelMenu from '../tree/TrackLabelMenu';
|
|
24
16
|
const useStyles = makeStyles()({
|
|
25
17
|
cell: {
|
|
26
18
|
whiteSpace: 'nowrap',
|
|
27
19
|
overflow: 'hidden',
|
|
28
20
|
textOverflow: 'ellipsis',
|
|
29
21
|
},
|
|
22
|
+
resizeHandle: {
|
|
23
|
+
marginLeft: 5,
|
|
24
|
+
background: 'grey',
|
|
25
|
+
width: 5,
|
|
26
|
+
},
|
|
30
27
|
});
|
|
31
28
|
const frac = 0.75;
|
|
32
29
|
const FacetedSelector = observer(function FacetedSelector({ model, }) {
|
|
33
|
-
var _a
|
|
30
|
+
var _a;
|
|
34
31
|
const { classes } = useStyles();
|
|
35
|
-
const { view, selection } = model;
|
|
32
|
+
const { view, selection, faceted } = model;
|
|
33
|
+
const { rows, panelWidth, showFilters, useShoppingCart, showOptions, filteredRows, filteredNonMetadataKeys, filteredMetadataKeys, visible, widths, } = faceted;
|
|
36
34
|
const { pluginManager } = getEnv(model);
|
|
37
35
|
const { ref, scrollLeft } = useResizeBar();
|
|
38
|
-
const [filterText, setFilterText] = useState('');
|
|
39
|
-
const [showOptions, setShowOptions] = useLocalStorage('facet-showTableOptions', false);
|
|
40
|
-
const [info, setInfo] = useState();
|
|
41
|
-
const [useShoppingCart, setUseShoppingCart] = useState(false);
|
|
42
|
-
const [showSparse, setShowSparse] = useLocalStorage('facet-showSparse', false);
|
|
43
|
-
const [showFilters, setShowFilters] = useLocalStorage('facet-showFilters', true);
|
|
44
|
-
const [panelWidth, setPanelWidth] = useLocalStorage('facet-panelWidth', 400);
|
|
45
|
-
const session = getSession(model);
|
|
46
|
-
const filterDebounced = useDebounce(filterText, 400);
|
|
47
36
|
const tracks = view.tracks;
|
|
48
|
-
const [filters, dispatch] = useReducer((state, update) => ({ ...state, [update.key]: update.val }), {});
|
|
49
|
-
const rows = useMemo(() => {
|
|
50
|
-
// metadata is spread onto the object for easier access and sorting
|
|
51
|
-
// by the mui data grid (it's unable to sort by nested objects)
|
|
52
|
-
return model.trackConfigurations
|
|
53
|
-
.filter(conf => matches(filterDebounced, conf, session))
|
|
54
|
-
.map(track => {
|
|
55
|
-
var _a, _b;
|
|
56
|
-
const metadata = readConfObject(track, 'metadata');
|
|
57
|
-
return {
|
|
58
|
-
id: track.trackId,
|
|
59
|
-
conf: track,
|
|
60
|
-
name: getTrackName(track, session),
|
|
61
|
-
category: (_a = readConfObject(track, 'category')) === null || _a === void 0 ? void 0 : _a.join(', '),
|
|
62
|
-
adapter: (_b = readConfObject(track, 'adapter')) === null || _b === void 0 ? void 0 : _b.type,
|
|
63
|
-
description: readConfObject(track, 'description'),
|
|
64
|
-
metadata,
|
|
65
|
-
...metadata,
|
|
66
|
-
};
|
|
67
|
-
});
|
|
68
|
-
}, [model, filterDebounced, session]);
|
|
69
|
-
const filteredNonMetadataKeys = useMemo(() => nonMetadataKeys.filter(f => showSparse ? true : rows.map(r => r[f]).filter(f => !!f).length > 5), [showSparse, rows]);
|
|
70
|
-
const filteredMetadataKeys = useMemo(() => [...new Set(rows.flatMap(row => getRootKeys(row.metadata)))].filter(f => showSparse
|
|
71
|
-
? true
|
|
72
|
-
: rows.map(r => r.metadata[f]).filter(f => !!f).length > 5), [showSparse, rows]);
|
|
73
|
-
const fields = useMemo(() => ['name', ...filteredNonMetadataKeys, ...filteredMetadataKeys], [filteredNonMetadataKeys, filteredMetadataKeys]);
|
|
74
|
-
const [widths, setWidths] = useState({
|
|
75
|
-
name: measureGridWidth(rows.map(r => r.name), { maxWidth: 500, stripHTML: true }) + 15,
|
|
76
|
-
...Object.fromEntries(filteredNonMetadataKeys.map(e => [
|
|
77
|
-
e,
|
|
78
|
-
measureGridWidth(rows.map(r => r[e]), { maxWidth: 400, stripHTML: true }),
|
|
79
|
-
])),
|
|
80
|
-
...Object.fromEntries(filteredMetadataKeys.map(e => [
|
|
81
|
-
e,
|
|
82
|
-
measureGridWidth(rows.map(r => r.metadata[e]), { maxWidth: 400, stripHTML: true }),
|
|
83
|
-
])),
|
|
84
|
-
});
|
|
85
|
-
const [visible, setVisible] = useState(Object.fromEntries(fields.map(c => [c, true])));
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
setVisible(visible => ({
|
|
88
|
-
...Object.fromEntries(fields.map(c => [c, true])),
|
|
89
|
-
...visible,
|
|
90
|
-
}));
|
|
91
|
-
}, [fields]);
|
|
92
|
-
useEffect(() => {
|
|
93
|
-
setWidths(widths => ({
|
|
94
|
-
name: widths.name,
|
|
95
|
-
...Object.fromEntries(filteredNonMetadataKeys
|
|
96
|
-
.filter(f => visible[f])
|
|
97
|
-
.map(e => [
|
|
98
|
-
e,
|
|
99
|
-
measureGridWidth(rows.map(r => r[e]), { stripHTML: true, maxWidth: 400 }),
|
|
100
|
-
])),
|
|
101
|
-
...Object.fromEntries(filteredMetadataKeys
|
|
102
|
-
.filter(f => visible[f])
|
|
103
|
-
.map(e => [
|
|
104
|
-
e,
|
|
105
|
-
measureGridWidth(rows.map(r => r.metadata[e]), { stripHTML: true, maxWidth: 400 }),
|
|
106
|
-
])),
|
|
107
|
-
}));
|
|
108
|
-
}, [filteredMetadataKeys, visible, filteredNonMetadataKeys, showSparse, rows]);
|
|
109
37
|
const widthsDebounced = useDebounce(widths, 200);
|
|
110
38
|
const columns = [
|
|
111
39
|
{
|
|
112
40
|
field: 'name',
|
|
113
41
|
hideable: false,
|
|
114
42
|
renderCell: (params) => {
|
|
115
|
-
const { value,
|
|
43
|
+
const { value, row } = params;
|
|
44
|
+
const { id, conf } = row;
|
|
116
45
|
return (React.createElement("div", { className: classes.cell },
|
|
117
46
|
React.createElement(SanitizedHTML, { html: value }),
|
|
118
|
-
React.createElement(
|
|
119
|
-
target: e.currentTarget,
|
|
120
|
-
id: id,
|
|
121
|
-
conf: row.conf,
|
|
122
|
-
}) },
|
|
123
|
-
React.createElement(MoreHoriz, null))));
|
|
47
|
+
React.createElement(TrackLabelMenu, { id: id, conf: conf, trackId: id, model: model })));
|
|
124
48
|
},
|
|
125
49
|
width: (_a = widthsDebounced.name) !== null && _a !== void 0 ? _a : 100,
|
|
126
50
|
},
|
|
@@ -130,34 +54,30 @@ const FacetedSelector = observer(function FacetedSelector({ model, }) {
|
|
|
130
54
|
field: e,
|
|
131
55
|
width: (_a = widthsDebounced[e]) !== null && _a !== void 0 ? _a : 100,
|
|
132
56
|
renderCell: (params) => {
|
|
133
|
-
const
|
|
134
|
-
return
|
|
57
|
+
const val = params.value;
|
|
58
|
+
return val ? React.createElement(SanitizedHTML, { className: classes.cell, html: val }) : '';
|
|
135
59
|
},
|
|
136
60
|
});
|
|
137
61
|
}),
|
|
138
62
|
...filteredMetadataKeys.map(e => {
|
|
139
63
|
var _a;
|
|
140
64
|
return ({
|
|
141
|
-
field: e
|
|
142
|
-
|
|
65
|
+
field: `metadata.${e}`,
|
|
66
|
+
headerName: ['name', ...filteredNonMetadataKeys].includes(e)
|
|
67
|
+
? `${e} (from metadata)`
|
|
68
|
+
: e,
|
|
69
|
+
width: (_a = widthsDebounced['metadata.' + e]) !== null && _a !== void 0 ? _a : 100,
|
|
70
|
+
valueGetter: (params) => params.row.metadata[e],
|
|
143
71
|
renderCell: (params) => {
|
|
144
|
-
const
|
|
145
|
-
return
|
|
72
|
+
const val = params.row.metadata[e];
|
|
73
|
+
return val ? React.createElement(SanitizedHTML, { className: classes.cell, html: val }) : '';
|
|
146
74
|
},
|
|
147
75
|
});
|
|
148
76
|
}),
|
|
149
77
|
];
|
|
150
78
|
const shownTrackIds = new Set(tracks.map(t => t.configuration.trackId));
|
|
151
|
-
const arrFilters = Object.entries(filters)
|
|
152
|
-
.filter(f => f[1].length > 0)
|
|
153
|
-
.map(([key, val]) => [key, new Set(val)]);
|
|
154
|
-
const filteredRows = rows.filter(row => arrFilters.every(([key, val]) => val.has(row[key])));
|
|
155
79
|
return (React.createElement(React.Fragment, null,
|
|
156
|
-
|
|
157
|
-
callback();
|
|
158
|
-
setInfo(undefined);
|
|
159
|
-
}, open: !!info, onClose: () => setInfo(undefined) })) : null,
|
|
160
|
-
React.createElement(FacetedHeader, { setShowSparse: setShowSparse, setShowFilters: setShowFilters, setShowOptions: setShowOptions, setFilterText: setFilterText, setUseShoppingCart: setUseShoppingCart, showFilters: showFilters, showSparse: showSparse, showOptions: showOptions, filterText: filterText, useShoppingCart: useShoppingCart, model: model }),
|
|
80
|
+
React.createElement(FacetedHeader, { model: model }),
|
|
161
81
|
React.createElement("div", { ref: ref, style: {
|
|
162
82
|
display: 'flex',
|
|
163
83
|
overflow: 'hidden',
|
|
@@ -168,11 +88,11 @@ const FacetedSelector = observer(function FacetedSelector({ model, }) {
|
|
|
168
88
|
height: window.innerHeight * frac,
|
|
169
89
|
width: window.innerWidth * frac - (showFilters ? panelWidth : 0),
|
|
170
90
|
} },
|
|
171
|
-
React.createElement(ResizeBar, { checkbox: true, widths: Object.values(widths).map(f => f !== null && f !== void 0 ? f : 100), setWidths: newWidths => setWidths(Object.fromEntries(Object.entries(widths).map((entry, idx) => [
|
|
91
|
+
React.createElement(ResizeBar, { checkbox: true, widths: Object.values(widths).map(f => f !== null && f !== void 0 ? f : 100), setWidths: newWidths => faceted.setWidths(Object.fromEntries(Object.entries(widths).map((entry, idx) => [
|
|
172
92
|
entry[0],
|
|
173
93
|
newWidths[idx],
|
|
174
94
|
]))), scrollLeft: scrollLeft }),
|
|
175
|
-
React.createElement(DataGrid, { rows: filteredRows, columnVisibilityModel: visible, onColumnVisibilityModelChange:
|
|
95
|
+
React.createElement(DataGrid, { rows: filteredRows, columnVisibilityModel: visible, onColumnVisibilityModelChange: n => faceted.setVisible(n), columnHeaderHeight: 35, checkboxSelection: true, disableRowSelectionOnClick: true, keepNonExistentRowsSelected: true, onRowSelectionModelChange: userSelectedIds => {
|
|
176
96
|
if (!useShoppingCart) {
|
|
177
97
|
const a1 = shownTrackIds;
|
|
178
98
|
const a2 = new Set(userSelectedIds);
|
|
@@ -181,27 +101,35 @@ const FacetedSelector = observer(function FacetedSelector({ model, }) {
|
|
|
181
101
|
transaction(() => {
|
|
182
102
|
;
|
|
183
103
|
[...a1].filter(x => !a2.has(x)).map(t => view.hideTrack(t));
|
|
184
|
-
[...a2]
|
|
104
|
+
[...a2]
|
|
105
|
+
.filter(x => !a1.has(x))
|
|
106
|
+
.map(t => {
|
|
107
|
+
view.showTrack(t);
|
|
108
|
+
model.addToRecentlyUsed(t);
|
|
109
|
+
});
|
|
185
110
|
});
|
|
186
111
|
}
|
|
187
112
|
else {
|
|
188
113
|
const root = getRoot(model);
|
|
189
114
|
const schema = pluginManager.pluggableConfigSchemaType('track');
|
|
190
|
-
|
|
191
|
-
model.setSelection(tracks);
|
|
115
|
+
model.setSelection(userSelectedIds.map(id => resolveIdentifier(schema, root, id)));
|
|
192
116
|
}
|
|
193
117
|
}, rowSelectionModel: useShoppingCart
|
|
194
118
|
? selection.map(s => s.trackId)
|
|
195
119
|
: [...shownTrackIds], slots: { toolbar: showOptions ? GridToolbar : null }, slotProps: {
|
|
196
|
-
toolbar: {
|
|
120
|
+
toolbar: {
|
|
121
|
+
printOptions: {
|
|
122
|
+
disableToolbarButton: true,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
197
125
|
}, columns: columns, rowHeight: 25 })),
|
|
198
126
|
showFilters ? (React.createElement(React.Fragment, null,
|
|
199
|
-
React.createElement(ResizeHandle, { vertical: true, onDrag: dist => setPanelWidth(panelWidth - dist),
|
|
127
|
+
React.createElement(ResizeHandle, { vertical: true, onDrag: dist => faceted.setPanelWidth(panelWidth - dist), className: classes.resizeHandle }),
|
|
200
128
|
React.createElement("div", { style: {
|
|
201
129
|
width: panelWidth,
|
|
202
130
|
overflowY: 'auto',
|
|
203
131
|
overflowX: 'hidden',
|
|
204
132
|
} },
|
|
205
|
-
React.createElement(FacetFilters, { width: panelWidth - 10, rows: rows, columns: columns
|
|
133
|
+
React.createElement(FacetFilters, { model: model, width: panelWidth - 10, rows: rows, columns: columns })))) : null)));
|
|
206
134
|
});
|
|
207
135
|
export default FacetedSelector;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
3
|
+
import { MenuItem } from '@jbrowse/core/ui/Menu';
|
|
4
|
+
import { HierarchicalTrackSelectorModel } from '../../model';
|
|
5
|
+
declare const DropdownTrackSelector: ({ model, tracks, extraMenuItems, children, onClick, }: {
|
|
6
|
+
model: HierarchicalTrackSelectorModel;
|
|
7
|
+
tracks: AnyConfigurationModel[];
|
|
8
|
+
extraMenuItems: MenuItem[];
|
|
9
|
+
onClick?: (() => void) | undefined;
|
|
10
|
+
children: React.ReactElement;
|
|
11
|
+
}) => React.JSX.Element | null;
|
|
12
|
+
export default DropdownTrackSelector;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { observer } from 'mobx-react';
|
|
3
|
+
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
|
|
4
|
+
import SanitizedHTML from '@jbrowse/core/ui/SanitizedHTML';
|
|
5
|
+
import { getTrackName } from '@jbrowse/core/util/tracks';
|
|
6
|
+
import { getSession } from '@jbrowse/core/util';
|
|
7
|
+
import TrackLabelMenu from './TrackLabelMenu';
|
|
8
|
+
const DropdownTrackSelector = observer(function ({ model, tracks, extraMenuItems, children, onClick, }) {
|
|
9
|
+
const { view } = model;
|
|
10
|
+
const [open, setOpen] = useState(false);
|
|
11
|
+
const session = getSession(model);
|
|
12
|
+
return view ? (React.createElement(CascadingMenuButton, { closeAfterItemClick: false, onClick: onClick, menuItems: [
|
|
13
|
+
...tracks.map(t => ({
|
|
14
|
+
type: 'checkbox',
|
|
15
|
+
label: (React.createElement(React.Fragment, null,
|
|
16
|
+
React.createElement(SanitizedHTML, { html: getTrackName(t, session) }),
|
|
17
|
+
' ',
|
|
18
|
+
React.createElement(TrackLabelMenu, { id: t.trackId, trackId: t.trackId, model: model, conf: t, setOpen: setOpen, stopPropagation: true }))),
|
|
19
|
+
checked: view.tracks.some((f) => f.configuration === t),
|
|
20
|
+
onClick: () => {
|
|
21
|
+
if (!open) {
|
|
22
|
+
if (model.view.toggleTrack(t.trackId)) {
|
|
23
|
+
model.addToRecentlyUsed(t.trackId);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
})),
|
|
28
|
+
...extraMenuItems,
|
|
29
|
+
] }, children)) : null;
|
|
30
|
+
});
|
|
31
|
+
export default DropdownTrackSelector;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Badge, Tooltip } from '@mui/material';
|
|
3
|
+
import { observer } from 'mobx-react';
|
|
4
|
+
import { makeStyles } from 'tss-react/mui';
|
|
5
|
+
// icons
|
|
6
|
+
import GradeIcon from '@mui/icons-material/Grade';
|
|
7
|
+
import DropdownTrackSelector from './DropdownTrackSelector';
|
|
8
|
+
const useStyles = makeStyles()({
|
|
9
|
+
smallBadge: {
|
|
10
|
+
height: 14,
|
|
11
|
+
},
|
|
12
|
+
margin: {
|
|
13
|
+
marginRight: 10,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
const FavoriteTracks = observer(function ({ model, }) {
|
|
17
|
+
const { classes } = useStyles();
|
|
18
|
+
const { view, favoriteTracks } = model;
|
|
19
|
+
return view ? (React.createElement(DropdownTrackSelector, { onClick: () => model.setFavoritesCounter(0), tracks: favoriteTracks, model: model, extraMenuItems: favoriteTracks.length
|
|
20
|
+
? [
|
|
21
|
+
{ type: 'divider' },
|
|
22
|
+
{
|
|
23
|
+
label: 'Clear favorites',
|
|
24
|
+
onClick: () => model.clearFavorites(),
|
|
25
|
+
},
|
|
26
|
+
]
|
|
27
|
+
: [
|
|
28
|
+
{
|
|
29
|
+
label: 'No favorite tracks yet',
|
|
30
|
+
onClick: () => { },
|
|
31
|
+
},
|
|
32
|
+
] },
|
|
33
|
+
React.createElement(Tooltip, { title: "Favorite tracks" },
|
|
34
|
+
React.createElement(Badge, { classes: { badge: classes.smallBadge }, color: "secondary", anchorOrigin: {
|
|
35
|
+
vertical: 'bottom',
|
|
36
|
+
horizontal: 'right',
|
|
37
|
+
}, className: classes.margin, badgeContent: model.favoritesCounter },
|
|
38
|
+
React.createElement(GradeIcon, null))))) : null;
|
|
39
|
+
});
|
|
40
|
+
export default FavoriteTracks;
|
|
@@ -5,6 +5,8 @@ import { readConfObject, } from '@jbrowse/core/configuration';
|
|
|
5
5
|
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
|
|
6
6
|
// icons
|
|
7
7
|
import MenuIcon from '@mui/icons-material/Menu';
|
|
8
|
+
// lazies
|
|
9
|
+
const FacetedDialog = lazy(() => import('../faceted/FacetedDialog'));
|
|
8
10
|
// lazy components
|
|
9
11
|
const CloseConnectionDlg = lazy(() => import('../dialogs/CloseConnectionDialog'));
|
|
10
12
|
const DeleteConnectionDlg = lazy(() => import('../dialogs/DeleteConnectionDialog'));
|
|
@@ -16,6 +18,7 @@ const HamburgerMenu = observer(function ({ model, }) {
|
|
|
16
18
|
const [deleteDlgDetails, setDeleteDlgDetails] = useState();
|
|
17
19
|
const [connectionToggleOpen, setConnectionToggleOpen] = useState(false);
|
|
18
20
|
const [connectionManagerOpen, setConnectionManagerOpen] = useState(false);
|
|
21
|
+
const [facetedOpen, setFacetedOpen] = useState(false);
|
|
19
22
|
function breakConnection(connectionConf, deletingConnection) {
|
|
20
23
|
var _a;
|
|
21
24
|
const name = readConfObject(connectionConf, 'name');
|
|
@@ -40,6 +43,12 @@ const HamburgerMenu = observer(function ({ model, }) {
|
|
|
40
43
|
}
|
|
41
44
|
return (React.createElement(React.Fragment, null,
|
|
42
45
|
React.createElement(CascadingMenuButton, { menuItems: [
|
|
46
|
+
{
|
|
47
|
+
label: 'Open faceted track selector',
|
|
48
|
+
onClick: () => {
|
|
49
|
+
setFacetedOpen(true);
|
|
50
|
+
},
|
|
51
|
+
},
|
|
43
52
|
...(isSessionWithAddTracks(session)
|
|
44
53
|
? [
|
|
45
54
|
{
|
|
@@ -54,59 +63,74 @@ const HamburgerMenu = observer(function ({ model, }) {
|
|
|
54
63
|
},
|
|
55
64
|
]
|
|
56
65
|
: []),
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
{
|
|
67
|
+
label: 'Connections...',
|
|
68
|
+
subMenu: [
|
|
69
|
+
...(isSessionModelWithConnections(session)
|
|
70
|
+
? [
|
|
71
|
+
{
|
|
72
|
+
label: 'Turn on/off connections...',
|
|
73
|
+
onClick: () => setConnectionToggleOpen(true),
|
|
74
|
+
},
|
|
75
|
+
]
|
|
76
|
+
: []),
|
|
77
|
+
...(isSessionModelWithConnectionEditing(session)
|
|
78
|
+
? [
|
|
79
|
+
{
|
|
80
|
+
label: 'Add connection...',
|
|
81
|
+
onClick: () => {
|
|
82
|
+
if (isSessionModelWithWidgets(session)) {
|
|
83
|
+
session.showWidget(session.addWidget('AddConnectionWidget', 'addConnectionWidget'));
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
label: 'Delete connections...',
|
|
89
|
+
onClick: () => setConnectionManagerOpen(true),
|
|
90
|
+
},
|
|
91
|
+
]
|
|
92
|
+
: []),
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
label: 'Sort...',
|
|
97
|
+
type: 'subMenu',
|
|
98
|
+
subMenu: [
|
|
59
99
|
{
|
|
60
|
-
label: '
|
|
61
|
-
|
|
100
|
+
label: 'Sort tracks by name',
|
|
101
|
+
type: 'checkbox',
|
|
102
|
+
checked: model.activeSortTrackNames,
|
|
103
|
+
onClick: () => model.setSortTrackNames(!model.activeSortTrackNames),
|
|
62
104
|
},
|
|
63
|
-
]
|
|
64
|
-
: []),
|
|
65
|
-
...(isSessionModelWithConnectionEditing(session)
|
|
66
|
-
? [
|
|
67
105
|
{
|
|
68
|
-
label: '
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
},
|
|
106
|
+
label: 'Sort categories by name',
|
|
107
|
+
type: 'checkbox',
|
|
108
|
+
checked: model.activeSortCategories,
|
|
109
|
+
onClick: () => model.setSortCategories(!model.activeSortCategories),
|
|
74
110
|
},
|
|
75
|
-
|
|
76
|
-
label: 'Delete connections...',
|
|
77
|
-
onClick: () => setConnectionManagerOpen(true),
|
|
78
|
-
},
|
|
79
|
-
]
|
|
80
|
-
: []),
|
|
81
|
-
{ type: 'divider' },
|
|
82
|
-
{
|
|
83
|
-
label: 'Sort tracks by name',
|
|
84
|
-
type: 'checkbox',
|
|
85
|
-
checked: model.activeSortTrackNames,
|
|
86
|
-
onClick: () => model.setSortTrackNames(!model.activeSortTrackNames),
|
|
111
|
+
],
|
|
87
112
|
},
|
|
88
113
|
{
|
|
89
|
-
label: '
|
|
90
|
-
type: '
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
114
|
+
label: 'Collapse...',
|
|
115
|
+
type: 'subMenu',
|
|
116
|
+
subMenu: [
|
|
117
|
+
...(model.hasAnySubcategories
|
|
118
|
+
? [
|
|
119
|
+
{
|
|
120
|
+
label: 'Collapse subcategories',
|
|
121
|
+
onClick: () => model.collapseSubCategories(),
|
|
122
|
+
},
|
|
123
|
+
]
|
|
124
|
+
: []),
|
|
97
125
|
{
|
|
98
|
-
label: 'Collapse
|
|
99
|
-
onClick: () => model.
|
|
126
|
+
label: 'Collapse top-level categories',
|
|
127
|
+
onClick: () => model.collapseTopLevelCategories(),
|
|
100
128
|
},
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
label: 'Expand all categories',
|
|
109
|
-
onClick: () => model.expandAllCategories(),
|
|
129
|
+
{
|
|
130
|
+
label: 'Expand all categories',
|
|
131
|
+
onClick: () => model.expandAllCategories(),
|
|
132
|
+
},
|
|
133
|
+
],
|
|
110
134
|
},
|
|
111
135
|
] },
|
|
112
136
|
React.createElement(MenuIcon, null)),
|
|
@@ -114,6 +138,7 @@ const HamburgerMenu = observer(function ({ model, }) {
|
|
|
114
138
|
modalInfo ? (React.createElement(CloseConnectionDlg, { modalInfo: modalInfo, onClose: () => setModalInfo(undefined) })) : null,
|
|
115
139
|
deleteDlgDetails ? (React.createElement(DeleteConnectionDlg, { handleClose: () => setDeleteDlgDetails(undefined), deleteDialogDetails: deleteDlgDetails, session: session })) : null,
|
|
116
140
|
connectionManagerOpen ? (React.createElement(ManageConnectionsDlg, { handleClose: () => setConnectionManagerOpen(false), breakConnection: breakConnection, session: session })) : null,
|
|
117
|
-
connectionToggleOpen ? (React.createElement(ToggleConnectionsDlg, { handleClose: () => setConnectionToggleOpen(false), session: session, breakConnection: breakConnection })) : null
|
|
141
|
+
connectionToggleOpen ? (React.createElement(ToggleConnectionsDlg, { handleClose: () => setConnectionToggleOpen(false), session: session, breakConnection: breakConnection })) : null,
|
|
142
|
+
facetedOpen ? (React.createElement(FacetedDialog, { handleClose: () => setFacetedOpen(false), model: model })) : null)));
|
|
118
143
|
});
|
|
119
144
|
export default HamburgerMenu;
|
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import {
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { IconButton, InputAdornment, TextField } from '@mui/material';
|
|
3
3
|
import { makeStyles } from 'tss-react/mui';
|
|
4
4
|
import { observer } from 'mobx-react';
|
|
5
5
|
// icons
|
|
6
6
|
import ClearIcon from '@mui/icons-material/Clear';
|
|
7
7
|
import HamburgerMenu from './HamburgerMenu';
|
|
8
8
|
import ShoppingCart from '../ShoppingCart';
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
import FavoriteTracks from './FavoriteTracks';
|
|
10
|
+
import RecentlyUsedTracks from './RecentlyUsedTracks';
|
|
11
11
|
const useStyles = makeStyles()(theme => ({
|
|
12
12
|
searchBox: {
|
|
13
13
|
margin: theme.spacing(2),
|
|
14
14
|
},
|
|
15
|
-
menuIcon: {
|
|
16
|
-
marginRight: theme.spacing(1),
|
|
17
|
-
marginBottom: 0,
|
|
18
|
-
},
|
|
19
15
|
}));
|
|
20
16
|
const SearchTracksTextField = observer(function ({ model, }) {
|
|
21
17
|
const { filterText } = model;
|
|
@@ -27,14 +23,12 @@ const SearchTracksTextField = observer(function ({ model, }) {
|
|
|
27
23
|
} }));
|
|
28
24
|
});
|
|
29
25
|
const HierarchicalTrackSelectorHeader = observer(function ({ model, setHeaderHeight, }) {
|
|
30
|
-
const { classes } = useStyles();
|
|
31
|
-
const [facetedOpen, setFacetedOpen] = useState(false);
|
|
32
26
|
return (React.createElement("div", { ref: ref => setHeaderHeight((ref === null || ref === void 0 ? void 0 : ref.getBoundingClientRect().height) || 0), "data-testid": "hierarchical_track_selector" },
|
|
33
27
|
React.createElement("div", { style: { display: 'flex' } },
|
|
34
28
|
React.createElement(HamburgerMenu, { model: model }),
|
|
35
29
|
React.createElement(ShoppingCart, { model: model }),
|
|
36
30
|
React.createElement(SearchTracksTextField, { model: model }),
|
|
37
|
-
React.createElement(
|
|
38
|
-
|
|
31
|
+
React.createElement(RecentlyUsedTracks, { model: model }),
|
|
32
|
+
React.createElement(FavoriteTracks, { model: model }))));
|
|
39
33
|
});
|
|
40
34
|
export default HierarchicalTrackSelectorHeader;
|
|
@@ -4,8 +4,8 @@ import { VariableSizeTree } from 'react-vtree';
|
|
|
4
4
|
import { getSession } from '@jbrowse/core/util';
|
|
5
5
|
import Node from './TrackListNode';
|
|
6
6
|
function getNodeData(node, nestingLevel, extra, selection) {
|
|
7
|
-
const isLeaf =
|
|
8
|
-
const selected =
|
|
7
|
+
const isLeaf = node.type === 'track';
|
|
8
|
+
const selected = isLeaf ? selection[node.trackId] : false;
|
|
9
9
|
return {
|
|
10
10
|
data: {
|
|
11
11
|
defaultHeight: isLeaf ? 22 : 40,
|
|
@@ -30,7 +30,12 @@ const HierarchicalTree = observer(function HierarchicalTree({ height, tree, mode
|
|
|
30
30
|
const { drawerPosition } = session;
|
|
31
31
|
const obj = useMemo(() => Object.fromEntries(selection.map(s => [s.trackId, s])), [selection]);
|
|
32
32
|
const extra = useMemo(() => ({
|
|
33
|
-
onChange: (trackId) =>
|
|
33
|
+
onChange: (trackId) => {
|
|
34
|
+
const trackTurnedOn = view.toggleTrack(trackId);
|
|
35
|
+
if (trackTurnedOn) {
|
|
36
|
+
model.addToRecentlyUsed(trackId);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
34
39
|
toggleCollapse: (pathName) => model.toggleCategory(pathName),
|
|
35
40
|
tree,
|
|
36
41
|
model,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Badge, Tooltip } from '@mui/material';
|
|
3
|
+
import { observer } from 'mobx-react';
|
|
4
|
+
// icons
|
|
5
|
+
import HistoryIcon from '@mui/icons-material/History';
|
|
6
|
+
import DropdownTrackSelector from './DropdownTrackSelector';
|
|
7
|
+
import { makeStyles } from 'tss-react/mui';
|
|
8
|
+
const useStyles = makeStyles()({
|
|
9
|
+
smallBadge: {
|
|
10
|
+
height: 14,
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
const RecentlyUsedTracks = observer(function ({ model, }) {
|
|
14
|
+
const { classes } = useStyles();
|
|
15
|
+
const { view, recentlyUsedCounter, recentlyUsedTracks } = model;
|
|
16
|
+
return view ? (React.createElement(DropdownTrackSelector, { onClick: () => model.setRecentlyUsedCounter(0), model: model, tracks: recentlyUsedTracks, extraMenuItems: recentlyUsedTracks.length
|
|
17
|
+
? [
|
|
18
|
+
{ type: 'divider' },
|
|
19
|
+
{
|
|
20
|
+
label: 'Clear recently used',
|
|
21
|
+
onClick: () => model.clearRecentlyUsed(),
|
|
22
|
+
},
|
|
23
|
+
]
|
|
24
|
+
: [
|
|
25
|
+
{
|
|
26
|
+
label: 'No recently used',
|
|
27
|
+
onClick: () => { },
|
|
28
|
+
},
|
|
29
|
+
] },
|
|
30
|
+
React.createElement(Tooltip, { title: "Recently used tracks" },
|
|
31
|
+
React.createElement(Badge, { classes: { badge: classes.smallBadge }, anchorOrigin: {
|
|
32
|
+
vertical: 'bottom',
|
|
33
|
+
horizontal: 'right',
|
|
34
|
+
}, color: "secondary", badgeContent: recentlyUsedCounter },
|
|
35
|
+
React.createElement(HistoryIcon, null))))) : null;
|
|
36
|
+
});
|
|
37
|
+
export default RecentlyUsedTracks;
|
|
@@ -22,10 +22,10 @@ const useStyles = makeStyles()(theme => ({
|
|
|
22
22
|
export default function Category({ isOpen, setOpen, data, }) {
|
|
23
23
|
const { classes } = useStyles();
|
|
24
24
|
const [menuEl, setMenuEl] = useState(null);
|
|
25
|
-
const { name, model, id, tree
|
|
25
|
+
const { menuItems = [], name, model, id, tree } = data;
|
|
26
26
|
return (React.createElement("div", { className: classes.accordionText, onClick: () => {
|
|
27
27
|
if (!menuEl) {
|
|
28
|
-
toggleCollapse(id);
|
|
28
|
+
data.toggleCollapse(id);
|
|
29
29
|
setOpen(!isOpen);
|
|
30
30
|
}
|
|
31
31
|
} },
|
|
@@ -57,8 +57,8 @@ export default function Category({ isOpen, setOpen, data, }) {
|
|
|
57
57
|
onClick: () => {
|
|
58
58
|
var _a;
|
|
59
59
|
for (const entry of ((_a = treeToMap(tree).get(id)) === null || _a === void 0 ? void 0 : _a.children) || []) {
|
|
60
|
-
if (
|
|
61
|
-
model.view.showTrack(entry.
|
|
60
|
+
if (entry.type === 'track') {
|
|
61
|
+
model.view.showTrack(entry.trackId);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
},
|
|
@@ -68,12 +68,13 @@ export default function Category({ isOpen, setOpen, data, }) {
|
|
|
68
68
|
onClick: () => {
|
|
69
69
|
var _a;
|
|
70
70
|
for (const entry of ((_a = treeToMap(tree).get(id)) === null || _a === void 0 ? void 0 : _a.children) || []) {
|
|
71
|
-
if (
|
|
72
|
-
model.view.hideTrack(entry.
|
|
71
|
+
if (entry.type === 'track') {
|
|
72
|
+
model.view.hideTrack(entry.trackId);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
76
|
},
|
|
77
|
+
...menuItems,
|
|
77
78
|
], onMenuItemClick: (_event, callback) => {
|
|
78
79
|
callback();
|
|
79
80
|
setMenuEl(null);
|