@jbrowse/plugin-data-management 2.6.2 → 2.7.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/AddConnectionWidget/components/AddConnectionWidget.d.ts +2 -2
- package/dist/AddConnectionWidget/components/AddConnectionWidget.js +17 -8
- package/dist/AddConnectionWidget/components/ConfigureConnection.d.ts +2 -2
- package/dist/AddConnectionWidget/components/ConfigureConnection.js +2 -1
- package/dist/AddConnectionWidget/components/ConnectionTypeSelect.d.ts +2 -2
- package/dist/AddConnectionWidget/components/ConnectionTypeSelect.js +5 -3
- package/dist/AddTrackWidget/components/AddTrackWidget.d.ts +3 -4
- package/dist/AddTrackWidget/components/AddTrackWidget.js +3 -3
- package/dist/AddTrackWidget/components/ConfirmTrack.d.ts +2 -2
- package/dist/AddTrackWidget/components/ConfirmTrack.js +3 -2
- package/dist/AddTrackWidget/components/DefaultAddTrackWorkflow.d.ts +3 -4
- package/dist/AddTrackWidget/components/DefaultAddTrackWorkflow.js +32 -27
- package/dist/AddTrackWidget/components/PasteConfigWorkflow.d.ts +3 -4
- package/dist/AddTrackWidget/components/PasteConfigWorkflow.js +10 -7
- package/dist/AddTrackWidget/components/TrackAdapterSelector.js +1 -1
- package/dist/AddTrackWidget/components/TrackSourceSelect.d.ts +3 -4
- package/dist/AddTrackWidget/components/TrackSourceSelect.js +3 -3
- package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalFab.d.ts +2 -2
- package/dist/HierarchicalTrackSelectorWidget/components/HierarchicalFab.js +2 -1
- package/dist/HierarchicalTrackSelectorWidget/components/ShoppingCart.d.ts +2 -2
- package/dist/HierarchicalTrackSelectorWidget/components/ShoppingCart.js +2 -1
- package/dist/HierarchicalTrackSelectorWidget/components/dialogs/CloseConnectionDialog.d.ts +3 -5
- package/dist/HierarchicalTrackSelectorWidget/components/dialogs/CloseConnectionDialog.js +2 -1
- package/dist/HierarchicalTrackSelectorWidget/components/dialogs/DeleteConnectionDialog.d.ts +3 -4
- package/dist/HierarchicalTrackSelectorWidget/components/dialogs/DeleteConnectionDialog.js +5 -8
- package/dist/HierarchicalTrackSelectorWidget/components/dialogs/ManageConnectionsDialog.d.ts +3 -4
- package/dist/HierarchicalTrackSelectorWidget/components/dialogs/ManageConnectionsDialog.js +3 -3
- package/dist/HierarchicalTrackSelectorWidget/components/dialogs/ToggleConnectionsDialog.d.ts +2 -3
- package/dist/HierarchicalTrackSelectorWidget/components/dialogs/ToggleConnectionsDialog.js +2 -3
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.d.ts +13 -0
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.js +72 -0
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js +31 -77
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.d.ts +3 -4
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.js +3 -3
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.d.ts +5 -3
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js +10 -4
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.d.ts +2 -2
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js +53 -33
- package/dist/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.d.ts +2 -2
- package/dist/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js +39 -21
- package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.d.ts +3 -4
- package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js +13 -9
- package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.d.ts +4 -3
- package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js +5 -6
- package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js +22 -0
- package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js +3 -3
- package/dist/HierarchicalTrackSelectorWidget/components/util.d.ts +2 -1
- package/dist/HierarchicalTrackSelectorWidget/components/util.js +1 -2
- package/dist/HierarchicalTrackSelectorWidget/filterTracks.d.ts +12 -0
- package/dist/HierarchicalTrackSelectorWidget/filterTracks.js +32 -0
- package/dist/HierarchicalTrackSelectorWidget/generateHierarchy.d.ts +21 -0
- package/dist/HierarchicalTrackSelectorWidget/generateHierarchy.js +98 -0
- package/dist/HierarchicalTrackSelectorWidget/model.d.ts +68 -28
- package/dist/HierarchicalTrackSelectorWidget/model.js +147 -117
- package/dist/HierarchicalTrackSelectorWidget/util.d.ts +12 -0
- package/dist/HierarchicalTrackSelectorWidget/util.js +53 -0
- package/dist/PluginStoreWidget/components/AddCustomPluginDialog.d.ts +7 -0
- package/dist/PluginStoreWidget/components/{CustomPluginForm.js → AddCustomPluginDialog.js} +13 -21
- package/dist/PluginStoreWidget/components/DeletePluginDialog.d.ts +5 -0
- package/dist/PluginStoreWidget/components/DeletePluginDialog.js +28 -0
- package/dist/PluginStoreWidget/components/InstalledPlugin.d.ts +3 -6
- package/dist/PluginStoreWidget/components/InstalledPlugin.js +24 -51
- package/dist/PluginStoreWidget/components/InstalledPluginsList.d.ts +3 -4
- package/dist/PluginStoreWidget/components/InstalledPluginsList.js +7 -9
- package/dist/PluginStoreWidget/components/PluginCard.d.ts +2 -2
- package/dist/PluginStoreWidget/components/PluginCard.js +5 -6
- package/dist/PluginStoreWidget/components/PluginStoreWidget.d.ts +3 -4
- package/dist/PluginStoreWidget/components/PluginStoreWidget.js +14 -42
- package/dist/PluginStoreWidget/components/util.d.ts +5 -0
- package/dist/PluginStoreWidget/components/util.js +29 -0
- package/dist/PluginStoreWidget/model.d.ts +2 -2
- package/dist/PluginStoreWidget/model.js +2 -2
- package/dist/index.d.ts +1 -9
- package/dist/index.js +1 -4
- package/dist/ucsc-trackhub/model.js +2 -2
- package/esm/AddConnectionWidget/components/AddConnectionWidget.d.ts +2 -2
- package/esm/AddConnectionWidget/components/AddConnectionWidget.js +19 -10
- package/esm/AddConnectionWidget/components/ConfigureConnection.d.ts +2 -2
- package/esm/AddConnectionWidget/components/ConfigureConnection.js +2 -1
- package/esm/AddConnectionWidget/components/ConnectionTypeSelect.d.ts +2 -2
- package/esm/AddConnectionWidget/components/ConnectionTypeSelect.js +5 -3
- package/esm/AddTrackWidget/components/AddTrackWidget.d.ts +3 -4
- package/esm/AddTrackWidget/components/AddTrackWidget.js +3 -3
- package/esm/AddTrackWidget/components/ConfirmTrack.d.ts +2 -2
- package/esm/AddTrackWidget/components/ConfirmTrack.js +4 -3
- package/esm/AddTrackWidget/components/DefaultAddTrackWorkflow.d.ts +3 -4
- package/esm/AddTrackWidget/components/DefaultAddTrackWorkflow.js +33 -28
- package/esm/AddTrackWidget/components/PasteConfigWorkflow.d.ts +3 -4
- package/esm/AddTrackWidget/components/PasteConfigWorkflow.js +11 -8
- package/esm/AddTrackWidget/components/TrackAdapterSelector.js +1 -1
- package/esm/AddTrackWidget/components/TrackSourceSelect.d.ts +3 -4
- package/esm/AddTrackWidget/components/TrackSourceSelect.js +3 -3
- package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalFab.d.ts +2 -2
- package/esm/HierarchicalTrackSelectorWidget/components/HierarchicalFab.js +2 -1
- package/esm/HierarchicalTrackSelectorWidget/components/ShoppingCart.d.ts +2 -2
- package/esm/HierarchicalTrackSelectorWidget/components/ShoppingCart.js +2 -1
- package/esm/HierarchicalTrackSelectorWidget/components/dialogs/CloseConnectionDialog.d.ts +3 -5
- package/esm/HierarchicalTrackSelectorWidget/components/dialogs/CloseConnectionDialog.js +2 -1
- package/esm/HierarchicalTrackSelectorWidget/components/dialogs/DeleteConnectionDialog.d.ts +3 -4
- package/esm/HierarchicalTrackSelectorWidget/components/dialogs/DeleteConnectionDialog.js +6 -9
- package/esm/HierarchicalTrackSelectorWidget/components/dialogs/ManageConnectionsDialog.d.ts +3 -4
- package/esm/HierarchicalTrackSelectorWidget/components/dialogs/ManageConnectionsDialog.js +3 -3
- package/esm/HierarchicalTrackSelectorWidget/components/dialogs/ToggleConnectionsDialog.d.ts +2 -3
- package/esm/HierarchicalTrackSelectorWidget/components/dialogs/ToggleConnectionsDialog.js +3 -2
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.d.ts +13 -0
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.js +43 -0
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js +31 -54
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.d.ts +3 -4
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedDialog.js +3 -3
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.d.ts +5 -3
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js +10 -4
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.d.ts +2 -2
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js +51 -31
- package/esm/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.d.ts +2 -2
- package/esm/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js +38 -20
- package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.d.ts +3 -4
- package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js +13 -9
- package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.d.ts +4 -3
- package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js +5 -6
- package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js +22 -0
- package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js +2 -2
- package/esm/HierarchicalTrackSelectorWidget/components/util.d.ts +2 -1
- package/esm/HierarchicalTrackSelectorWidget/components/util.js +1 -2
- package/esm/HierarchicalTrackSelectorWidget/filterTracks.d.ts +12 -0
- package/esm/HierarchicalTrackSelectorWidget/filterTracks.js +28 -0
- package/esm/HierarchicalTrackSelectorWidget/generateHierarchy.d.ts +21 -0
- package/esm/HierarchicalTrackSelectorWidget/generateHierarchy.js +94 -0
- package/esm/HierarchicalTrackSelectorWidget/model.d.ts +68 -28
- package/esm/HierarchicalTrackSelectorWidget/model.js +149 -116
- package/esm/HierarchicalTrackSelectorWidget/util.d.ts +12 -0
- package/esm/HierarchicalTrackSelectorWidget/util.js +45 -0
- package/esm/PluginStoreWidget/components/AddCustomPluginDialog.d.ts +7 -0
- package/esm/PluginStoreWidget/components/{CustomPluginForm.js → AddCustomPluginDialog.js} +13 -21
- package/esm/PluginStoreWidget/components/DeletePluginDialog.d.ts +5 -0
- package/esm/PluginStoreWidget/components/DeletePluginDialog.js +22 -0
- package/esm/PluginStoreWidget/components/InstalledPlugin.d.ts +3 -6
- package/esm/PluginStoreWidget/components/InstalledPlugin.js +27 -54
- package/esm/PluginStoreWidget/components/InstalledPluginsList.d.ts +3 -4
- package/esm/PluginStoreWidget/components/InstalledPluginsList.js +7 -9
- package/esm/PluginStoreWidget/components/PluginCard.d.ts +2 -2
- package/esm/PluginStoreWidget/components/PluginCard.js +5 -6
- package/esm/PluginStoreWidget/components/PluginStoreWidget.d.ts +3 -4
- package/esm/PluginStoreWidget/components/PluginStoreWidget.js +15 -43
- package/esm/PluginStoreWidget/components/util.d.ts +5 -0
- package/esm/PluginStoreWidget/components/util.js +25 -0
- package/esm/PluginStoreWidget/model.d.ts +2 -2
- package/esm/PluginStoreWidget/model.js +1 -1
- package/esm/index.d.ts +1 -9
- package/esm/index.js +1 -3
- package/esm/ucsc-trackhub/model.js +2 -2
- package/package.json +5 -4
- package/dist/PluginStoreWidget/components/CustomPluginForm.d.ts +0 -9
- package/dist/SetDefaultSession/SetDefaultSession.d.ts +0 -6
- package/dist/SetDefaultSession/SetDefaultSession.js +0 -38
- package/dist/SetDefaultSession/index.d.ts +0 -1
- package/dist/SetDefaultSession/index.js +0 -8
- package/esm/PluginStoreWidget/components/CustomPluginForm.d.ts +0 -9
- package/esm/SetDefaultSession/SetDefaultSession.d.ts +0 -6
- package/esm/SetDefaultSession/SetDefaultSession.js +0 -33
- package/esm/SetDefaultSession/index.d.ts +0 -1
- package/esm/SetDefaultSession/index.js +0 -1
|
@@ -6,7 +6,7 @@ export interface InfoArgs {
|
|
|
6
6
|
id: string;
|
|
7
7
|
conf: AnyConfigurationModel;
|
|
8
8
|
}
|
|
9
|
-
declare const
|
|
9
|
+
declare const FacetedSelector: ({ model, }: {
|
|
10
10
|
model: HierarchicalTrackSelectorModel;
|
|
11
11
|
}) => React.JSX.Element;
|
|
12
|
-
export default
|
|
12
|
+
export default FacetedSelector;
|
|
@@ -9,35 +9,43 @@ import { getTrackName } from '@jbrowse/core/util/tracks';
|
|
|
9
9
|
import { ResizeHandle } from '@jbrowse/core/ui';
|
|
10
10
|
import SanitizedHTML from '@jbrowse/core/ui/SanitizedHTML';
|
|
11
11
|
import JBrowseMenu from '@jbrowse/core/ui/Menu';
|
|
12
|
-
import ResizeBar
|
|
13
|
-
import { getEnv, getSession, measureGridWidth, useDebounce, } from '@jbrowse/core/util';
|
|
12
|
+
import ResizeBar from '@jbrowse/core/ui/ResizeBar';
|
|
13
|
+
import { getEnv, getSession, measureGridWidth, useDebounce, useLocalStorage, } from '@jbrowse/core/util';
|
|
14
14
|
import { readConfObject, } from '@jbrowse/core/configuration';
|
|
15
|
+
import { useResizeBar } from '@jbrowse/core/ui/useResizeBar';
|
|
16
|
+
import { makeStyles } from 'tss-react/mui';
|
|
15
17
|
// icons
|
|
16
18
|
import MoreHoriz from '@mui/icons-material/MoreHoriz';
|
|
17
|
-
|
|
18
|
-
import { matches } from '../../model';
|
|
19
|
+
import { matches } from '../../util';
|
|
19
20
|
import FacetedHeader from './FacetedHeader';
|
|
20
21
|
import FacetFilters from './FacetFilters';
|
|
21
22
|
import { getRootKeys } from './util';
|
|
22
23
|
const nonMetadataKeys = ['category', 'adapter', 'description'];
|
|
24
|
+
const useStyles = makeStyles()({
|
|
25
|
+
cell: {
|
|
26
|
+
whiteSpace: 'nowrap',
|
|
27
|
+
overflow: 'hidden',
|
|
28
|
+
textOverflow: 'ellipsis',
|
|
29
|
+
},
|
|
30
|
+
});
|
|
23
31
|
const frac = 0.75;
|
|
24
|
-
|
|
32
|
+
const FacetedSelector = observer(function FacetedSelector({ model, }) {
|
|
25
33
|
var _a, _b;
|
|
34
|
+
const { classes } = useStyles();
|
|
26
35
|
const { view, selection } = model;
|
|
27
36
|
const { pluginManager } = getEnv(model);
|
|
28
37
|
const { ref, scrollLeft } = useResizeBar();
|
|
29
38
|
const [filterText, setFilterText] = useState('');
|
|
30
|
-
const [showOptions, setShowOptions] =
|
|
39
|
+
const [showOptions, setShowOptions] = useLocalStorage('facet-showTableOptions', false);
|
|
31
40
|
const [info, setInfo] = useState();
|
|
32
41
|
const [useShoppingCart, setUseShoppingCart] = useState(false);
|
|
33
|
-
const [
|
|
34
|
-
const [
|
|
42
|
+
const [showSparse, setShowSparse] = useLocalStorage('facet-showSparse', false);
|
|
43
|
+
const [showFilters, setShowFilters] = useLocalStorage('facet-showFilters', true);
|
|
44
|
+
const [panelWidth, setPanelWidth] = useLocalStorage('facet-panelWidth', 400);
|
|
35
45
|
const session = getSession(model);
|
|
36
46
|
const filterDebounced = useDebounce(filterText, 400);
|
|
37
47
|
const tracks = view.tracks;
|
|
38
|
-
const [filters, dispatch] = useReducer((state, update) => {
|
|
39
|
-
return { ...state, [update.key]: update.val };
|
|
40
|
-
}, {});
|
|
48
|
+
const [filters, dispatch] = useReducer((state, update) => ({ ...state, [update.key]: update.val }), {});
|
|
41
49
|
const rows = useMemo(() => {
|
|
42
50
|
// metadata is spread onto the object for easier access and sorting
|
|
43
51
|
// by the mui data grid (it's unable to sort by nested objects)
|
|
@@ -58,10 +66,10 @@ export default observer(function FacetedSelector({ model, }) {
|
|
|
58
66
|
};
|
|
59
67
|
});
|
|
60
68
|
}, [model, filterDebounced, session]);
|
|
61
|
-
const filteredNonMetadataKeys = useMemo(() => nonMetadataKeys.filter(f =>
|
|
62
|
-
const filteredMetadataKeys = useMemo(() => [...new Set(rows.flatMap(row => getRootKeys(row.metadata)))].filter(f =>
|
|
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
|
|
63
71
|
? true
|
|
64
|
-
: rows.map(r => r.metadata[f]).filter(f => !!f).length > 5), [
|
|
72
|
+
: rows.map(r => r.metadata[f]).filter(f => !!f).length > 5), [showSparse, rows]);
|
|
65
73
|
const fields = useMemo(() => ['name', ...filteredNonMetadataKeys, ...filteredMetadataKeys], [filteredNonMetadataKeys, filteredMetadataKeys]);
|
|
66
74
|
const [widths, setWidths] = useState({
|
|
67
75
|
name: measureGridWidth(rows.map(r => r.name), { maxWidth: 500, stripHTML: true }) + 15,
|
|
@@ -97,15 +105,15 @@ export default observer(function FacetedSelector({ model, }) {
|
|
|
97
105
|
measureGridWidth(rows.map(r => r.metadata[e]), { stripHTML: true, maxWidth: 400 }),
|
|
98
106
|
])),
|
|
99
107
|
}));
|
|
100
|
-
}, [filteredMetadataKeys, visible, filteredNonMetadataKeys,
|
|
101
|
-
const widthsDebounced = useDebounce(widths,
|
|
108
|
+
}, [filteredMetadataKeys, visible, filteredNonMetadataKeys, showSparse, rows]);
|
|
109
|
+
const widthsDebounced = useDebounce(widths, 200);
|
|
102
110
|
const columns = [
|
|
103
111
|
{
|
|
104
112
|
field: 'name',
|
|
105
113
|
hideable: false,
|
|
106
114
|
renderCell: (params) => {
|
|
107
115
|
const { value, id, row } = params;
|
|
108
|
-
return (React.createElement(
|
|
116
|
+
return (React.createElement("div", { className: classes.cell },
|
|
109
117
|
React.createElement(SanitizedHTML, { html: value }),
|
|
110
118
|
React.createElement(IconButton, { onClick: e => setInfo({
|
|
111
119
|
target: e.currentTarget,
|
|
@@ -123,7 +131,7 @@ export default observer(function FacetedSelector({ model, }) {
|
|
|
123
131
|
width: (_a = widthsDebounced[e]) !== null && _a !== void 0 ? _a : 100,
|
|
124
132
|
renderCell: (params) => {
|
|
125
133
|
const { value } = params;
|
|
126
|
-
return value ? React.createElement(SanitizedHTML, { html: value }) : '';
|
|
134
|
+
return (React.createElement("div", { className: classes.cell }, value ? React.createElement(SanitizedHTML, { html: value }) : ''));
|
|
127
135
|
},
|
|
128
136
|
});
|
|
129
137
|
}),
|
|
@@ -134,19 +142,22 @@ export default observer(function FacetedSelector({ model, }) {
|
|
|
134
142
|
width: (_a = widthsDebounced[e]) !== null && _a !== void 0 ? _a : 100,
|
|
135
143
|
renderCell: (params) => {
|
|
136
144
|
const { value } = params;
|
|
137
|
-
return value ? React.createElement(SanitizedHTML, { html: value }) : '';
|
|
145
|
+
return (React.createElement("div", { className: classes.cell }, value ? React.createElement(SanitizedHTML, { html: value }) : ''));
|
|
138
146
|
},
|
|
139
147
|
});
|
|
140
148
|
}),
|
|
141
149
|
];
|
|
142
|
-
const shownTrackIds = tracks.map(t => t.configuration.trackId);
|
|
143
|
-
const arrFilters = Object.entries(filters)
|
|
150
|
+
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])));
|
|
144
155
|
return (React.createElement(React.Fragment, null,
|
|
145
156
|
info ? (React.createElement(JBrowseMenu, { anchorEl: info === null || info === void 0 ? void 0 : info.target, menuItems: ((_b = session.getTrackActionMenuItems) === null || _b === void 0 ? void 0 : _b.call(session, info.conf)) || [], onMenuItemClick: (_event, callback) => {
|
|
146
157
|
callback();
|
|
147
158
|
setInfo(undefined);
|
|
148
159
|
}, open: !!info, onClose: () => setInfo(undefined) })) : null,
|
|
149
|
-
React.createElement(FacetedHeader, {
|
|
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 }),
|
|
150
161
|
React.createElement("div", { ref: ref, style: {
|
|
151
162
|
display: 'flex',
|
|
152
163
|
overflow: 'hidden',
|
|
@@ -155,21 +166,22 @@ export default observer(function FacetedSelector({ model, }) {
|
|
|
155
166
|
} },
|
|
156
167
|
React.createElement("div", { style: {
|
|
157
168
|
height: window.innerHeight * frac,
|
|
158
|
-
width: window.innerWidth * frac - panelWidth,
|
|
169
|
+
width: window.innerWidth * frac - (showFilters ? panelWidth : 0),
|
|
159
170
|
} },
|
|
160
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) => [
|
|
161
172
|
entry[0],
|
|
162
173
|
newWidths[idx],
|
|
163
174
|
]))), scrollLeft: scrollLeft }),
|
|
164
|
-
React.createElement(DataGrid, { rows:
|
|
175
|
+
React.createElement(DataGrid, { rows: filteredRows, columnVisibilityModel: visible, onColumnVisibilityModelChange: newModel => setVisible(newModel), columnHeaderHeight: 35, checkboxSelection: true, disableRowSelectionOnClick: true, keepNonExistentRowsSelected: true, onRowSelectionModelChange: userSelectedIds => {
|
|
165
176
|
if (!useShoppingCart) {
|
|
166
177
|
const a1 = shownTrackIds;
|
|
167
|
-
const a2 = userSelectedIds;
|
|
178
|
+
const a2 = new Set(userSelectedIds);
|
|
168
179
|
// synchronize the user selection with the view
|
|
169
180
|
// see share https://stackoverflow.com/a/33034768/2129219
|
|
170
181
|
transaction(() => {
|
|
171
|
-
|
|
172
|
-
|
|
182
|
+
;
|
|
183
|
+
[...a1].filter(x => !a2.has(x)).map(t => view.hideTrack(t));
|
|
184
|
+
[...a2].filter(x => !a1.has(x)).map(t => view.showTrack(t));
|
|
173
185
|
});
|
|
174
186
|
}
|
|
175
187
|
else {
|
|
@@ -178,10 +190,18 @@ export default observer(function FacetedSelector({ model, }) {
|
|
|
178
190
|
const tracks = userSelectedIds.map(id => resolveIdentifier(schema, root, id));
|
|
179
191
|
model.setSelection(tracks);
|
|
180
192
|
}
|
|
181
|
-
}, rowSelectionModel: useShoppingCart
|
|
193
|
+
}, rowSelectionModel: useShoppingCart
|
|
194
|
+
? selection.map(s => s.trackId)
|
|
195
|
+
: [...shownTrackIds], slots: { toolbar: showOptions ? GridToolbar : null }, slotProps: {
|
|
182
196
|
toolbar: { printOptions: { disableToolbarButton: true } },
|
|
183
197
|
}, columns: columns, rowHeight: 25 })),
|
|
184
|
-
React.createElement(
|
|
185
|
-
|
|
186
|
-
React.createElement(
|
|
198
|
+
showFilters ? (React.createElement(React.Fragment, null,
|
|
199
|
+
React.createElement(ResizeHandle, { vertical: true, onDrag: dist => setPanelWidth(panelWidth - dist), style: { marginLeft: 5, background: 'grey', width: 5 } }),
|
|
200
|
+
React.createElement("div", { style: {
|
|
201
|
+
width: panelWidth,
|
|
202
|
+
overflowY: 'auto',
|
|
203
|
+
overflowX: 'hidden',
|
|
204
|
+
} },
|
|
205
|
+
React.createElement(FacetFilters, { width: panelWidth - 10, rows: rows, columns: columns, dispatch: dispatch, filters: filters })))) : null)));
|
|
187
206
|
});
|
|
207
|
+
export default FacetedSelector;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { HierarchicalTrackSelectorModel } from '../../model';
|
|
3
|
-
declare const
|
|
3
|
+
declare const HamburgerMenu: ({ model, }: {
|
|
4
4
|
model: HierarchicalTrackSelectorModel;
|
|
5
5
|
}) => React.JSX.Element;
|
|
6
|
-
export default
|
|
6
|
+
export default HamburgerMenu;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import React, { Suspense, lazy, useState } from 'react';
|
|
2
|
-
import { IconButton } from '@mui/material';
|
|
3
|
-
import { makeStyles } from 'tss-react/mui';
|
|
4
2
|
import { observer } from 'mobx-react';
|
|
5
|
-
import JBrowseMenu from '@jbrowse/core/ui/Menu';
|
|
6
3
|
import { getSession, isSessionModelWithConnectionEditing, isSessionModelWithConnections, isSessionModelWithWidgets, isSessionWithAddTracks, } from '@jbrowse/core/util';
|
|
7
4
|
import { readConfObject, } from '@jbrowse/core/configuration';
|
|
5
|
+
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
|
|
8
6
|
// icons
|
|
9
7
|
import MenuIcon from '@mui/icons-material/Menu';
|
|
10
8
|
// lazy components
|
|
@@ -12,23 +10,16 @@ const CloseConnectionDlg = lazy(() => import('../dialogs/CloseConnectionDialog')
|
|
|
12
10
|
const DeleteConnectionDlg = lazy(() => import('../dialogs/DeleteConnectionDialog'));
|
|
13
11
|
const ManageConnectionsDlg = lazy(() => import('../dialogs/ManageConnectionsDialog'));
|
|
14
12
|
const ToggleConnectionsDlg = lazy(() => import('../dialogs/ToggleConnectionsDialog'));
|
|
15
|
-
const
|
|
16
|
-
menuIcon: {
|
|
17
|
-
marginRight: theme.spacing(1),
|
|
18
|
-
marginBottom: 0,
|
|
19
|
-
},
|
|
20
|
-
}));
|
|
21
|
-
export default observer(function HamburgerMenu({ model, }) {
|
|
13
|
+
const HamburgerMenu = observer(function ({ model, }) {
|
|
22
14
|
const session = getSession(model);
|
|
23
|
-
const [menuEl, setMenuEl] = useState();
|
|
24
15
|
const [modalInfo, setModalInfo] = useState();
|
|
25
16
|
const [deleteDlgDetails, setDeleteDlgDetails] = useState();
|
|
26
17
|
const [connectionToggleOpen, setConnectionToggleOpen] = useState(false);
|
|
27
18
|
const [connectionManagerOpen, setConnectionManagerOpen] = useState(false);
|
|
28
|
-
const { classes } = useStyles();
|
|
29
19
|
function breakConnection(connectionConf, deletingConnection) {
|
|
20
|
+
var _a;
|
|
30
21
|
const name = readConfObject(connectionConf, 'name');
|
|
31
|
-
const result = session.prepareToBreakConnection(connectionConf);
|
|
22
|
+
const result = (_a = session.prepareToBreakConnection) === null || _a === void 0 ? void 0 : _a.call(session, connectionConf);
|
|
32
23
|
if (result) {
|
|
33
24
|
const [safelyBreakConnection, dereferenceTypeCount] = result;
|
|
34
25
|
if (Object.keys(dereferenceTypeCount).length > 0) {
|
|
@@ -48,12 +39,7 @@ export default observer(function HamburgerMenu({ model, }) {
|
|
|
48
39
|
}
|
|
49
40
|
}
|
|
50
41
|
return (React.createElement(React.Fragment, null,
|
|
51
|
-
React.createElement(
|
|
52
|
-
React.createElement(MenuIcon, null)),
|
|
53
|
-
React.createElement(JBrowseMenu, { anchorEl: menuEl, open: Boolean(menuEl), onMenuItemClick: (_, callback) => {
|
|
54
|
-
callback();
|
|
55
|
-
setMenuEl(undefined);
|
|
56
|
-
}, onClose: () => setMenuEl(undefined), menuItems: [
|
|
42
|
+
React.createElement(CascadingMenuButton, { menuItems: [
|
|
57
43
|
...(isSessionWithAddTracks(session)
|
|
58
44
|
? [
|
|
59
45
|
{
|
|
@@ -92,10 +78,42 @@ export default observer(function HamburgerMenu({ model, }) {
|
|
|
92
78
|
},
|
|
93
79
|
]
|
|
94
80
|
: []),
|
|
95
|
-
|
|
81
|
+
{ type: 'divider' },
|
|
82
|
+
{
|
|
83
|
+
label: 'Sort tracks by name',
|
|
84
|
+
type: 'checkbox',
|
|
85
|
+
checked: model.activeSortTrackNames,
|
|
86
|
+
onClick: () => model.setSortTrackNames(!model.activeSortTrackNames),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
label: 'Sort categories by name',
|
|
90
|
+
type: 'checkbox',
|
|
91
|
+
checked: model.activeSortCategories,
|
|
92
|
+
onClick: () => model.setSortCategories(!model.activeSortCategories),
|
|
93
|
+
},
|
|
94
|
+
{ type: 'divider' },
|
|
95
|
+
...(model.hasAnySubcategories
|
|
96
|
+
? [
|
|
97
|
+
{
|
|
98
|
+
label: 'Collapse subcategories',
|
|
99
|
+
onClick: () => model.collapseSubCategories(),
|
|
100
|
+
},
|
|
101
|
+
]
|
|
102
|
+
: []),
|
|
103
|
+
{
|
|
104
|
+
label: 'Collapse top-level categories',
|
|
105
|
+
onClick: () => model.collapseTopLevelCategories(),
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
label: 'Expand all categories',
|
|
109
|
+
onClick: () => model.expandAllCategories(),
|
|
110
|
+
},
|
|
111
|
+
] },
|
|
112
|
+
React.createElement(MenuIcon, null)),
|
|
96
113
|
React.createElement(Suspense, { fallback: React.createElement(React.Fragment, null) },
|
|
97
114
|
modalInfo ? (React.createElement(CloseConnectionDlg, { modalInfo: modalInfo, onClose: () => setModalInfo(undefined) })) : null,
|
|
98
115
|
deleteDlgDetails ? (React.createElement(DeleteConnectionDlg, { handleClose: () => setDeleteDlgDetails(undefined), deleteDialogDetails: deleteDlgDetails, session: session })) : null,
|
|
99
116
|
connectionManagerOpen ? (React.createElement(ManageConnectionsDlg, { handleClose: () => setConnectionManagerOpen(false), breakConnection: breakConnection, session: session })) : null,
|
|
100
117
|
connectionToggleOpen ? (React.createElement(ToggleConnectionsDlg, { handleClose: () => setConnectionToggleOpen(false), session: session, breakConnection: breakConnection })) : null)));
|
|
101
118
|
});
|
|
119
|
+
export default HamburgerMenu;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { HierarchicalTrackSelectorModel } from '../../model';
|
|
3
|
-
declare
|
|
3
|
+
declare const HierarchicalTrackSelectorHeader: ({ model, setHeaderHeight, }: {
|
|
4
4
|
model: HierarchicalTrackSelectorModel;
|
|
5
5
|
setHeaderHeight: (n: number) => void;
|
|
6
|
-
})
|
|
7
|
-
|
|
8
|
-
export default _default;
|
|
6
|
+
}) => React.JSX.Element;
|
|
7
|
+
export default HierarchicalTrackSelectorHeader;
|
|
@@ -17,20 +17,24 @@ const useStyles = makeStyles()(theme => ({
|
|
|
17
17
|
marginBottom: 0,
|
|
18
18
|
},
|
|
19
19
|
}));
|
|
20
|
-
function
|
|
20
|
+
const SearchTracksTextField = observer(function ({ model, }) {
|
|
21
|
+
const { filterText } = model;
|
|
22
|
+
const { classes } = useStyles();
|
|
23
|
+
return (React.createElement(TextField, { className: classes.searchBox, label: "Filter tracks", value: filterText, onChange: event => model.setFilterText(event.target.value), fullWidth: true, InputProps: {
|
|
24
|
+
endAdornment: (React.createElement(InputAdornment, { position: "end" },
|
|
25
|
+
React.createElement(IconButton, { onClick: () => model.clearFilterText() },
|
|
26
|
+
React.createElement(ClearIcon, null)))),
|
|
27
|
+
} }));
|
|
28
|
+
});
|
|
29
|
+
const HierarchicalTrackSelectorHeader = observer(function ({ model, setHeaderHeight, }) {
|
|
21
30
|
const { classes } = useStyles();
|
|
22
31
|
const [facetedOpen, setFacetedOpen] = useState(false);
|
|
23
|
-
const { filterText } = model;
|
|
24
32
|
return (React.createElement("div", { ref: ref => setHeaderHeight((ref === null || ref === void 0 ? void 0 : ref.getBoundingClientRect().height) || 0), "data-testid": "hierarchical_track_selector" },
|
|
25
33
|
React.createElement("div", { style: { display: 'flex' } },
|
|
26
34
|
React.createElement(HamburgerMenu, { model: model }),
|
|
27
35
|
React.createElement(ShoppingCart, { model: model }),
|
|
28
|
-
React.createElement(
|
|
29
|
-
endAdornment: (React.createElement(InputAdornment, { position: "end" },
|
|
30
|
-
React.createElement(IconButton, { onClick: () => model.clearFilterText() },
|
|
31
|
-
React.createElement(ClearIcon, null)))),
|
|
32
|
-
} }),
|
|
36
|
+
React.createElement(SearchTracksTextField, { model: model }),
|
|
33
37
|
React.createElement(Button, { className: classes.menuIcon, onClick: () => setFacetedOpen(true) }, "Open faceted selector")),
|
|
34
38
|
React.createElement(Suspense, { fallback: React.createElement("div", null) }, facetedOpen ? (React.createElement(FacetedDialog, { handleClose: () => setFacetedOpen(false), model: model })) : null)));
|
|
35
|
-
}
|
|
36
|
-
export default
|
|
39
|
+
});
|
|
40
|
+
export default HierarchicalTrackSelectorHeader;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { TreeNode
|
|
3
|
-
|
|
2
|
+
import { TreeNode } from '../../generateHierarchy';
|
|
3
|
+
import { HierarchicalTrackSelectorModel } from '../../model';
|
|
4
|
+
declare const HierarchicalTree: ({ height, tree, model, }: {
|
|
4
5
|
height: number;
|
|
5
6
|
tree: TreeNode;
|
|
6
7
|
model: HierarchicalTrackSelectorModel;
|
|
7
8
|
}) => React.JSX.Element;
|
|
8
|
-
export default
|
|
9
|
+
export default HierarchicalTree;
|
|
@@ -23,7 +23,7 @@ function getNodeData(node, nestingLevel, extra, selection) {
|
|
|
23
23
|
// this is the main tree component for the hierarchical track selector in note:
|
|
24
24
|
// in jbrowse-web the toolbar is position="sticky" which means the autosizer
|
|
25
25
|
// includes the height of the toolbar, so we subtract the given offsets
|
|
26
|
-
|
|
26
|
+
const HierarchicalTree = observer(function HierarchicalTree({ height, tree, model, }) {
|
|
27
27
|
const { filterText, selection, view } = model;
|
|
28
28
|
const treeRef = useRef(null);
|
|
29
29
|
const session = getSession(model);
|
|
@@ -37,15 +37,13 @@ export default observer(function HierarchicalTree({ height, tree, model, }) {
|
|
|
37
37
|
drawerPosition,
|
|
38
38
|
}), [view, model, drawerPosition, tree]);
|
|
39
39
|
const treeWalker = useCallback(function* treeWalker() {
|
|
40
|
-
for (
|
|
41
|
-
|
|
42
|
-
yield getNodeData(r, 0, extra, obj);
|
|
40
|
+
for (const child of tree.children) {
|
|
41
|
+
yield getNodeData(child, 0, extra, obj);
|
|
43
42
|
}
|
|
44
43
|
while (true) {
|
|
45
44
|
// @ts-expect-error
|
|
46
45
|
const parentMeta = yield;
|
|
47
|
-
for (
|
|
48
|
-
const curr = parentMeta.node.children[i];
|
|
46
|
+
for (const curr of parentMeta.node.children) {
|
|
49
47
|
yield getNodeData(curr, parentMeta.nestingLevel + 1, extra, obj);
|
|
50
48
|
}
|
|
51
49
|
}
|
|
@@ -60,3 +58,4 @@ export default observer(function HierarchicalTree({ height, tree, model, }) {
|
|
|
60
58
|
return (React.createElement(React.Fragment, null,
|
|
61
59
|
React.createElement(VariableSizeTree, { ref: treeRef, treeWalker: treeWalker, height: height }, Node)));
|
|
62
60
|
});
|
|
61
|
+
export default HierarchicalTree;
|
|
@@ -52,6 +52,28 @@ export default function Category({ isOpen, setOpen, data, }) {
|
|
|
52
52
|
model.removeFromSelection(getAllChildren(r));
|
|
53
53
|
},
|
|
54
54
|
},
|
|
55
|
+
{
|
|
56
|
+
label: 'Show all tracks',
|
|
57
|
+
onClick: () => {
|
|
58
|
+
var _a;
|
|
59
|
+
for (const entry of ((_a = treeToMap(tree).get(id)) === null || _a === void 0 ? void 0 : _a.children) || []) {
|
|
60
|
+
if (!entry.children.length) {
|
|
61
|
+
model.view.showTrack(entry.id);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
label: 'Hide all tracks',
|
|
68
|
+
onClick: () => {
|
|
69
|
+
var _a;
|
|
70
|
+
for (const entry of ((_a = treeToMap(tree).get(id)) === null || _a === void 0 ? void 0 : _a.children) || []) {
|
|
71
|
+
if (!entry.children.length) {
|
|
72
|
+
model.view.hideTrack(entry.id);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
},
|
|
55
77
|
], onMenuItemClick: (_event, callback) => {
|
|
56
78
|
callback();
|
|
57
79
|
setMenuEl(null);
|
|
@@ -3,12 +3,12 @@ import { Checkbox, FormControlLabel, IconButton, Tooltip } from '@mui/material';
|
|
|
3
3
|
import { makeStyles } from 'tss-react/mui';
|
|
4
4
|
import JBrowseMenu from '@jbrowse/core/ui/Menu';
|
|
5
5
|
import { getSession } from '@jbrowse/core/util';
|
|
6
|
+
import SanitizedHTML from '@jbrowse/core/ui/SanitizedHTML';
|
|
6
7
|
import { readConfObject, } from '@jbrowse/core/configuration';
|
|
7
8
|
// icons
|
|
8
9
|
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
|
|
9
10
|
// locals
|
|
10
11
|
import { isUnsupported } from '../util';
|
|
11
|
-
import { SanitizedHTML } from '@jbrowse/core/ui';
|
|
12
12
|
const useStyles = makeStyles()(theme => ({
|
|
13
13
|
compactCheckbox: {
|
|
14
14
|
padding: 0,
|
|
@@ -31,7 +31,7 @@ export default function TrackLabel({ data }) {
|
|
|
31
31
|
React.createElement(FormControlLabel, { className: classes.checkboxLabel, control: React.createElement(Checkbox, { className: classes.compactCheckbox, checked: checked, onChange: () => onChange(id), disabled: isUnsupported(name), inputProps: {
|
|
32
32
|
// @ts-expect-error
|
|
33
33
|
'data-testid': `htsTrackEntry-${id}`,
|
|
34
|
-
} }), label: React.createElement("div", { style: { background: selected ? '#cccc' : undefined } },
|
|
34
|
+
} }), label: React.createElement("div", { "data-testid": `htsTrackLabel-${id}`, style: { background: selected ? '#cccc' : undefined } },
|
|
35
35
|
React.createElement(SanitizedHTML, { html: name })) })),
|
|
36
36
|
React.createElement(IconButton, { onClick: e => setInfo({ target: e.currentTarget, id, conf }), style: { padding: 0 }, "data-testid": `htsTrackEntryMenu-${id}` },
|
|
37
37
|
React.createElement(MoreHorizIcon, null)),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
2
|
-
import { HierarchicalTrackSelectorModel
|
|
2
|
+
import { HierarchicalTrackSelectorModel } from '../model';
|
|
3
|
+
import { TreeNode } from '../generateHierarchy';
|
|
3
4
|
export interface NodeData {
|
|
4
5
|
nestingLevel: number;
|
|
5
6
|
checked: boolean;
|
|
@@ -6,8 +6,7 @@ export function treeToMap(tree, map = new Map()) {
|
|
|
6
6
|
if (tree.id && tree.children.length) {
|
|
7
7
|
map.set(tree.id, tree);
|
|
8
8
|
}
|
|
9
|
-
for (
|
|
10
|
-
const node = tree.children[i];
|
|
9
|
+
for (const node of tree.children) {
|
|
11
10
|
treeToMap(node, map);
|
|
12
11
|
}
|
|
13
12
|
return map;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
2
|
+
export declare function filterTracks(tracks: AnyConfigurationModel[], self: {
|
|
3
|
+
view?: {
|
|
4
|
+
type: string;
|
|
5
|
+
trackSelectorAnyOverlap?: boolean;
|
|
6
|
+
};
|
|
7
|
+
assemblyNames: string[];
|
|
8
|
+
}): ({
|
|
9
|
+
[x: string]: any;
|
|
10
|
+
} & import("mobx-state-tree/dist/internal").NonEmptyObject & {
|
|
11
|
+
setSubschema(slotName: string, data: unknown): any;
|
|
12
|
+
} & import("mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>)[];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readConfObject, } from '@jbrowse/core/configuration';
|
|
2
|
+
import { getEnv, getSession, notEmpty } from '@jbrowse/core/util';
|
|
3
|
+
import { hasAllOverlap, hasAnyOverlap } from './util';
|
|
4
|
+
export function filterTracks(tracks, self) {
|
|
5
|
+
const { assemblyManager } = getSession(self);
|
|
6
|
+
const { pluginManager } = getEnv(self);
|
|
7
|
+
const { view } = self;
|
|
8
|
+
if (!view) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
const trackListAssemblies = self.assemblyNames
|
|
12
|
+
.map(a => assemblyManager.get(a))
|
|
13
|
+
.filter(notEmpty);
|
|
14
|
+
return tracks
|
|
15
|
+
.filter(c => {
|
|
16
|
+
const trackAssemblyNames = readConfObject(c, 'assemblyNames');
|
|
17
|
+
const trackAssemblies = trackAssemblyNames === null || trackAssemblyNames === void 0 ? void 0 : trackAssemblyNames.map(name => assemblyManager.get(name)).filter(notEmpty);
|
|
18
|
+
return view.trackSelectorAnyOverlap
|
|
19
|
+
? hasAnyOverlap(trackAssemblies, trackListAssemblies)
|
|
20
|
+
: hasAllOverlap(trackAssemblies, trackListAssemblies);
|
|
21
|
+
})
|
|
22
|
+
.filter(c => {
|
|
23
|
+
const { displayTypes } = pluginManager.getViewType(view.type);
|
|
24
|
+
const compatDisplays = displayTypes.map(d => d.name);
|
|
25
|
+
const trackDisplays = c.displays.map((d) => d.type);
|
|
26
|
+
return hasAnyOverlap(compatDisplays, trackDisplays);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
2
|
+
export interface TreeNode {
|
|
3
|
+
name: string;
|
|
4
|
+
id: string;
|
|
5
|
+
conf?: AnyConfigurationModel;
|
|
6
|
+
checked?: boolean;
|
|
7
|
+
isOpenByDefault?: boolean;
|
|
8
|
+
children: TreeNode[];
|
|
9
|
+
}
|
|
10
|
+
export declare function generateHierarchy(model: {
|
|
11
|
+
filterText: string;
|
|
12
|
+
activeSortTrackNames: boolean;
|
|
13
|
+
activeSortCategories: boolean;
|
|
14
|
+
view?: {
|
|
15
|
+
tracks: {
|
|
16
|
+
configuration: AnyConfigurationModel;
|
|
17
|
+
}[];
|
|
18
|
+
};
|
|
19
|
+
}, trackConfs: AnyConfigurationModel[], collapsed: {
|
|
20
|
+
get: (arg: string) => boolean | undefined;
|
|
21
|
+
}, extra?: string): TreeNode[];
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { readConfObject, } from '@jbrowse/core/configuration';
|
|
2
|
+
import { getSession } from '@jbrowse/core/util';
|
|
3
|
+
import { getTrackName } from '@jbrowse/core/util/tracks';
|
|
4
|
+
// locals
|
|
5
|
+
import { matches } from './util';
|
|
6
|
+
function sortConfs(confs, sortNames, sortCategories) {
|
|
7
|
+
// uses readConfObject instead of getTrackName so that the undefined
|
|
8
|
+
// reference sequence track is sorted to the top
|
|
9
|
+
const ret = confs.map(c => {
|
|
10
|
+
var _a, _b, _c;
|
|
11
|
+
return [
|
|
12
|
+
c,
|
|
13
|
+
readConfObject(c, 'name'),
|
|
14
|
+
((_a = readConfObject(c, 'category')) === null || _a === void 0 ? void 0 : _a[0]) || '',
|
|
15
|
+
((_b = readConfObject(c, 'category')) === null || _b === void 0 ? void 0 : _b[1]) || '',
|
|
16
|
+
((_c = readConfObject(c, 'category')) === null || _c === void 0 ? void 0 : _c[2]) || '',
|
|
17
|
+
];
|
|
18
|
+
});
|
|
19
|
+
if (sortNames) {
|
|
20
|
+
ret.sort((a, b) => a[1].localeCompare(b[1]));
|
|
21
|
+
}
|
|
22
|
+
if (sortCategories) {
|
|
23
|
+
// sort up to three sub-category levels, harder to code it to go deeper
|
|
24
|
+
// than this and likely rarely used
|
|
25
|
+
ret.sort((a, b) => {
|
|
26
|
+
if (a[2] !== b[2]) {
|
|
27
|
+
return a[2].localeCompare(b[2]);
|
|
28
|
+
}
|
|
29
|
+
else if (a[3] !== b[3]) {
|
|
30
|
+
return a[3].localeCompare(b[3]);
|
|
31
|
+
}
|
|
32
|
+
else if (a[4] !== b[4]) {
|
|
33
|
+
return a[4].localeCompare(b[4]);
|
|
34
|
+
}
|
|
35
|
+
return 0;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return ret.map(a => a[0]);
|
|
39
|
+
}
|
|
40
|
+
export function generateHierarchy(model, trackConfs, collapsed, extra) {
|
|
41
|
+
const hierarchy = { children: [] };
|
|
42
|
+
const { filterText, activeSortTrackNames, activeSortCategories, view } = model;
|
|
43
|
+
if (!view) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
const session = getSession(model);
|
|
47
|
+
const viewTracks = view.tracks;
|
|
48
|
+
const confs = trackConfs.filter(conf => matches(filterText, conf, session));
|
|
49
|
+
// uses getConf
|
|
50
|
+
for (const conf of sortConfs(confs, activeSortTrackNames, activeSortCategories)) {
|
|
51
|
+
// copy the categories since this array can be mutated downstream
|
|
52
|
+
const categories = [...(readConfObject(conf, 'category') || [])];
|
|
53
|
+
// hack where if trackId ends with sessionTrack, then push it to a
|
|
54
|
+
// category that starts with a space to force sort to the top
|
|
55
|
+
if (conf.trackId.endsWith('sessionTrack')) {
|
|
56
|
+
categories.unshift(' Session tracks');
|
|
57
|
+
}
|
|
58
|
+
let currLevel = hierarchy;
|
|
59
|
+
// find existing category to put track into or create it
|
|
60
|
+
for (let i = 0; i < categories.length; i++) {
|
|
61
|
+
const category = categories[i];
|
|
62
|
+
const ret = currLevel.children.find(c => c.name === category);
|
|
63
|
+
const id = [extra, categories.slice(0, i + 1).join(',')]
|
|
64
|
+
.filter(f => !!f)
|
|
65
|
+
.join('-');
|
|
66
|
+
if (!ret) {
|
|
67
|
+
const n = {
|
|
68
|
+
children: [],
|
|
69
|
+
name: category,
|
|
70
|
+
id,
|
|
71
|
+
isOpenByDefault: !collapsed.get(id),
|
|
72
|
+
};
|
|
73
|
+
currLevel.children.push(n);
|
|
74
|
+
currLevel = n;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
currLevel = ret;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// uses splice to try to put all leaf nodes above "category nodes" if you
|
|
81
|
+
// change the splice to a simple push and open
|
|
82
|
+
// test_data/test_order/config.json you will see the weirdness
|
|
83
|
+
const r = currLevel.children.findIndex(elt => elt.children.length);
|
|
84
|
+
const idx = r === -1 ? currLevel.children.length : r;
|
|
85
|
+
currLevel.children.splice(idx, 0, {
|
|
86
|
+
id: conf.trackId,
|
|
87
|
+
name: getTrackName(conf, session),
|
|
88
|
+
conf,
|
|
89
|
+
checked: viewTracks.some(f => f.configuration === conf),
|
|
90
|
+
children: [],
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return hierarchy.children;
|
|
94
|
+
}
|