@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.
Files changed (83) hide show
  1. package/dist/AddTrackWidget/model.d.ts +81 -0
  2. package/dist/AddTrackWidget/model.js +81 -0
  3. package/dist/HierarchicalTrackSelectorWidget/components/ShoppingCart.d.ts +1 -1
  4. package/dist/HierarchicalTrackSelectorWidget/components/ShoppingCart.js +11 -53
  5. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.d.ts +5 -7
  6. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.js +12 -11
  7. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.d.ts +5 -7
  8. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js +11 -8
  9. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.d.ts +1 -11
  10. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js +9 -7
  11. package/dist/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js +40 -135
  12. package/dist/HierarchicalTrackSelectorWidget/components/tree/DropdownTrackSelector.d.ts +12 -0
  13. package/dist/HierarchicalTrackSelectorWidget/components/tree/DropdownTrackSelector.js +57 -0
  14. package/dist/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.d.ts +6 -0
  15. package/dist/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.js +42 -0
  16. package/dist/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js +71 -46
  17. package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js +5 -34
  18. package/dist/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js +8 -3
  19. package/dist/HierarchicalTrackSelectorWidget/components/tree/RecentlyUsedTracks.d.ts +6 -0
  20. package/dist/HierarchicalTrackSelectorWidget/components/tree/RecentlyUsedTracks.js +42 -0
  21. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js +7 -6
  22. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js +9 -28
  23. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabelMenu.d.ts +12 -0
  24. package/dist/HierarchicalTrackSelectorWidget/components/tree/TrackLabelMenu.js +50 -0
  25. package/dist/HierarchicalTrackSelectorWidget/components/util.d.ts +3 -0
  26. package/dist/HierarchicalTrackSelectorWidget/components/util.js +5 -1
  27. package/dist/HierarchicalTrackSelectorWidget/facetedModel.d.ts +128 -0
  28. package/dist/HierarchicalTrackSelectorWidget/facetedModel.js +206 -0
  29. package/dist/HierarchicalTrackSelectorWidget/facetedUtil.d.ts +2 -0
  30. package/dist/HierarchicalTrackSelectorWidget/{components/faceted/util.js → facetedUtil.js} +5 -1
  31. package/dist/HierarchicalTrackSelectorWidget/generateHierarchy.d.ts +17 -5
  32. package/dist/HierarchicalTrackSelectorWidget/generateHierarchy.js +27 -21
  33. package/dist/HierarchicalTrackSelectorWidget/model.d.ts +196 -17
  34. package/dist/HierarchicalTrackSelectorWidget/model.js +178 -23
  35. package/dist/ucsc-trackhub/doConnect.d.ts +1 -0
  36. package/dist/ucsc-trackhub/doConnect.js +131 -0
  37. package/dist/ucsc-trackhub/model.d.ts +19 -2
  38. package/dist/ucsc-trackhub/model.js +16 -71
  39. package/dist/ucsc-trackhub/ucscTrackHub.d.ts +161 -4
  40. package/dist/ucsc-trackhub/ucscTrackHub.js +49 -166
  41. package/esm/AddTrackWidget/model.d.ts +81 -0
  42. package/esm/AddTrackWidget/model.js +81 -0
  43. package/esm/HierarchicalTrackSelectorWidget/components/ShoppingCart.d.ts +1 -1
  44. package/esm/HierarchicalTrackSelectorWidget/components/ShoppingCart.js +12 -31
  45. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.d.ts +5 -7
  46. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilter.js +13 -11
  47. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.d.ts +5 -7
  48. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetFilters.js +12 -8
  49. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.d.ts +1 -11
  50. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedHeader.js +9 -7
  51. package/esm/HierarchicalTrackSelectorWidget/components/faceted/FacetedSelector.js +41 -113
  52. package/esm/HierarchicalTrackSelectorWidget/components/tree/DropdownTrackSelector.d.ts +12 -0
  53. package/esm/HierarchicalTrackSelectorWidget/components/tree/DropdownTrackSelector.js +29 -0
  54. package/esm/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.d.ts +6 -0
  55. package/esm/HierarchicalTrackSelectorWidget/components/tree/FavoriteTracks.js +37 -0
  56. package/esm/HierarchicalTrackSelectorWidget/components/tree/HamburgerMenu.js +71 -46
  57. package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalHeader.js +6 -12
  58. package/esm/HierarchicalTrackSelectorWidget/components/tree/HierarchicalTree.js +8 -3
  59. package/esm/HierarchicalTrackSelectorWidget/components/tree/RecentlyUsedTracks.d.ts +6 -0
  60. package/esm/HierarchicalTrackSelectorWidget/components/tree/RecentlyUsedTracks.js +37 -0
  61. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackCategory.js +7 -6
  62. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabel.js +8 -27
  63. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabelMenu.d.ts +12 -0
  64. package/esm/HierarchicalTrackSelectorWidget/components/tree/TrackLabelMenu.js +45 -0
  65. package/esm/HierarchicalTrackSelectorWidget/components/util.d.ts +3 -0
  66. package/esm/HierarchicalTrackSelectorWidget/components/util.js +5 -1
  67. package/esm/HierarchicalTrackSelectorWidget/facetedModel.d.ts +128 -0
  68. package/esm/HierarchicalTrackSelectorWidget/facetedModel.js +202 -0
  69. package/esm/HierarchicalTrackSelectorWidget/facetedUtil.d.ts +2 -0
  70. package/esm/HierarchicalTrackSelectorWidget/{components/faceted/util.js → facetedUtil.js} +3 -0
  71. package/esm/HierarchicalTrackSelectorWidget/generateHierarchy.d.ts +17 -5
  72. package/esm/HierarchicalTrackSelectorWidget/generateHierarchy.js +27 -21
  73. package/esm/HierarchicalTrackSelectorWidget/model.d.ts +196 -17
  74. package/esm/HierarchicalTrackSelectorWidget/model.js +180 -25
  75. package/esm/ucsc-trackhub/doConnect.d.ts +1 -0
  76. package/esm/ucsc-trackhub/doConnect.js +127 -0
  77. package/esm/ucsc-trackhub/model.d.ts +19 -2
  78. package/esm/ucsc-trackhub/model.js +17 -72
  79. package/esm/ucsc-trackhub/ucscTrackHub.d.ts +161 -4
  80. package/esm/ucsc-trackhub/ucscTrackHub.js +48 -141
  81. package/package.json +3 -3
  82. package/dist/HierarchicalTrackSelectorWidget/components/faceted/util.d.ts +0 -1
  83. package/esm/HierarchicalTrackSelectorWidget/components/faceted/util.d.ts +0 -1
@@ -1,42 +1,23 @@
1
- import React, { useState } from 'react';
2
- import { Badge, IconButton } from '@mui/material';
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
- const useStyles = makeStyles()(theme => ({
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(React.Fragment, null,
26
- selection.length ? (React.createElement(IconButton, { className: classes.menuIcon, onClick: event => setSelectionEl(event.currentTarget) },
27
- React.createElement(Badge, { badgeContent: selection.length, color: "primary" },
28
- React.createElement(ShoppingCartIcon, null)))) : null,
29
- React.createElement(JBrowseMenu, { anchorEl: selectionEl, open: Boolean(selectionEl), onMenuItemClick: (_, callback) => {
30
- callback();
31
- setSelectionEl(undefined);
32
- }, onClose: () => setSelectionEl(undefined), menuItems: [
33
- { label: 'Clear', onClick: () => model.clearSelection() },
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
- export default function FacetFilter({ column, vals, width, dispatch, filters, }: {
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
- dispatch: (arg: {
9
- key: string;
10
- val: string[];
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
- export default function FacetFilter({ column, vals, width, dispatch, filters, }) {
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: () => dispatch({ key: column.field, val: [] }) }),
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[column.field], onChange: event => {
36
- dispatch({
37
- key: column.field,
38
- // @ts-expect-error
39
- val: [...event.target.options]
40
- .filter(opt => opt.selected)
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
- export default function FacetFilters({ rows, columns, dispatch, filters, width, }: {
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
- }): React.JSX.Element;
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
- export default function FacetFilters({ rows, columns, dispatch, filters, width, }) {
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 = Object.keys(filters);
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[entry]) === null || _a === void 0 ? void 0 : _a.length) {
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[facet]) === null || _b === void 0 ? void 0 : _b.length) ? new Set(filters[facet]) : undefined;
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
- return (React.createElement(FacetFilter, { key: column.field, vals: [...uniqs.get(column.field)], column: column, width: width, dispatch: dispatch, filters: filters }));
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({ setFilterText, setUseShoppingCart, setShowSparse, setShowFilters, setShowOptions, showOptions, showSparse, showFilters, useShoppingCart, filterText, model, }: {
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({ setFilterText, setUseShoppingCart, setShowSparse, setShowFilters, setShowOptions, showOptions, showSparse, showFilters, useShoppingCart, filterText, model, }) {
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, { useMemo, useState, useEffect, useReducer } from '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, getSession, measureGridWidth, useDebounce, useLocalStorage, } from '@jbrowse/core/util';
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 { getRootKeys } from './util';
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, _b;
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, id, row } = params;
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(IconButton, { onClick: e => setInfo({
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 { value } = params;
134
- return (React.createElement("div", { className: classes.cell }, value ? React.createElement(SanitizedHTML, { html: value }) : ''));
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
- width: (_a = widthsDebounced[e]) !== null && _a !== void 0 ? _a : 100,
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 { value } = params;
145
- return (React.createElement("div", { className: classes.cell }, value ? React.createElement(SanitizedHTML, { html: value }) : ''));
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
- 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) => {
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: newModel => setVisible(newModel), columnHeaderHeight: 35, checkboxSelection: true, disableRowSelectionOnClick: true, keepNonExistentRowsSelected: true, onRowSelectionModelChange: userSelectedIds => {
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].filter(x => !a1.has(x)).map(t => view.showTrack(t));
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
- const tracks = userSelectedIds.map(id => resolveIdentifier(schema, root, id));
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: { printOptions: { disableToolbarButton: true } },
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), style: { marginLeft: 5, background: 'grey', width: 5 } }),
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, dispatch: dispatch, filters: filters })))) : null)));
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,6 @@
1
+ import React from 'react';
2
+ import { HierarchicalTrackSelectorModel } from '../../model';
3
+ declare const FavoriteTracks: ({ model, }: {
4
+ model: HierarchicalTrackSelectorModel;
5
+ }) => React.JSX.Element | null;
6
+ export default FavoriteTracks;
@@ -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;