@jbrowse/plugin-data-management 2.7.2 → 2.9.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/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 +57 -0
- package/dist/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.d.ts +6 -0
- package/dist/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.js +42 -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 +196 -17
- package/dist/HierarchicalTrackSelectorWidget/model.js +178 -23
- 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/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 +29 -0
- package/esm/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.d.ts +6 -0
- package/esm/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.js +37 -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 +196 -17
- package/esm/HierarchicalTrackSelectorWidget/model.js +180 -25
- 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 -3
- package/dist/HierarchicalTrackSelectorWidget/components/faceted/util.d.ts +0 -1
- package/esm/HierarchicalTrackSelectorWidget/components/faceted/util.d.ts +0 -1
|
@@ -1,42 +1,23 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import { Badge
|
|
3
|
-
import { makeStyles } from 'tss-react/mui';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Badge } from '@mui/material';
|
|
4
3
|
import { observer } from 'mobx-react';
|
|
5
|
-
import JBrowseMenu from '@jbrowse/core/ui/Menu';
|
|
6
4
|
import { getSession, getEnv } from '@jbrowse/core/util';
|
|
7
5
|
// icons
|
|
8
6
|
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
|
|
9
|
-
|
|
10
|
-
searchBox: {
|
|
11
|
-
margin: theme.spacing(2),
|
|
12
|
-
},
|
|
13
|
-
menuIcon: {
|
|
14
|
-
marginRight: theme.spacing(1),
|
|
15
|
-
marginBottom: 0,
|
|
16
|
-
},
|
|
17
|
-
}));
|
|
7
|
+
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
|
|
18
8
|
const ShoppingCart = observer(function ({ model, }) {
|
|
19
|
-
const { classes } = useStyles();
|
|
20
9
|
const { selection } = model;
|
|
21
10
|
const { pluginManager } = getEnv(model);
|
|
22
11
|
const session = getSession(model);
|
|
23
|
-
const [selectionEl, setSelectionEl] = useState();
|
|
24
12
|
const items = pluginManager.evaluateExtensionPoint('TrackSelector-multiTrackMenuItems', [], { session });
|
|
25
|
-
return (React.createElement(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
...items.map(item => ({
|
|
35
|
-
...item,
|
|
36
|
-
...('onClick' in item
|
|
37
|
-
? { onClick: () => item.onClick(model) }
|
|
38
|
-
: {}),
|
|
39
|
-
})),
|
|
40
|
-
] })));
|
|
13
|
+
return selection.length ? (React.createElement(CascadingMenuButton, { menuItems: [
|
|
14
|
+
{ label: 'Clear', onClick: () => model.clearSelection() },
|
|
15
|
+
...items.map(item => ({
|
|
16
|
+
...item,
|
|
17
|
+
...('onClick' in item ? { onClick: () => item.onClick(model) } : {}),
|
|
18
|
+
})),
|
|
19
|
+
] },
|
|
20
|
+
React.createElement(Badge, { badgeContent: selection.length, color: "primary" },
|
|
21
|
+
React.createElement(ShoppingCartIcon, null)))) : null;
|
|
41
22
|
});
|
|
42
23
|
export default ShoppingCart;
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
2
|
+
import { HierarchicalTrackSelectorModel } from '../../model';
|
|
3
|
+
declare const FacetFilter: ({ column, vals, width, model, }: {
|
|
3
4
|
column: {
|
|
4
5
|
field: string;
|
|
5
6
|
};
|
|
6
7
|
vals: [string, number][];
|
|
7
8
|
width: number;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}) => void;
|
|
12
|
-
filters: Record<string, string[]>;
|
|
13
|
-
}): React.JSX.Element;
|
|
9
|
+
model: HierarchicalTrackSelectorModel;
|
|
10
|
+
}) => React.JSX.Element;
|
|
11
|
+
export default FacetFilter;
|
|
@@ -6,6 +6,7 @@ import ClearIcon from '@mui/icons-material/Clear';
|
|
|
6
6
|
import MinimizeIcon from '@mui/icons-material/Minimize';
|
|
7
7
|
import AddIcon from '@mui/icons-material/Add';
|
|
8
8
|
import { coarseStripHTML } from '@jbrowse/core/util';
|
|
9
|
+
import { observer } from 'mobx-react';
|
|
9
10
|
const useStyles = makeStyles()(theme => ({
|
|
10
11
|
facet: {
|
|
11
12
|
margin: 0,
|
|
@@ -24,22 +25,22 @@ function ExpandButton({ visible, onClick, }) {
|
|
|
24
25
|
return (React.createElement(Tooltip, { title: "Minimize/expand this facet filter" },
|
|
25
26
|
React.createElement(IconButton, { onClick: () => onClick(), size: "small" }, visible ? React.createElement(MinimizeIcon, null) : React.createElement(AddIcon, null))));
|
|
26
27
|
}
|
|
27
|
-
|
|
28
|
+
const FacetFilter = observer(function ({ column, vals, width, model, }) {
|
|
28
29
|
const { classes } = useStyles();
|
|
29
30
|
const [visible, setVisible] = useState(true);
|
|
31
|
+
const { faceted } = model;
|
|
32
|
+
const { filters } = faceted;
|
|
30
33
|
return (React.createElement(FormControl, { key: column.field, className: classes.facet, style: { width } },
|
|
31
34
|
React.createElement("div", { style: { display: 'flex' } },
|
|
32
35
|
React.createElement(Typography, null, column.field),
|
|
33
|
-
React.createElement(ClearButton, { onClick: () =>
|
|
36
|
+
React.createElement(ClearButton, { onClick: () => model.faceted.setFilter(column.field, []) }),
|
|
34
37
|
React.createElement(ExpandButton, { visible: visible, onClick: () => setVisible(!visible) })),
|
|
35
|
-
visible ? (React.createElement(Select, { multiple: true, native: true, className: classes.select, value: filters
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.map(opt => opt.value),
|
|
42
|
-
});
|
|
38
|
+
visible ? (React.createElement(Select, { multiple: true, native: true, className: classes.select, value: filters.get(column.field) || [], onChange: event => {
|
|
39
|
+
faceted.setFilter(column.field,
|
|
40
|
+
// @ts-expect-error
|
|
41
|
+
[...event.target.options]
|
|
42
|
+
.filter(opt => opt.selected)
|
|
43
|
+
.map(opt => opt.value));
|
|
43
44
|
} }, vals
|
|
44
45
|
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
45
46
|
.map(([name, count]) => (React.createElement("option", { key: name, value: name },
|
|
@@ -47,4 +48,5 @@ export default function FacetFilter({ column, vals, width, dispatch, filters, })
|
|
|
47
48
|
" (",
|
|
48
49
|
count,
|
|
49
50
|
")"))))) : null));
|
|
50
|
-
}
|
|
51
|
+
});
|
|
52
|
+
export default FacetFilter;
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
2
|
+
import { HierarchicalTrackSelectorModel } from '../../model';
|
|
3
|
+
declare const FacetFilters: ({ rows, columns, width, model, }: {
|
|
3
4
|
rows: Record<string, unknown>[];
|
|
4
|
-
filters: Record<string, string[]>;
|
|
5
5
|
columns: {
|
|
6
6
|
field: string;
|
|
7
7
|
}[];
|
|
8
|
-
dispatch: (arg: {
|
|
9
|
-
key: string;
|
|
10
|
-
val: string[];
|
|
11
|
-
}) => void;
|
|
12
8
|
width: number;
|
|
13
|
-
|
|
9
|
+
model: HierarchicalTrackSelectorModel;
|
|
10
|
+
}) => React.JSX.Element;
|
|
11
|
+
export default FacetFilters;
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import FacetFilter from './FacetFilter';
|
|
3
|
-
|
|
3
|
+
import { observer } from 'mobx-react';
|
|
4
|
+
const FacetFilters = observer(function ({ rows, columns, width, model, }) {
|
|
4
5
|
var _a, _b;
|
|
6
|
+
const { faceted } = model;
|
|
7
|
+
const { filters } = faceted;
|
|
5
8
|
const facets = columns.slice(1);
|
|
6
9
|
const uniqs = new Map(facets.map(f => [f.field, new Map()]));
|
|
7
10
|
// this code "stages the facet filters" in order that the user has selected
|
|
8
11
|
// them, which relies on the js behavior that the order of the returned keys is
|
|
9
12
|
// related to the insertion order.
|
|
10
|
-
const filterKeys =
|
|
13
|
+
const filterKeys = faceted.filters.keys();
|
|
11
14
|
const facetKeys = facets.map(f => f.field);
|
|
12
15
|
const ret = new Set();
|
|
13
16
|
for (const entry of filterKeys) {
|
|
14
17
|
// give non-empty filters priority
|
|
15
|
-
if ((_a = filters
|
|
18
|
+
if ((_a = filters.get(entry)) === null || _a === void 0 ? void 0 : _a.length) {
|
|
16
19
|
ret.add(entry);
|
|
17
20
|
}
|
|
18
21
|
}
|
|
@@ -35,12 +38,13 @@ export default function FacetFilters({ rows, columns, dispatch, filters, width,
|
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
|
-
const filter = ((_b = filters
|
|
41
|
+
const filter = ((_b = filters.get(facet)) === null || _b === void 0 ? void 0 : _b.length)
|
|
42
|
+
? new Set(filters.get(facet))
|
|
43
|
+
: undefined;
|
|
39
44
|
currentRows = currentRows.filter(row => {
|
|
40
45
|
return filter !== undefined ? filter.has(row[facet]) : true;
|
|
41
46
|
});
|
|
42
47
|
}
|
|
43
|
-
return (React.createElement("div", null, facets.map(column => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
48
|
+
return (React.createElement("div", null, facets.map(column => (React.createElement(FacetFilter, { key: column.field, vals: [...uniqs.get(column.field)], column: column, width: width, model: model })))));
|
|
49
|
+
});
|
|
50
|
+
export default FacetFilters;
|
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { HierarchicalTrackSelectorModel } from '../../model';
|
|
3
|
-
export default function FacetedHeader({
|
|
4
|
-
setFilterText: (arg: string) => void;
|
|
5
|
-
setUseShoppingCart: (arg: boolean) => void;
|
|
6
|
-
setShowSparse: (arg: boolean) => void;
|
|
7
|
-
setShowFilters: (arg: boolean) => void;
|
|
8
|
-
setShowOptions: (arg: boolean) => void;
|
|
9
|
-
filterText: string;
|
|
10
|
-
showOptions: boolean;
|
|
11
|
-
useShoppingCart: boolean;
|
|
12
|
-
showSparse: boolean;
|
|
13
|
-
showFilters: boolean;
|
|
3
|
+
export default function FacetedHeader({ model, }: {
|
|
14
4
|
model: HierarchicalTrackSelectorModel;
|
|
15
5
|
}): React.JSX.Element;
|
|
@@ -6,14 +6,16 @@ import ClearIcon from '@mui/icons-material/Clear';
|
|
|
6
6
|
import MoreVert from '@mui/icons-material/MoreVert';
|
|
7
7
|
// locals
|
|
8
8
|
import ShoppingCart from '../ShoppingCart';
|
|
9
|
-
export default function FacetedHeader({
|
|
9
|
+
export default function FacetedHeader({ model, }) {
|
|
10
|
+
const { faceted } = model;
|
|
10
11
|
const [anchorEl, setAnchorEl] = useState(null);
|
|
12
|
+
const { showOptions, showFilters, showSparse, useShoppingCart } = faceted;
|
|
11
13
|
return (React.createElement(React.Fragment, null,
|
|
12
14
|
React.createElement(Grid, { container: true, spacing: 4, alignItems: "center" },
|
|
13
15
|
React.createElement(Grid, { item: true },
|
|
14
|
-
React.createElement(TextField, { label: "Search...", value: filterText, onChange: event => setFilterText(event.target.value), InputProps: {
|
|
16
|
+
React.createElement(TextField, { label: "Search...", value: faceted.filterText, onChange: event => faceted.setFilterText(event.target.value), InputProps: {
|
|
15
17
|
endAdornment: (React.createElement(InputAdornment, { position: "end" },
|
|
16
|
-
React.createElement(IconButton, { onClick: () => setFilterText('') },
|
|
18
|
+
React.createElement(IconButton, { onClick: () => faceted.setFilterText('') },
|
|
17
19
|
React.createElement(ClearIcon, null)))),
|
|
18
20
|
} })),
|
|
19
21
|
React.createElement(Grid, { item: true },
|
|
@@ -27,25 +29,25 @@ export default function FacetedHeader({ setFilterText, setUseShoppingCart, setSh
|
|
|
27
29
|
}, menuItems: [
|
|
28
30
|
{
|
|
29
31
|
label: 'Add tracks to selection instead of turning them on/off',
|
|
30
|
-
onClick: () => setUseShoppingCart(!useShoppingCart),
|
|
32
|
+
onClick: () => faceted.setUseShoppingCart(!useShoppingCart),
|
|
31
33
|
type: 'checkbox',
|
|
32
34
|
checked: useShoppingCart,
|
|
33
35
|
},
|
|
34
36
|
{
|
|
35
37
|
label: 'Show sparse metadata columns',
|
|
36
|
-
onClick: () => setShowSparse(!showSparse),
|
|
38
|
+
onClick: () => faceted.setShowSparse(!showSparse),
|
|
37
39
|
checked: showSparse,
|
|
38
40
|
type: 'checkbox',
|
|
39
41
|
},
|
|
40
42
|
{
|
|
41
43
|
label: 'Show facet filters',
|
|
42
|
-
onClick: () => setShowFilters(!showFilters),
|
|
44
|
+
onClick: () => faceted.setShowFilters(!showFilters),
|
|
43
45
|
checked: showFilters,
|
|
44
46
|
type: 'checkbox',
|
|
45
47
|
},
|
|
46
48
|
{
|
|
47
49
|
label: 'Show extra table options',
|
|
48
|
-
onClick: () => setShowOptions(!showOptions),
|
|
50
|
+
onClick: () => faceted.setShowOptions(!showOptions),
|
|
49
51
|
checked: showOptions,
|
|
50
52
|
type: 'checkbox',
|
|
51
53
|
},
|
|
@@ -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,29 @@
|
|
|
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
|
+
model.view.toggleTrack(t.trackId);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
})),
|
|
26
|
+
...extraMenuItems,
|
|
27
|
+
] }, children)) : null;
|
|
28
|
+
});
|
|
29
|
+
export default DropdownTrackSelector;
|
|
@@ -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 GradeIcon from '@mui/icons-material/Grade';
|
|
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 FavoriteTracks = observer(function ({ model, }) {
|
|
14
|
+
const { classes } = useStyles();
|
|
15
|
+
const { view, favoriteTracks } = model;
|
|
16
|
+
return view ? (React.createElement(DropdownTrackSelector, { onClick: () => model.setFavoritesCounter(0), tracks: favoriteTracks, model: model, extraMenuItems: favoriteTracks.length
|
|
17
|
+
? [
|
|
18
|
+
{ type: 'divider' },
|
|
19
|
+
{
|
|
20
|
+
label: 'Clear favorites',
|
|
21
|
+
onClick: () => model.clearFavorites(),
|
|
22
|
+
},
|
|
23
|
+
]
|
|
24
|
+
: [
|
|
25
|
+
{
|
|
26
|
+
label: 'No favorite tracks yet',
|
|
27
|
+
onClick: () => { },
|
|
28
|
+
},
|
|
29
|
+
] },
|
|
30
|
+
React.createElement(Tooltip, { title: "Favorite tracks" },
|
|
31
|
+
React.createElement(Badge, { classes: { badge: classes.smallBadge }, color: "secondary", anchorOrigin: {
|
|
32
|
+
vertical: 'bottom',
|
|
33
|
+
horizontal: 'right',
|
|
34
|
+
}, style: { marginRight: 10 }, badgeContent: model.favoritesCounter },
|
|
35
|
+
React.createElement(GradeIcon, null))))) : null;
|
|
36
|
+
});
|
|
37
|
+
export default FavoriteTracks;
|