@jbrowse/plugin-linear-genome-view 2.10.3 → 2.11.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/BaseLinearDisplay/models/serverSideRenderedBlock.js +0 -1
- package/dist/LaunchLinearGenomeView/index.js +14 -6
- package/dist/LinearBasicDisplay/components/SetMaxHeight.d.ts +1 -1
- package/dist/LinearGenomeView/components/Cytobands.d.ts +1 -125
- package/dist/LinearGenomeView/components/ExportSvgDialog.js +9 -5
- package/dist/LinearGenomeView/components/Highlight.d.ts +3 -3
- package/dist/LinearGenomeView/components/Highlight.js +21 -17
- package/dist/LinearGenomeView/components/MiniControls.js +13 -15
- package/dist/LinearGenomeView/components/OverviewHighlight.d.ts +1 -1
- package/dist/LinearGenomeView/components/OverviewHighlight.js +20 -11
- package/dist/LinearGenomeView/components/OverviewRubberband.d.ts +1 -1
- package/dist/LinearGenomeView/components/OverviewScalebar.js +4 -1
- package/dist/LinearGenomeView/components/OverviewScalebarPolygon.d.ts +1 -1
- package/dist/LinearGenomeView/components/RefNameAutocomplete/index.d.ts +9 -9
- package/dist/LinearGenomeView/components/RefNameAutocomplete/index.js +28 -35
- package/dist/LinearGenomeView/components/Rubberband.d.ts +1 -1
- package/dist/LinearGenomeView/components/SearchBox.d.ts +1 -1
- package/dist/LinearGenomeView/components/SearchResultsTable.js +1 -1
- package/dist/LinearGenomeView/components/TrackContainer.js +10 -23
- package/dist/LinearGenomeView/components/TrackLabel.js +33 -6
- package/dist/LinearGenomeView/components/ZoomControls.js +4 -4
- package/dist/LinearGenomeView/model.d.ts +67 -7
- package/dist/LinearGenomeView/model.js +102 -16
- package/dist/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +1 -1
- package/dist/index.d.ts +159 -21
- package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.js +0 -1
- package/esm/LaunchLinearGenomeView/index.js +14 -6
- package/esm/LinearBasicDisplay/components/SetMaxHeight.d.ts +1 -1
- package/esm/LinearGenomeView/components/Cytobands.d.ts +1 -125
- package/esm/LinearGenomeView/components/ExportSvgDialog.js +9 -5
- package/esm/LinearGenomeView/components/Highlight.d.ts +3 -3
- package/esm/LinearGenomeView/components/Highlight.js +21 -17
- package/esm/LinearGenomeView/components/MiniControls.js +14 -16
- package/esm/LinearGenomeView/components/OverviewHighlight.d.ts +1 -1
- package/esm/LinearGenomeView/components/OverviewHighlight.js +20 -11
- package/esm/LinearGenomeView/components/OverviewRubberband.d.ts +1 -1
- package/esm/LinearGenomeView/components/OverviewScalebar.js +5 -2
- package/esm/LinearGenomeView/components/OverviewScalebarPolygon.d.ts +1 -1
- package/esm/LinearGenomeView/components/RefNameAutocomplete/index.d.ts +9 -9
- package/esm/LinearGenomeView/components/RefNameAutocomplete/index.js +28 -35
- package/esm/LinearGenomeView/components/Rubberband.d.ts +1 -1
- package/esm/LinearGenomeView/components/SearchBox.d.ts +1 -1
- package/esm/LinearGenomeView/components/SearchResultsTable.js +1 -1
- package/esm/LinearGenomeView/components/TrackContainer.js +10 -23
- package/esm/LinearGenomeView/components/TrackLabel.js +33 -6
- package/esm/LinearGenomeView/components/TracksContainer.js +2 -2
- package/esm/LinearGenomeView/components/ZoomControls.js +4 -4
- package/esm/LinearGenomeView/model.d.ts +67 -7
- package/esm/LinearGenomeView/model.js +103 -17
- package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +1 -1
- package/esm/index.d.ts +159 -21
- package/package.json +2 -2
|
@@ -10,6 +10,10 @@ function LoadingMessage() {
|
|
|
10
10
|
function useSvgLocal(key, val) {
|
|
11
11
|
return useLocalStorage('svg-' + key, val);
|
|
12
12
|
}
|
|
13
|
+
function TextField2({ children, ...rest }) {
|
|
14
|
+
return (React.createElement("div", null,
|
|
15
|
+
React.createElement(TextField, { ...rest }, children)));
|
|
16
|
+
}
|
|
13
17
|
export default function ExportSvgDialog({ model, handleClose, }) {
|
|
14
18
|
const session = getSession(model);
|
|
15
19
|
const offscreenCanvas = typeof OffscreenCanvas !== 'undefined';
|
|
@@ -22,15 +26,13 @@ export default function ExportSvgDialog({ model, handleClose, }) {
|
|
|
22
26
|
return (React.createElement(Dialog, { open: true, onClose: handleClose, title: "Export SVG" },
|
|
23
27
|
React.createElement(DialogContent, null,
|
|
24
28
|
error ? (React.createElement(ErrorMessage, { error: error })) : loading ? (React.createElement(LoadingMessage, null)) : null,
|
|
25
|
-
React.createElement(
|
|
26
|
-
React.createElement("
|
|
27
|
-
React.createElement(TextField, { select: true, label: "Track label positioning", variant: "outlined", style: { width: 150 }, value: trackLabels, onChange: event => setTrackLabels(event.target.value) },
|
|
29
|
+
React.createElement(TextField2, { helperText: "filename", value: filename, onChange: event => setFilename(event.target.value) }),
|
|
30
|
+
React.createElement(TextField2, { select: true, label: "Track label positioning", variant: "outlined", style: { width: 150 }, value: trackLabels, onChange: event => setTrackLabels(event.target.value) },
|
|
28
31
|
React.createElement(MenuItem, { value: "offset" }, "Offset"),
|
|
29
32
|
React.createElement(MenuItem, { value: "overlay" }, "Overlay"),
|
|
30
33
|
React.createElement(MenuItem, { value: "left" }, "Left"),
|
|
31
34
|
React.createElement(MenuItem, { value: "none" }, "None")),
|
|
32
|
-
React.createElement("
|
|
33
|
-
session.allThemes ? (React.createElement(TextField, { select: true, label: "Theme", variant: "outlined", value: themeName, onChange: event => setThemeName(event.target.value) }, Object.entries(session.allThemes()).map(([key, val]) => (React.createElement(MenuItem, { key: key, value: key },
|
|
35
|
+
session.allThemes ? (React.createElement(TextField2, { select: true, label: "Theme", variant: "outlined", value: themeName, onChange: event => setThemeName(event.target.value) }, Object.entries(session.allThemes()).map(([key, val]) => (React.createElement(MenuItem, { key: key, value: key },
|
|
34
36
|
// @ts-expect-error
|
|
35
37
|
val.name || '(Unknown name)'))))) : null,
|
|
36
38
|
offscreenCanvas ? (React.createElement(FormControlLabel, { control: React.createElement(Checkbox, { checked: rasterizeLayers, onChange: () => setRasterizeLayers(val => !val) }), label: "Rasterize canvas based tracks? File may be much larger if this is turned off" })) : (React.createElement(Typography, null, "Note: rasterizing layers not yet supported in this browser, so SVG size may be large"))),
|
|
@@ -51,6 +53,8 @@ export default function ExportSvgDialog({ model, handleClose, }) {
|
|
|
51
53
|
catch (e) {
|
|
52
54
|
console.error(e);
|
|
53
55
|
setError(e);
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
54
58
|
setLoading(false);
|
|
55
59
|
}
|
|
56
60
|
} }, "Submit"))));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { LinearGenomeViewModel } from '../model';
|
|
3
3
|
type LGV = LinearGenomeViewModel;
|
|
4
|
-
declare const
|
|
4
|
+
declare const HighlightGroup: ({ model, }: {
|
|
5
5
|
model: LGV;
|
|
6
|
-
}) => React.JSX.Element
|
|
7
|
-
export default
|
|
6
|
+
}) => React.JSX.Element[];
|
|
7
|
+
export default HighlightGroup;
|
|
@@ -10,28 +10,28 @@ import LinkIcon from '@mui/icons-material/Link';
|
|
|
10
10
|
import CloseIcon from '@mui/icons-material/Close';
|
|
11
11
|
import BookmarkIcon from '@mui/icons-material/Bookmark';
|
|
12
12
|
const useStyles = makeStyles()(theme => {
|
|
13
|
-
var _a, _b
|
|
13
|
+
var _a, _b;
|
|
14
14
|
return ({
|
|
15
15
|
highlight: {
|
|
16
16
|
height: '100%',
|
|
17
17
|
position: 'absolute',
|
|
18
|
-
|
|
18
|
+
overflow: 'hidden',
|
|
19
|
+
background: `${colord((_b = (_a = theme.palette.highlight) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod')
|
|
19
20
|
.alpha(0.35)
|
|
20
21
|
.toRgbString()}`,
|
|
21
|
-
borderLeft: `1px solid ${(_d = (_c = theme.palette.quaternary) === null || _c === void 0 ? void 0 : _c.main) !== null && _d !== void 0 ? _d : 'goldenrod'}`,
|
|
22
|
-
borderRight: `1px solid ${(_f = (_e = theme.palette.quaternary) === null || _e === void 0 ? void 0 : _e.main) !== null && _f !== void 0 ? _f : 'goldenrod'}`,
|
|
23
22
|
},
|
|
24
23
|
});
|
|
25
24
|
});
|
|
26
|
-
const Highlight = observer(function Highlight({ model }) {
|
|
27
|
-
var _a, _b;
|
|
25
|
+
const Highlight = observer(function Highlight({ model, highlight, }) {
|
|
26
|
+
var _a, _b, _c;
|
|
28
27
|
const { classes } = useStyles();
|
|
29
28
|
const [open, setOpen] = useState(false);
|
|
30
29
|
const anchorEl = useRef(null);
|
|
31
|
-
const color = (_b = (_a = useTheme().palette.
|
|
30
|
+
const color = (_b = (_a = useTheme().palette.highlight) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod';
|
|
32
31
|
const session = getSession(model);
|
|
32
|
+
const { assemblyManager } = session;
|
|
33
33
|
const dismissHighlight = () => {
|
|
34
|
-
model.
|
|
34
|
+
model.removeHighlight(highlight);
|
|
35
35
|
};
|
|
36
36
|
const menuItems = [
|
|
37
37
|
{
|
|
@@ -48,7 +48,7 @@ const Highlight = observer(function Highlight({ model }) {
|
|
|
48
48
|
bookmarkWidget = session.addWidget('GridBookmarkWidget', 'GridBookmark');
|
|
49
49
|
}
|
|
50
50
|
// @ts-ignore
|
|
51
|
-
bookmarkWidget.addBookmark(
|
|
51
|
+
bookmarkWidget.addBookmark(highlight);
|
|
52
52
|
dismissHighlight();
|
|
53
53
|
},
|
|
54
54
|
},
|
|
@@ -56,9 +56,6 @@ const Highlight = observer(function Highlight({ model }) {
|
|
|
56
56
|
function handleClose() {
|
|
57
57
|
setOpen(false);
|
|
58
58
|
}
|
|
59
|
-
if (!model.highlight) {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
59
|
// coords
|
|
63
60
|
const mapCoords = (r) => {
|
|
64
61
|
const s = model.bpToPx({
|
|
@@ -76,19 +73,26 @@ const Highlight = observer(function Highlight({ model }) {
|
|
|
76
73
|
}
|
|
77
74
|
: undefined;
|
|
78
75
|
};
|
|
79
|
-
const
|
|
80
|
-
|
|
76
|
+
const asm = assemblyManager.get(highlight === null || highlight === void 0 ? void 0 : highlight.assemblyName);
|
|
77
|
+
const h = mapCoords({
|
|
78
|
+
...highlight,
|
|
79
|
+
refName: (_c = asm === null || asm === void 0 ? void 0 : asm.getCanonicalRefName(highlight.refName)) !== null && _c !== void 0 ? _c : highlight.refName,
|
|
80
|
+
});
|
|
81
|
+
return h ? (React.createElement("div", { className: classes.highlight, style: {
|
|
81
82
|
left: h.left,
|
|
82
83
|
width: h.width,
|
|
83
84
|
} },
|
|
84
85
|
React.createElement(Tooltip, { title: 'Highlighted from URL parameter', arrow: true },
|
|
85
|
-
React.createElement(IconButton, { ref: anchorEl, onClick: () => setOpen(true) },
|
|
86
|
+
React.createElement(IconButton, { ref: anchorEl, onClick: () => setOpen(true), style: { zIndex: 3 } },
|
|
86
87
|
React.createElement(LinkIcon, { fontSize: "small", sx: {
|
|
87
88
|
color: `${colord(color).darken(0.2).toRgbString()}`,
|
|
88
89
|
} }))),
|
|
89
90
|
React.createElement(Menu, { anchorEl: anchorEl.current, onMenuItemClick: (_event, callback) => {
|
|
90
91
|
callback(session);
|
|
91
92
|
handleClose();
|
|
92
|
-
}, open: open, onClose: handleClose, menuItems: menuItems }))) : null
|
|
93
|
+
}, open: open, onClose: handleClose, menuItems: menuItems }))) : null;
|
|
94
|
+
});
|
|
95
|
+
const HighlightGroup = observer(function HighlightGroup({ model, }) {
|
|
96
|
+
return model.highlight.map((highlight, idx) => (React.createElement(Highlight, { key: JSON.stringify(highlight) + '-' + idx, model: model, highlight: highlight })));
|
|
93
97
|
});
|
|
94
|
-
export default
|
|
98
|
+
export default HighlightGroup;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { observer } from 'mobx-react';
|
|
3
|
-
import { IconButton, Paper } from '@mui/material';
|
|
3
|
+
import { IconButton, Paper, alpha } from '@mui/material';
|
|
4
|
+
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
|
|
5
|
+
import { makeStyles } from 'tss-react/mui';
|
|
6
|
+
import { getSession } from '@jbrowse/core/util';
|
|
4
7
|
// icons
|
|
5
8
|
import ZoomIn from '@mui/icons-material/ZoomIn';
|
|
6
9
|
import ZoomOut from '@mui/icons-material/ZoomOut';
|
|
7
10
|
import ArrowDown from '@mui/icons-material/KeyboardArrowDown';
|
|
8
|
-
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
|
|
9
|
-
import { makeStyles } from 'tss-react/mui';
|
|
10
|
-
import { getSession } from '@jbrowse/core/util';
|
|
11
11
|
const useStyles = makeStyles()(theme => ({
|
|
12
12
|
background: {
|
|
13
13
|
position: 'absolute',
|
|
@@ -16,22 +16,20 @@ const useStyles = makeStyles()(theme => ({
|
|
|
16
16
|
background: theme.palette.background.paper,
|
|
17
17
|
},
|
|
18
18
|
focusedBackground: {
|
|
19
|
-
background: theme.palette.secondary.light,
|
|
20
|
-
svg: {
|
|
21
|
-
fill: theme.palette.secondary.contrastText,
|
|
22
|
-
},
|
|
19
|
+
background: alpha(theme.palette.secondary.light, 0.2),
|
|
23
20
|
},
|
|
24
21
|
}));
|
|
25
22
|
const MiniControls = observer(function ({ model, }) {
|
|
26
|
-
const { classes
|
|
23
|
+
const { classes } = useStyles();
|
|
27
24
|
const { id, bpPerPx, maxBpPerPx, minBpPerPx, scaleFactor, hideHeader } = model;
|
|
28
25
|
const { focusedViewId } = getSession(model);
|
|
29
|
-
return hideHeader ? (React.createElement(Paper, { className:
|
|
30
|
-
React.createElement(
|
|
31
|
-
React.createElement(
|
|
32
|
-
|
|
33
|
-
React.createElement(
|
|
34
|
-
|
|
35
|
-
React.createElement(
|
|
26
|
+
return hideHeader ? (React.createElement(Paper, { className: classes.background },
|
|
27
|
+
React.createElement(Paper, { className: focusedViewId === id ? classes.focusedBackground : undefined },
|
|
28
|
+
React.createElement(CascadingMenuButton, { menuItems: model.menuItems() },
|
|
29
|
+
React.createElement(ArrowDown, { fontSize: "small" })),
|
|
30
|
+
React.createElement(IconButton, { "data-testid": "zoom_out", onClick: () => model.zoom(bpPerPx * 2), disabled: bpPerPx >= maxBpPerPx - 0.0001 || scaleFactor !== 1 },
|
|
31
|
+
React.createElement(ZoomOut, { fontSize: "small" })),
|
|
32
|
+
React.createElement(IconButton, { "data-testid": "zoom_in", onClick: () => model.zoom(bpPerPx / 2), disabled: bpPerPx <= minBpPerPx + 0.0001 || scaleFactor !== 1 },
|
|
33
|
+
React.createElement(ZoomIn, { fontSize: "small" }))))) : null;
|
|
36
34
|
});
|
|
37
35
|
export default MiniControls;
|
|
@@ -2,23 +2,26 @@ import React from 'react';
|
|
|
2
2
|
import { observer } from 'mobx-react';
|
|
3
3
|
import { makeStyles } from 'tss-react/mui';
|
|
4
4
|
import { colord } from '@jbrowse/core/util/colord';
|
|
5
|
+
import { getSession, notEmpty, } from '@jbrowse/core/util';
|
|
5
6
|
const useStyles = makeStyles()(theme => {
|
|
6
7
|
var _a, _b, _c, _d, _e, _f;
|
|
7
8
|
return ({
|
|
8
9
|
highlight: {
|
|
9
10
|
height: '100%',
|
|
10
11
|
position: 'absolute',
|
|
11
|
-
background: `${colord((_b = (_a = theme.palette.
|
|
12
|
+
background: `${colord((_b = (_a = theme.palette.highlight) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod')
|
|
12
13
|
.alpha(0.35)
|
|
13
14
|
.toRgbString()}`,
|
|
14
|
-
borderLeft: `1px solid ${(_d = (_c = theme.palette.
|
|
15
|
-
borderRight: `1px solid ${(_f = (_e = theme.palette.
|
|
15
|
+
borderLeft: `1px solid ${(_d = (_c = theme.palette.highlight) === null || _c === void 0 ? void 0 : _c.main) !== null && _d !== void 0 ? _d : 'goldenrod'}`,
|
|
16
|
+
borderRight: `1px solid ${(_f = (_e = theme.palette.highlight) === null || _e === void 0 ? void 0 : _e.main) !== null && _f !== void 0 ? _f : 'goldenrod'}`,
|
|
16
17
|
},
|
|
17
18
|
});
|
|
18
19
|
});
|
|
19
20
|
const OverviewHighlight = observer(function OverviewHighlight({ model, overview, }) {
|
|
20
21
|
const { classes } = useStyles();
|
|
21
22
|
const { cytobandOffset } = model;
|
|
23
|
+
const session = getSession(model);
|
|
24
|
+
const { assemblyManager } = session;
|
|
22
25
|
// coords
|
|
23
26
|
const mapCoords = (r) => {
|
|
24
27
|
const s = overview.bpToPx({
|
|
@@ -36,13 +39,19 @@ const OverviewHighlight = observer(function OverviewHighlight({ model, overview,
|
|
|
36
39
|
}
|
|
37
40
|
: undefined;
|
|
38
41
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
42
|
+
return model.highlight
|
|
43
|
+
.map(h => {
|
|
44
|
+
var _a;
|
|
45
|
+
const asm = assemblyManager.get(h === null || h === void 0 ? void 0 : h.assemblyName);
|
|
46
|
+
return mapCoords({
|
|
47
|
+
...h,
|
|
48
|
+
refName: (_a = asm === null || asm === void 0 ? void 0 : asm.getCanonicalRefName(h.refName)) !== null && _a !== void 0 ? _a : h.refName,
|
|
49
|
+
});
|
|
50
|
+
})
|
|
51
|
+
.filter(notEmpty)
|
|
52
|
+
.map(({ left, width }, idx) => (React.createElement("div", { key: `${left}_${width}_${idx}`, className: classes.highlight, style: {
|
|
53
|
+
width: width,
|
|
54
|
+
left: left,
|
|
55
|
+
} })));
|
|
47
56
|
});
|
|
48
57
|
export default OverviewHighlight;
|
|
@@ -5,6 +5,6 @@ type LGV = LinearGenomeViewModel;
|
|
|
5
5
|
declare const OverviewRubberband: ({ model, overview, ControlComponent, }: {
|
|
6
6
|
model: LGV;
|
|
7
7
|
overview: Base1DViewModel;
|
|
8
|
-
ControlComponent?: React.ReactElement
|
|
8
|
+
ControlComponent?: React.ReactElement;
|
|
9
9
|
}) => React.JSX.Element;
|
|
10
10
|
export default OverviewRubberband;
|
|
@@ -4,7 +4,7 @@ import { makeStyles } from 'tss-react/mui';
|
|
|
4
4
|
import { observer } from 'mobx-react';
|
|
5
5
|
// core
|
|
6
6
|
import Base1DView from '@jbrowse/core/util/Base1DViewModel';
|
|
7
|
-
import { getSession, getTickDisplayStr } from '@jbrowse/core/util';
|
|
7
|
+
import { getEnv, getSession, getTickDisplayStr } from '@jbrowse/core/util';
|
|
8
8
|
import { ContentBlock } from '@jbrowse/core/util/blockTypes';
|
|
9
9
|
// locals
|
|
10
10
|
import { HEADER_BAR_HEIGHT, HEADER_OVERVIEW_HEIGHT, } from '..';
|
|
@@ -118,6 +118,7 @@ const Scalebar = observer(function ({ model, scale, overview, }) {
|
|
|
118
118
|
const { classes } = useStyles();
|
|
119
119
|
const theme = useTheme();
|
|
120
120
|
const { dynamicBlocks, showCytobands, cytobandOffset } = model;
|
|
121
|
+
const { pluginManager } = getEnv(model);
|
|
121
122
|
const visibleRegions = dynamicBlocks.contentBlocks;
|
|
122
123
|
const overviewVisibleRegions = overview.dynamicBlocks;
|
|
123
124
|
const { tertiary, primary } = theme.palette;
|
|
@@ -138,6 +139,7 @@ const Scalebar = observer(function ({ model, scale, overview, }) {
|
|
|
138
139
|
}) || 0;
|
|
139
140
|
const color = showCytobands ? '#f00' : scalebarColor;
|
|
140
141
|
const transparency = showCytobands ? 0.1 : 0.3;
|
|
142
|
+
const additional = pluginManager.evaluateExtensionPoint('LinearGenomeView-OverviewScalebarComponent', undefined, { model, overview });
|
|
141
143
|
return (React.createElement("div", { className: classes.scalebar },
|
|
142
144
|
React.createElement("div", { className: classes.scalebarVisibleRegion, style: {
|
|
143
145
|
width: lastOverviewPx - firstOverviewPx,
|
|
@@ -153,7 +155,8 @@ const Scalebar = observer(function ({ model, scale, overview, }) {
|
|
|
153
155
|
backgroundImage: 'repeating-linear-gradient(90deg, transparent, transparent 1px, rgba(255,255,255,.5) 1px, rgba(255,255,255,.5) 3px)',
|
|
154
156
|
} })) : (React.createElement(OverviewBox, { scale: scale, block: block, model: model, overview: overview, key: `${JSON.stringify(block)}-${idx}` }));
|
|
155
157
|
}),
|
|
156
|
-
React.createElement(OverviewHighlight, { model: model, overview: overview })
|
|
158
|
+
React.createElement(OverviewHighlight, { model: model, overview: overview }),
|
|
159
|
+
additional));
|
|
157
160
|
});
|
|
158
161
|
const OverviewScalebar = observer(function ({ model, children, }) {
|
|
159
162
|
const { classes } = useStyles();
|
|
@@ -4,6 +4,6 @@ import { LinearGenomeViewModel } from '..';
|
|
|
4
4
|
declare const OverviewScalebarPolygon: ({ model, overview, useOffset, }: {
|
|
5
5
|
model: LinearGenomeViewModel;
|
|
6
6
|
overview: Base1DViewModel;
|
|
7
|
-
useOffset?: boolean
|
|
7
|
+
useOffset?: boolean;
|
|
8
8
|
}) => React.JSX.Element | null;
|
|
9
9
|
export default OverviewScalebarPolygon;
|
|
@@ -4,15 +4,15 @@ import { TextFieldProps as TFP } from '@mui/material';
|
|
|
4
4
|
import { LinearGenomeViewModel } from '../../model';
|
|
5
5
|
declare const RefNameAutocomplete: ({ model, onSelect, assemblyName, style, fetchResults, onChange, value, showHelp, minWidth, maxWidth, TextFieldProps, }: {
|
|
6
6
|
model: LinearGenomeViewModel;
|
|
7
|
-
onSelect?: (
|
|
8
|
-
onChange?: (
|
|
9
|
-
assemblyName?: string
|
|
10
|
-
value?: string
|
|
7
|
+
onSelect?: (region: BaseResult) => void;
|
|
8
|
+
onChange?: (val: string) => void;
|
|
9
|
+
assemblyName?: string;
|
|
10
|
+
value?: string;
|
|
11
11
|
fetchResults: (query: string) => Promise<BaseResult[]>;
|
|
12
|
-
style?: React.CSSProperties
|
|
13
|
-
minWidth?: number
|
|
14
|
-
maxWidth?: number
|
|
15
|
-
showHelp?: boolean
|
|
16
|
-
TextFieldProps?: TFP
|
|
12
|
+
style?: React.CSSProperties;
|
|
13
|
+
minWidth?: number;
|
|
14
|
+
maxWidth?: number;
|
|
15
|
+
showHelp?: boolean;
|
|
16
|
+
TextFieldProps?: TFP;
|
|
17
17
|
}) => React.JSX.Element;
|
|
18
18
|
export default RefNameAutocomplete;
|
|
@@ -17,7 +17,8 @@ const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName,
|
|
|
17
17
|
const assembly = assemblyName ? assemblyManager.get(assemblyName) : undefined;
|
|
18
18
|
const { coarseVisibleLocStrings, hasDisplayedRegions } = model;
|
|
19
19
|
useEffect(() => {
|
|
20
|
-
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
21
|
+
;
|
|
21
22
|
(async () => {
|
|
22
23
|
try {
|
|
23
24
|
if (debouncedSearch === '' || !assemblyName) {
|
|
@@ -25,21 +26,14 @@ const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName,
|
|
|
25
26
|
}
|
|
26
27
|
setLoaded(false);
|
|
27
28
|
const results = await fetchResults(debouncedSearch);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
setSearchOptions(getDeduplicatedResult(results));
|
|
31
|
-
}
|
|
29
|
+
setLoaded(true);
|
|
30
|
+
setSearchOptions(getDeduplicatedResult(results));
|
|
32
31
|
}
|
|
33
32
|
catch (e) {
|
|
34
33
|
console.error(e);
|
|
35
|
-
|
|
36
|
-
session.notify(`${e}`, 'error');
|
|
37
|
-
}
|
|
34
|
+
session.notifyError(`${e}`, e);
|
|
38
35
|
}
|
|
39
36
|
})();
|
|
40
|
-
return () => {
|
|
41
|
-
active = false;
|
|
42
|
-
};
|
|
43
37
|
}, [assemblyName, fetchResults, debouncedSearch, session, model]);
|
|
44
38
|
const inputBoxVal = coarseVisibleLocStrings || value || '';
|
|
45
39
|
// heuristic, text width + 60 accommodates help icon and search icon
|
|
@@ -54,29 +48,28 @@ const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName,
|
|
|
54
48
|
}))) || [], [regions]);
|
|
55
49
|
// notes on implementation:
|
|
56
50
|
// The selectOnFocus setting helps highlight the field when clicked
|
|
57
|
-
return (React.createElement(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}, options: (searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.length) ? searchOptions : regionOptions, getOptionDisabled: option => option.group === 'limitOption', filterOptions: (opts, { inputValue }) => getFiltered(opts, inputValue), renderInput: params => (React.createElement(AutocompleteTextField, { showHelp: showHelp, params: params, inputBoxVal: inputBoxVal, TextFieldProps: TextFieldProps, setCurrentSearch: setCurrentSearch, setInputValue: setInputValue })), getOptionLabel: opt => typeof opt === 'string' ? opt : opt.result.getDisplayString() })));
|
|
51
|
+
return (React.createElement(Autocomplete, { "data-testid": "autocomplete", disableListWrap: true, disableClearable: true, disabled: !assemblyName, freeSolo: true, includeInputInList: true, selectOnFocus: true, style: { ...style, width }, value: inputBoxVal, loading: !loaded, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
|
|
52
|
+
setInputValue(newInputValue);
|
|
53
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(newInputValue);
|
|
54
|
+
}, loadingText: "loading results", open: open, onOpen: () => setOpen(true), onClose: () => {
|
|
55
|
+
setOpen(false);
|
|
56
|
+
setLoaded(true);
|
|
57
|
+
if (hasDisplayedRegions) {
|
|
58
|
+
setCurrentSearch('');
|
|
59
|
+
setSearchOptions(undefined);
|
|
60
|
+
}
|
|
61
|
+
}, onChange: (_event, selectedOption) => {
|
|
62
|
+
if (!selectedOption || !assemblyName) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (typeof selectedOption === 'string') {
|
|
66
|
+
// handles string inputs on keyPress enter
|
|
67
|
+
onSelect === null || onSelect === void 0 ? void 0 : onSelect(new BaseResult({ label: selectedOption }));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
onSelect === null || onSelect === void 0 ? void 0 : onSelect(selectedOption.result);
|
|
71
|
+
}
|
|
72
|
+
setInputValue(inputBoxVal);
|
|
73
|
+
}, options: (searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.length) ? searchOptions : regionOptions, getOptionDisabled: option => option.group === 'limitOption', filterOptions: (opts, { inputValue }) => getFiltered(opts, inputValue), renderInput: params => (React.createElement(AutocompleteTextField, { showHelp: showHelp, params: params, inputBoxVal: inputBoxVal, TextFieldProps: TextFieldProps, setCurrentSearch: setCurrentSearch, setInputValue: setInputValue })), getOptionLabel: opt => typeof opt === 'string' ? opt : opt.result.getDisplayString() }));
|
|
81
74
|
});
|
|
82
75
|
export default RefNameAutocomplete;
|
|
@@ -3,6 +3,6 @@ import { LinearGenomeViewModel } from '..';
|
|
|
3
3
|
type LGV = LinearGenomeViewModel;
|
|
4
4
|
declare const Rubberband: ({ model, ControlComponent, }: {
|
|
5
5
|
model: LGV;
|
|
6
|
-
ControlComponent?: React.ReactElement
|
|
6
|
+
ControlComponent?: React.ReactElement;
|
|
7
7
|
}) => React.JSX.Element;
|
|
8
8
|
export default Rubberband;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { LinearGenomeViewModel } from '..';
|
|
3
3
|
declare const SearchBox: ({ model, showHelp, }: {
|
|
4
|
-
showHelp?: boolean
|
|
4
|
+
showHelp?: boolean;
|
|
5
5
|
model: LinearGenomeViewModel;
|
|
6
6
|
}) => React.JSX.Element;
|
|
7
7
|
export default SearchBox;
|
|
@@ -69,7 +69,7 @@ export default function SearchResultsTable({ searchResults, assemblyName: optAss
|
|
|
69
69
|
}
|
|
70
70
|
catch (e) {
|
|
71
71
|
console.error(e);
|
|
72
|
-
session.
|
|
72
|
+
session.notifyError(`${e}`, e);
|
|
73
73
|
}
|
|
74
74
|
handleClose();
|
|
75
75
|
}, color: "primary", variant: "contained" }, "Go")))))))));
|
|
@@ -5,7 +5,6 @@ import { observer } from 'mobx-react';
|
|
|
5
5
|
import { isAlive } from 'mobx-state-tree';
|
|
6
6
|
import { ErrorBoundary } from 'react-error-boundary';
|
|
7
7
|
import { ResizeHandle, ErrorMessage } from '@jbrowse/core/ui';
|
|
8
|
-
import { useDebouncedCallback } from '@jbrowse/core/util';
|
|
9
8
|
import TrackLabelContainer from './TrackLabelContainer';
|
|
10
9
|
import TrackRenderingContainer from './TrackRenderingContainer';
|
|
11
10
|
const useStyles = makeStyles()({
|
|
@@ -18,40 +17,28 @@ const useStyles = makeStyles()({
|
|
|
18
17
|
position: 'relative',
|
|
19
18
|
zIndex: 2,
|
|
20
19
|
},
|
|
21
|
-
overlay: {
|
|
22
|
-
pointerEvents: 'none',
|
|
23
|
-
position: 'absolute',
|
|
24
|
-
top: 0,
|
|
25
|
-
left: 0,
|
|
26
|
-
width: '100%',
|
|
27
|
-
zIndex: 3,
|
|
28
|
-
},
|
|
29
20
|
});
|
|
30
21
|
const TrackContainer = observer(function ({ model, track, }) {
|
|
31
22
|
const { classes } = useStyles();
|
|
32
23
|
const display = track.displays[0];
|
|
33
24
|
const { draggingTrackId } = model;
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const debouncedOnDragEnter = useDebouncedCallback(() => {
|
|
37
|
-
if (isAlive(display) && dimmed) {
|
|
38
|
-
model.moveTrack(draggingTrackId, track.id);
|
|
39
|
-
}
|
|
40
|
-
}, 100);
|
|
41
|
-
return (React.createElement(Paper, { ref: ref2, className: classes.root, variant: "outlined", onClick: event => {
|
|
25
|
+
const ref = useRef(null);
|
|
26
|
+
return (React.createElement(Paper, { ref: ref, className: classes.root, variant: "outlined", onClick: event => {
|
|
42
27
|
var _a;
|
|
43
28
|
if (event.detail === 2 && !track.displays[0].featureIdUnderMouse) {
|
|
44
|
-
const left = ((_a =
|
|
29
|
+
const left = ((_a = ref.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().left) || 0;
|
|
45
30
|
model.zoomTo(model.bpPerPx / 2, event.clientX - left, true);
|
|
46
31
|
}
|
|
47
32
|
} },
|
|
48
33
|
React.createElement(TrackLabelContainer, { track: track, view: model }),
|
|
49
34
|
React.createElement(ErrorBoundary, { FallbackComponent: e => React.createElement(ErrorMessage, { error: e.error }) },
|
|
50
|
-
React.createElement(TrackRenderingContainer, { model: model, track: track, onDragEnter:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
35
|
+
React.createElement(TrackRenderingContainer, { model: model, track: track, onDragEnter: () => {
|
|
36
|
+
if (isAlive(display) &&
|
|
37
|
+
draggingTrackId !== undefined &&
|
|
38
|
+
draggingTrackId !== display.id) {
|
|
39
|
+
model.moveTrack(draggingTrackId, track.id);
|
|
40
|
+
}
|
|
41
|
+
} })),
|
|
55
42
|
React.createElement(ResizeHandle, { onDrag: display.resizeHeight, className: classes.resizeHandle })));
|
|
56
43
|
});
|
|
57
44
|
export default TrackContainer;
|
|
@@ -12,6 +12,10 @@ import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|
|
12
12
|
import CloseIcon from '@mui/icons-material/Close';
|
|
13
13
|
import MinimizeIcon from '@mui/icons-material/Minimize';
|
|
14
14
|
import AddIcon from '@mui/icons-material/Add';
|
|
15
|
+
import KeyboardDoubleArrowDownIcon from '@mui/icons-material/KeyboardDoubleArrowDown';
|
|
16
|
+
import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp';
|
|
17
|
+
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
|
18
|
+
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
|
15
19
|
import TrackLabelDragHandle from './TrackLabelDragHandle';
|
|
16
20
|
const useStyles = makeStyles()(theme => ({
|
|
17
21
|
root: {
|
|
@@ -19,9 +23,6 @@ const useStyles = makeStyles()(theme => ({
|
|
|
19
23
|
'&:hover': {
|
|
20
24
|
background: theme.palette.background.paper,
|
|
21
25
|
},
|
|
22
|
-
transition: theme.transitions.create(['background'], {
|
|
23
|
-
duration: theme.transitions.duration.shortest,
|
|
24
|
-
}),
|
|
25
26
|
},
|
|
26
27
|
trackName: {
|
|
27
28
|
margin: '0 auto',
|
|
@@ -44,9 +45,35 @@ const TrackLabel = observer(React.forwardRef(function TrackLabel2({ track, class
|
|
|
44
45
|
const trackName = getTrackName(trackConf, session);
|
|
45
46
|
const items = [
|
|
46
47
|
{
|
|
47
|
-
label:
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
label: 'Track order',
|
|
49
|
+
type: 'subMenu',
|
|
50
|
+
subMenu: [
|
|
51
|
+
{
|
|
52
|
+
label: minimized ? 'Restore track' : 'Minimize track',
|
|
53
|
+
icon: minimized ? AddIcon : MinimizeIcon,
|
|
54
|
+
onClick: () => track.setMinimized(!minimized),
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
label: 'Move track to top',
|
|
58
|
+
icon: KeyboardDoubleArrowUpIcon,
|
|
59
|
+
onClick: () => view.moveTrackToTop(track.id),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
label: 'Move track up',
|
|
63
|
+
icon: KeyboardArrowUpIcon,
|
|
64
|
+
onClick: () => view.moveTrackUp(track.id),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
label: 'Move track down',
|
|
68
|
+
icon: KeyboardArrowDownIcon,
|
|
69
|
+
onClick: () => view.moveTrackDown(track.id),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
label: 'Move track to bottom',
|
|
73
|
+
icon: KeyboardDoubleArrowDownIcon,
|
|
74
|
+
onClick: () => view.moveTrackToBottom(track.id),
|
|
75
|
+
},
|
|
76
|
+
],
|
|
50
77
|
},
|
|
51
78
|
...(((_a = session.getTrackActionMenuItems) === null || _a === void 0 ? void 0 : _a.call(session, trackConf)) || []),
|
|
52
79
|
...track.trackMenuItems(),
|
|
@@ -13,7 +13,7 @@ import Gridlines from './Gridlines';
|
|
|
13
13
|
import CenterLine from './CenterLine';
|
|
14
14
|
import VerticalGuide from './VerticalGuide';
|
|
15
15
|
import RubberbandSpan from './RubberbandSpan';
|
|
16
|
-
import
|
|
16
|
+
import HighlightGroup from './Highlight';
|
|
17
17
|
const useStyles = makeStyles()({
|
|
18
18
|
tracksContainer: {
|
|
19
19
|
position: 'relative',
|
|
@@ -40,7 +40,7 @@ const TracksContainer = observer(function TracksContainer({ children, model, })
|
|
|
40
40
|
top: anchorPosition.clientY,
|
|
41
41
|
}, onMenuItemClick: handleMenuItemClick, open: open, onClose: handleClose, menuItems: model.rubberBandMenuItems() })) : null,
|
|
42
42
|
React.createElement(Rubberband, { model: model, ControlComponent: React.createElement(Scalebar, { model: model, style: { height: SCALE_BAR_HEIGHT, boxSizing: 'border-box' } }) }),
|
|
43
|
-
React.createElement(
|
|
43
|
+
React.createElement(HighlightGroup, { model: model }),
|
|
44
44
|
additional,
|
|
45
45
|
children));
|
|
46
46
|
});
|
|
@@ -17,16 +17,16 @@ const useStyles = makeStyles()(theme => ({
|
|
|
17
17
|
}));
|
|
18
18
|
const ZoomControls = observer(function ({ model, }) {
|
|
19
19
|
const { classes } = useStyles();
|
|
20
|
-
const { maxBpPerPx, minBpPerPx, bpPerPx
|
|
20
|
+
const { maxBpPerPx, minBpPerPx, bpPerPx } = model;
|
|
21
21
|
const [value, setValue] = useState(-Math.log2(bpPerPx) * 100);
|
|
22
22
|
useEffect(() => {
|
|
23
23
|
setValue(-Math.log2(bpPerPx) * 100);
|
|
24
24
|
}, [setValue, bpPerPx]);
|
|
25
25
|
return (React.createElement("div", { className: classes.container },
|
|
26
|
-
React.createElement(IconButton, { "data-testid": "zoom_out", onClick: () => model.zoom(bpPerPx * 2), disabled: bpPerPx >= maxBpPerPx - 0.0001
|
|
26
|
+
React.createElement(IconButton, { "data-testid": "zoom_out", onClick: () => model.zoom(bpPerPx * 2), disabled: bpPerPx >= maxBpPerPx - 0.0001, size: "large" },
|
|
27
27
|
React.createElement(ZoomOut, null)),
|
|
28
|
-
React.createElement(Slider, { size: "small", className: classes.slider, value: value, min: -Math.log2(maxBpPerPx) * 100, max: -Math.log2(minBpPerPx) * 100, onChange: (_, val) => setValue(val), onChangeCommitted: () => model.zoomTo(2 ** (-value / 100))
|
|
29
|
-
React.createElement(IconButton, { "data-testid": "zoom_in", onClick: () => model.zoom(model.bpPerPx / 2), disabled: bpPerPx <= minBpPerPx + 0.0001
|
|
28
|
+
React.createElement(Slider, { size: "small", className: classes.slider, value: value, min: -Math.log2(maxBpPerPx) * 100, max: -Math.log2(minBpPerPx) * 100, onChange: (_, val) => setValue(val), onChangeCommitted: () => model.zoomTo(2 ** (-value / 100)) }),
|
|
29
|
+
React.createElement(IconButton, { "data-testid": "zoom_in", onClick: () => model.zoom(model.bpPerPx / 2), disabled: bpPerPx <= minBpPerPx + 0.0001, size: "large" },
|
|
30
30
|
React.createElement(ZoomIn, null))));
|
|
31
31
|
});
|
|
32
32
|
export default ZoomControls;
|