@jbrowse/plugin-linear-comparative-view 3.5.0 → 3.6.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/LGVSyntenyDisplay/model.d.ts +4 -7
- package/dist/LinearComparativeDisplay/stateModelFactory.d.ts +4 -0
- package/dist/LinearComparativeDisplay/stateModelFactory.js +14 -7
- package/dist/LinearComparativeView/components/HeaderSearchBoxes.js +2 -2
- package/dist/LinearComparativeView/components/Rubberband.d.ts +2 -2
- package/dist/LinearComparativeView/components/Rubberband.js +16 -154
- package/dist/LinearComparativeView/components/RubberbandSpan.d.ts +15 -0
- package/dist/LinearComparativeView/components/RubberbandSpan.js +38 -0
- package/dist/LinearComparativeView/components/RubberbandTooltip.d.ts +5 -0
- package/dist/LinearComparativeView/components/RubberbandTooltip.js +20 -0
- package/dist/LinearComparativeView/components/useRangeSelect.d.ts +59 -0
- package/dist/LinearComparativeView/components/useRangeSelect.js +128 -0
- package/dist/LinearComparativeView/components/util.d.ts +4 -0
- package/dist/LinearComparativeView/components/util.js +6 -0
- package/dist/LinearComparativeView/model.d.ts +2 -0
- package/dist/LinearSyntenyDisplay/components/BlockError.d.ts +3 -0
- package/dist/LinearSyntenyDisplay/components/BlockError.js +16 -0
- package/dist/LinearSyntenyDisplay/components/BlockMessage.d.ts +3 -0
- package/dist/LinearSyntenyDisplay/components/BlockMessage.js +24 -0
- package/dist/LinearSyntenyDisplay/components/Component.js +11 -50
- package/dist/LinearSyntenyDisplay/components/LoadingMessage.d.ts +3 -0
- package/dist/LinearSyntenyDisplay/components/LoadingMessage.js +21 -0
- package/dist/LinearSyntenyDisplay/model.d.ts +4 -0
- package/dist/LinearSyntenyView/components/ImportForm/ImportSyntenyOpenCustomTrack.js +2 -51
- package/dist/LinearSyntenyView/components/ImportForm/selectors/AnchorsSelector.d.ts +3 -0
- package/dist/LinearSyntenyView/components/ImportForm/selectors/AnchorsSelector.js +13 -0
- package/dist/LinearSyntenyView/components/ImportForm/selectors/PifGzSelector.d.ts +3 -0
- package/dist/LinearSyntenyView/components/ImportForm/selectors/PifGzSelector.js +13 -0
- package/dist/LinearSyntenyView/components/ImportForm/selectors/SelectorTypes.d.ts +19 -0
- package/dist/LinearSyntenyView/components/ImportForm/selectors/SelectorTypes.js +10 -0
- package/dist/LinearSyntenyView/components/ImportForm/selectors/StandardFormatSelector.d.ts +3 -0
- package/dist/LinearSyntenyView/components/ImportForm/selectors/StandardFormatSelector.js +20 -0
- package/dist/LinearSyntenyView/components/ImportForm/selectors/SwapAssemblies.d.ts +13 -0
- package/dist/LinearSyntenyView/components/ImportForm/selectors/SwapAssemblies.js +32 -0
- package/dist/LinearSyntenyView/components/ImportForm/selectors/index.d.ts +4 -0
- package/dist/LinearSyntenyView/components/ImportForm/selectors/index.js +27 -0
- package/dist/LinearSyntenyView/model.d.ts +2 -0
- package/dist/LinearSyntenyView/svgcomponents/SVGLinearSyntenyView.js +1 -1
- package/esm/LGVSyntenyDisplay/model.d.ts +4 -7
- package/esm/LinearComparativeDisplay/stateModelFactory.d.ts +4 -0
- package/esm/LinearComparativeDisplay/stateModelFactory.js +14 -7
- package/esm/LinearComparativeView/components/HeaderSearchBoxes.js +2 -2
- package/esm/LinearComparativeView/components/Rubberband.d.ts +2 -2
- package/esm/LinearComparativeView/components/Rubberband.js +17 -155
- package/esm/LinearComparativeView/components/RubberbandSpan.d.ts +15 -0
- package/esm/LinearComparativeView/components/RubberbandSpan.js +32 -0
- package/esm/LinearComparativeView/components/RubberbandTooltip.d.ts +5 -0
- package/esm/LinearComparativeView/components/RubberbandTooltip.js +17 -0
- package/esm/LinearComparativeView/components/useRangeSelect.d.ts +59 -0
- package/esm/LinearComparativeView/components/useRangeSelect.js +125 -0
- package/esm/LinearComparativeView/components/util.d.ts +4 -0
- package/esm/LinearComparativeView/components/util.js +3 -0
- package/esm/LinearComparativeView/model.d.ts +2 -0
- package/esm/LinearSyntenyDisplay/components/BlockError.d.ts +3 -0
- package/esm/LinearSyntenyDisplay/components/BlockError.js +13 -0
- package/esm/LinearSyntenyDisplay/components/BlockMessage.d.ts +3 -0
- package/esm/LinearSyntenyDisplay/components/BlockMessage.js +21 -0
- package/esm/LinearSyntenyDisplay/components/Component.js +9 -48
- package/esm/LinearSyntenyDisplay/components/LoadingMessage.d.ts +3 -0
- package/esm/LinearSyntenyDisplay/components/LoadingMessage.js +18 -0
- package/esm/LinearSyntenyDisplay/model.d.ts +4 -0
- package/esm/LinearSyntenyView/components/ImportForm/ImportSyntenyOpenCustomTrack.js +3 -49
- package/esm/LinearSyntenyView/components/ImportForm/selectors/AnchorsSelector.d.ts +3 -0
- package/esm/LinearSyntenyView/components/ImportForm/selectors/AnchorsSelector.js +8 -0
- package/esm/LinearSyntenyView/components/ImportForm/selectors/PifGzSelector.d.ts +3 -0
- package/esm/LinearSyntenyView/components/ImportForm/selectors/PifGzSelector.js +8 -0
- package/esm/LinearSyntenyView/components/ImportForm/selectors/SelectorTypes.d.ts +19 -0
- package/esm/LinearSyntenyView/components/ImportForm/selectors/SelectorTypes.js +7 -0
- package/esm/LinearSyntenyView/components/ImportForm/selectors/StandardFormatSelector.d.ts +3 -0
- package/esm/LinearSyntenyView/components/ImportForm/selectors/StandardFormatSelector.js +15 -0
- package/esm/LinearSyntenyView/components/ImportForm/selectors/SwapAssemblies.d.ts +13 -0
- package/esm/LinearSyntenyView/components/ImportForm/selectors/SwapAssemblies.js +27 -0
- package/esm/LinearSyntenyView/components/ImportForm/selectors/index.d.ts +4 -0
- package/esm/LinearSyntenyView/components/ImportForm/selectors/index.js +4 -0
- package/esm/LinearSyntenyView/model.d.ts +2 -0
- package/esm/LinearSyntenyView/svgcomponents/SVGLinearSyntenyView.js +1 -1
- package/package.json +5 -5
|
@@ -1,165 +1,27 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useRef } from 'react';
|
|
3
3
|
import { Menu } from '@jbrowse/core/ui';
|
|
4
|
-
import { stringify } from '@jbrowse/core/util';
|
|
5
|
-
import { Popover, Typography, alpha } from '@mui/material';
|
|
6
|
-
import { transaction } from 'mobx';
|
|
7
4
|
import { observer } from 'mobx-react';
|
|
8
5
|
import { makeStyles } from 'tss-react/mui';
|
|
6
|
+
import RubberbandSpan from './RubberbandSpan';
|
|
9
7
|
import VerticalGuide from './VerticalGuide';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
rubberbandControl: {
|
|
21
|
-
cursor: 'crosshair',
|
|
22
|
-
width: '100%',
|
|
23
|
-
minHeight: 8,
|
|
24
|
-
},
|
|
25
|
-
rubberbandText: {
|
|
26
|
-
color: theme.palette.tertiary.contrastText,
|
|
27
|
-
},
|
|
28
|
-
popover: {
|
|
29
|
-
mouseEvents: 'none',
|
|
30
|
-
cursor: 'crosshair',
|
|
31
|
-
},
|
|
32
|
-
paper: {
|
|
33
|
-
paddingLeft: theme.spacing(1),
|
|
34
|
-
paddingRight: theme.spacing(1),
|
|
35
|
-
},
|
|
36
|
-
};
|
|
8
|
+
import { useRangeSelect } from './useRangeSelect';
|
|
9
|
+
const useStyles = makeStyles()({
|
|
10
|
+
rubberbandControl: {
|
|
11
|
+
cursor: 'crosshair',
|
|
12
|
+
width: '100%',
|
|
13
|
+
minHeight: 8,
|
|
14
|
+
position: 'relative',
|
|
15
|
+
zIndex: 900,
|
|
16
|
+
},
|
|
37
17
|
});
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const [currentX, setCurrentX] = useState();
|
|
41
|
-
const [anchorPosition, setAnchorPosition] = useState();
|
|
42
|
-
const [guideX, setGuideX] = useState();
|
|
43
|
-
const controlsRef = useRef(null);
|
|
44
|
-
const rubberbandRef = useRef(null);
|
|
18
|
+
const Rubberband = observer(function ({ model, ControlComponent = _jsx("div", {}), }) {
|
|
19
|
+
const ref = useRef(null);
|
|
45
20
|
const { classes } = useStyles();
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
function computeOffsets(offsetX, view) {
|
|
49
|
-
if (startX === undefined) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
let leftPx = startX;
|
|
53
|
-
let rightPx = offsetX;
|
|
54
|
-
if (rightPx < leftPx) {
|
|
55
|
-
;
|
|
56
|
-
[leftPx, rightPx] = [rightPx, leftPx];
|
|
57
|
-
}
|
|
58
|
-
const leftOffset = view.pxToBp(leftPx);
|
|
59
|
-
const rightOffset = view.pxToBp(rightPx);
|
|
60
|
-
return { leftOffset, rightOffset };
|
|
61
|
-
}
|
|
62
|
-
function globalMouseMove(event) {
|
|
63
|
-
if (controlsRef.current && mouseDragging) {
|
|
64
|
-
const relativeX = event.clientX - controlsRef.current.getBoundingClientRect().left;
|
|
65
|
-
setCurrentX(relativeX);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
function globalMouseUp(event) {
|
|
69
|
-
if (startX !== undefined && controlsRef.current) {
|
|
70
|
-
const { clientX, clientY } = event;
|
|
71
|
-
const ref = controlsRef.current;
|
|
72
|
-
const offsetX = clientX - ref.getBoundingClientRect().left;
|
|
73
|
-
setAnchorPosition({
|
|
74
|
-
offsetX,
|
|
75
|
-
clientX,
|
|
76
|
-
clientY,
|
|
77
|
-
});
|
|
78
|
-
transaction(() => {
|
|
79
|
-
for (const view of model.views) {
|
|
80
|
-
const args = computeOffsets(offsetX, view);
|
|
81
|
-
if (args) {
|
|
82
|
-
const { leftOffset, rightOffset } = args;
|
|
83
|
-
view.setOffsets(leftOffset, rightOffset);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
setGuideX(undefined);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if (mouseDragging) {
|
|
91
|
-
window.addEventListener('mousemove', globalMouseMove);
|
|
92
|
-
window.addEventListener('mouseup', globalMouseUp);
|
|
93
|
-
return () => {
|
|
94
|
-
window.removeEventListener('mousemove', globalMouseMove);
|
|
95
|
-
window.removeEventListener('mouseup', globalMouseUp);
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
return () => { };
|
|
99
|
-
}, [startX, mouseDragging, model]);
|
|
100
|
-
useEffect(() => {
|
|
101
|
-
if (!mouseDragging &&
|
|
102
|
-
currentX !== undefined &&
|
|
103
|
-
startX !== undefined &&
|
|
104
|
-
Math.abs(currentX - startX) <= 3) {
|
|
105
|
-
handleClose();
|
|
106
|
-
}
|
|
107
|
-
}, [mouseDragging, currentX, startX]);
|
|
108
|
-
function mouseDown(event) {
|
|
109
|
-
event.preventDefault();
|
|
110
|
-
event.stopPropagation();
|
|
111
|
-
const relativeX = event.clientX -
|
|
112
|
-
event.target.getBoundingClientRect().left;
|
|
113
|
-
setStartX(relativeX);
|
|
114
|
-
setCurrentX(relativeX);
|
|
115
|
-
}
|
|
116
|
-
function mouseMove(event) {
|
|
117
|
-
const target = event.target;
|
|
118
|
-
setGuideX(event.clientX - target.getBoundingClientRect().left);
|
|
119
|
-
}
|
|
120
|
-
function mouseOut() {
|
|
121
|
-
setGuideX(undefined);
|
|
122
|
-
transaction(() => {
|
|
123
|
-
for (const view of model.views) {
|
|
124
|
-
view.setOffsets(undefined, undefined);
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
function handleClose() {
|
|
129
|
-
setAnchorPosition(undefined);
|
|
130
|
-
setStartX(undefined);
|
|
131
|
-
setCurrentX(undefined);
|
|
132
|
-
}
|
|
133
|
-
const open = Boolean(anchorPosition);
|
|
134
|
-
function handleMenuItemClick(_, callback) {
|
|
135
|
-
callback();
|
|
136
|
-
handleClose();
|
|
137
|
-
}
|
|
138
|
-
if (startX === undefined) {
|
|
139
|
-
return (_jsxs(_Fragment, { children: [guideX !== undefined ? (_jsx(VerticalGuide, { model: model, coordX: guideX })) : null, _jsx("div", { ref: controlsRef, className: classes.rubberbandControl, onMouseDown: mouseDown, onMouseOut: mouseOut, onMouseMove: mouseMove, children: ControlComponent })] }));
|
|
140
|
-
}
|
|
141
|
-
const right = anchorPosition ? anchorPosition.offsetX : currentX || 0;
|
|
142
|
-
const left = Math.min(right, startX);
|
|
143
|
-
const width = Math.abs(right - startX);
|
|
144
|
-
const { views } = model;
|
|
145
|
-
const leftBpOffset = views.map(view => view.pxToBp(left));
|
|
146
|
-
const rightBpOffset = views.map(view => view.pxToBp(left + width));
|
|
147
|
-
const numOfBpSelected = views.map(view => Math.ceil(width * view.bpPerPx));
|
|
148
|
-
return (_jsxs(_Fragment, { children: [rubberbandRef.current ? (_jsxs(_Fragment, { children: [_jsx(Popover, { className: classes.popover, classes: { paper: classes.paper }, open: true, anchorEl: rubberbandRef.current, anchorOrigin: {
|
|
149
|
-
vertical: 'top',
|
|
150
|
-
horizontal: 'left',
|
|
151
|
-
}, transformOrigin: {
|
|
152
|
-
vertical: 'bottom',
|
|
153
|
-
horizontal: 'right',
|
|
154
|
-
}, keepMounted: true, disableRestoreFocus: true, children: leftBpOffset.map((l, idx) => (_jsx(Typography, { children: stringify(l, true) }, [JSON.stringify(l), idx, 'left'].join('-')))) }), _jsx(Popover, { className: classes.popover, classes: { paper: classes.paper }, open: true, anchorEl: rubberbandRef.current, anchorOrigin: {
|
|
155
|
-
vertical: 'top',
|
|
156
|
-
horizontal: 'right',
|
|
157
|
-
}, transformOrigin: {
|
|
158
|
-
vertical: 'bottom',
|
|
159
|
-
horizontal: 'left',
|
|
160
|
-
}, keepMounted: true, disableRestoreFocus: true, children: rightBpOffset.map((l, idx) => (_jsx(Typography, { children: stringify(l, true) }, [JSON.stringify(l), idx, 'right'].join('-')))) })] })) : null, _jsx("div", { ref: rubberbandRef, className: classes.rubberband, style: { left, width }, children: _jsx(Typography, { variant: "h6", className: classes.rubberbandText, children: numOfBpSelected.map((n, i) => (_jsx(Typography, { children: `${n.toLocaleString('en-US')}bp` }, `${n}_${i}`))) }) }), _jsx("div", { className: classes.rubberbandControl, ref: controlsRef, onMouseDown: mouseDown, onMouseOut: mouseOut, onMouseMove: mouseMove, children: ControlComponent }), anchorPosition ? (_jsx(Menu, { anchorReference: "anchorPosition", anchorPosition: {
|
|
21
|
+
const { guideX, rubberbandOn, leftBpOffset, rightBpOffset, numOfBpSelected, width, left, anchorPosition, open, handleMenuItemClick, handleClose, mouseMove, mouseDown, mouseOut, } = useRangeSelect(ref, model);
|
|
22
|
+
return (_jsxs(_Fragment, { children: [guideX !== undefined ? (_jsx(VerticalGuide, { model: model, coordX: guideX })) : rubberbandOn ? (_jsx(RubberbandSpan, { leftBpOffset: leftBpOffset, rightBpOffset: rightBpOffset, numOfBpSelected: numOfBpSelected, width: width, left: left })) : null, anchorPosition ? (_jsx(Menu, { anchorReference: "anchorPosition", anchorPosition: {
|
|
161
23
|
left: anchorPosition.clientX,
|
|
162
24
|
top: anchorPosition.clientY,
|
|
163
|
-
}, onMenuItemClick: handleMenuItemClick, open: open, onClose: handleClose, menuItems: model.rubberBandMenuItems() })) : null] }));
|
|
25
|
+
}, onMenuItemClick: handleMenuItemClick, open: open, onClose: handleClose, menuItems: model.rubberBandMenuItems() })) : null, _jsx("div", { "data-testid": "rubberband_controls", className: classes.rubberbandControl, ref: ref, onMouseDown: mouseDown, onMouseMove: mouseMove, onMouseOut: mouseOut, children: ControlComponent })] }));
|
|
164
26
|
});
|
|
165
|
-
export default
|
|
27
|
+
export default Rubberband;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface Offset {
|
|
2
|
+
coord: number;
|
|
3
|
+
refName?: string;
|
|
4
|
+
oob?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export default function RubberbandSpan({ leftBpOffset, rightBpOffset, numOfBpSelected, left, width, top, sticky, }: {
|
|
7
|
+
leftBpOffset: Offset[];
|
|
8
|
+
rightBpOffset: Offset[];
|
|
9
|
+
numOfBpSelected?: number[];
|
|
10
|
+
left: number;
|
|
11
|
+
width: number;
|
|
12
|
+
top?: number;
|
|
13
|
+
sticky?: boolean;
|
|
14
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { getBpDisplayStr, stringify } from '@jbrowse/core/util';
|
|
4
|
+
import { Typography, alpha } from '@mui/material';
|
|
5
|
+
import { makeStyles } from 'tss-react/mui';
|
|
6
|
+
import RubberbandTooltip from './RubberbandTooltip';
|
|
7
|
+
const useStyles = makeStyles()(theme => {
|
|
8
|
+
const { tertiary } = theme.palette;
|
|
9
|
+
const background = alpha(tertiary.light, 0.7);
|
|
10
|
+
return {
|
|
11
|
+
rubberband: {
|
|
12
|
+
height: '100%',
|
|
13
|
+
background,
|
|
14
|
+
position: 'absolute',
|
|
15
|
+
zIndex: 830,
|
|
16
|
+
textAlign: 'center',
|
|
17
|
+
},
|
|
18
|
+
rubberbandText: {
|
|
19
|
+
color: theme.palette.tertiary.contrastText,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
export default function RubberbandSpan({ leftBpOffset, rightBpOffset, numOfBpSelected, left, width, top = 0, sticky = false, }) {
|
|
24
|
+
const { classes } = useStyles();
|
|
25
|
+
const [anchorEl, setAnchorEl] = useState(null);
|
|
26
|
+
return (_jsxs(_Fragment, { children: [anchorEl ? (_jsxs(_Fragment, { children: [_jsx(RubberbandTooltip, { side: "left", anchorEl: anchorEl, text: leftBpOffset.map((l, idx) => (_jsx("div", { children: stringify(l, true) }, `JSON.stringify(l)-${idx}`))) }), _jsx(RubberbandTooltip, { side: "right", anchorEl: anchorEl, text: rightBpOffset.map((l, idx) => (_jsx("div", { children: stringify(l, true) }, `JSON.stringify(l)-${idx}`))) })] })) : null, _jsx("div", { className: classes.rubberband, style: { left, width }, children: numOfBpSelected ? (_jsx(Typography, { ref: el => {
|
|
27
|
+
setAnchorEl(el);
|
|
28
|
+
}, variant: "h6", className: classes.rubberbandText, style: {
|
|
29
|
+
top,
|
|
30
|
+
position: sticky ? 'sticky' : undefined,
|
|
31
|
+
}, children: numOfBpSelected.map((n, i) => (_jsx("div", { children: getBpDisplayStr(n) }, i))) })) : null })] }));
|
|
32
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Tooltip } from '@mui/material';
|
|
3
|
+
export default function RubberbandTooltip({ anchorEl, side, text, }) {
|
|
4
|
+
return (_jsx(Tooltip, { title: text, open: true, placement: side === 'left' ? 'left-start' : 'right-start', slotProps: {
|
|
5
|
+
popper: {
|
|
6
|
+
anchorEl,
|
|
7
|
+
modifiers: [
|
|
8
|
+
{
|
|
9
|
+
name: 'offset',
|
|
10
|
+
options: {
|
|
11
|
+
offset: [-30, -10],
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
}, children: _jsx("span", {}) }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { LinearComparativeViewModel } from '../model';
|
|
3
|
+
interface AnchorPosition {
|
|
4
|
+
offsetX: number;
|
|
5
|
+
clientX: number;
|
|
6
|
+
clientY: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function useRangeSelect(ref: React.RefObject<HTMLDivElement | null>, model: LinearComparativeViewModel): {
|
|
9
|
+
open: boolean;
|
|
10
|
+
guideX: number | undefined;
|
|
11
|
+
mouseDown: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
12
|
+
mouseMove: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
13
|
+
mouseOut: () => void;
|
|
14
|
+
handleMenuItemClick: (_: unknown, callback: () => void) => void;
|
|
15
|
+
rubberbandOn?: undefined;
|
|
16
|
+
handleClose?: undefined;
|
|
17
|
+
leftBpOffset?: undefined;
|
|
18
|
+
rightBpOffset?: undefined;
|
|
19
|
+
anchorPosition?: undefined;
|
|
20
|
+
numOfBpSelected?: undefined;
|
|
21
|
+
width?: undefined;
|
|
22
|
+
left?: undefined;
|
|
23
|
+
} | {
|
|
24
|
+
open: boolean;
|
|
25
|
+
rubberbandOn: boolean;
|
|
26
|
+
mouseDown: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
27
|
+
mouseMove: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
28
|
+
mouseOut: () => void;
|
|
29
|
+
handleClose: () => void;
|
|
30
|
+
handleMenuItemClick: (_: unknown, callback: () => void) => void;
|
|
31
|
+
leftBpOffset: {
|
|
32
|
+
coord: number;
|
|
33
|
+
index: number;
|
|
34
|
+
refName: string;
|
|
35
|
+
oob: boolean;
|
|
36
|
+
assemblyName: string;
|
|
37
|
+
offset: number;
|
|
38
|
+
start: number;
|
|
39
|
+
end: number;
|
|
40
|
+
reversed?: boolean;
|
|
41
|
+
}[];
|
|
42
|
+
rightBpOffset: {
|
|
43
|
+
coord: number;
|
|
44
|
+
index: number;
|
|
45
|
+
refName: string;
|
|
46
|
+
oob: boolean;
|
|
47
|
+
assemblyName: string;
|
|
48
|
+
offset: number;
|
|
49
|
+
start: number;
|
|
50
|
+
end: number;
|
|
51
|
+
reversed?: boolean;
|
|
52
|
+
}[];
|
|
53
|
+
anchorPosition: AnchorPosition | undefined;
|
|
54
|
+
numOfBpSelected: number[];
|
|
55
|
+
width: number;
|
|
56
|
+
left: number;
|
|
57
|
+
guideX?: undefined;
|
|
58
|
+
};
|
|
59
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { transaction } from 'mobx';
|
|
3
|
+
import { getRelativeX } from './util';
|
|
4
|
+
export function useRangeSelect(ref, model) {
|
|
5
|
+
const [startX, setStartX] = useState();
|
|
6
|
+
const [currentX, setCurrentX] = useState();
|
|
7
|
+
const [anchorPosition, setAnchorPosition] = useState();
|
|
8
|
+
const [guideX, setGuideX] = useState();
|
|
9
|
+
const mouseDragging = startX !== undefined && anchorPosition === undefined;
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
function computeOffsets(offsetX) {
|
|
12
|
+
if (startX === undefined) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const leftPx = Math.min(startX, offsetX);
|
|
16
|
+
const rightPx = Math.max(startX, offsetX);
|
|
17
|
+
return model.views.map(view => ({
|
|
18
|
+
leftOffset: view.pxToBp(leftPx),
|
|
19
|
+
rightOffset: view.pxToBp(rightPx),
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
function globalMouseMove(event) {
|
|
23
|
+
if (ref.current && mouseDragging) {
|
|
24
|
+
const relativeX = getRelativeX(event, ref.current);
|
|
25
|
+
setCurrentX(relativeX);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function globalMouseUp(event) {
|
|
29
|
+
if (startX !== undefined && ref.current) {
|
|
30
|
+
const { clientX, clientY } = event;
|
|
31
|
+
const offsetX = getRelativeX(event, ref.current);
|
|
32
|
+
setAnchorPosition({
|
|
33
|
+
offsetX,
|
|
34
|
+
clientX,
|
|
35
|
+
clientY,
|
|
36
|
+
});
|
|
37
|
+
const offsets = computeOffsets(offsetX);
|
|
38
|
+
if (offsets) {
|
|
39
|
+
transaction(() => {
|
|
40
|
+
for (const [idx, elt] of offsets.entries()) {
|
|
41
|
+
model.views[idx].setOffsets(elt.leftOffset, elt.rightOffset);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
setGuideX(undefined);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (mouseDragging) {
|
|
49
|
+
window.addEventListener('mousemove', globalMouseMove);
|
|
50
|
+
window.addEventListener('mouseup', globalMouseUp);
|
|
51
|
+
return () => {
|
|
52
|
+
window.removeEventListener('mousemove', globalMouseMove);
|
|
53
|
+
window.removeEventListener('mouseup', globalMouseUp);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return () => { };
|
|
57
|
+
}, [startX, mouseDragging, model, ref]);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!mouseDragging &&
|
|
60
|
+
currentX !== undefined &&
|
|
61
|
+
startX !== undefined &&
|
|
62
|
+
Math.abs(currentX - startX) <= 3) {
|
|
63
|
+
handleClose();
|
|
64
|
+
}
|
|
65
|
+
}, [mouseDragging, currentX, startX]);
|
|
66
|
+
function mouseDown(event) {
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
event.stopPropagation();
|
|
69
|
+
const relativeX = getRelativeX(event, ref.current);
|
|
70
|
+
setStartX(relativeX);
|
|
71
|
+
setCurrentX(relativeX);
|
|
72
|
+
}
|
|
73
|
+
function mouseMove(event) {
|
|
74
|
+
setGuideX(getRelativeX(event, ref.current));
|
|
75
|
+
}
|
|
76
|
+
function mouseOut() {
|
|
77
|
+
setGuideX(undefined);
|
|
78
|
+
transaction(() => {
|
|
79
|
+
for (const view of model.views) {
|
|
80
|
+
view.setOffsets(undefined, undefined);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function handleClose() {
|
|
85
|
+
setAnchorPosition(undefined);
|
|
86
|
+
setStartX(undefined);
|
|
87
|
+
setCurrentX(undefined);
|
|
88
|
+
}
|
|
89
|
+
function handleMenuItemClick(_, callback) {
|
|
90
|
+
callback();
|
|
91
|
+
handleClose();
|
|
92
|
+
}
|
|
93
|
+
const open = Boolean(anchorPosition);
|
|
94
|
+
if (startX === undefined) {
|
|
95
|
+
return {
|
|
96
|
+
open,
|
|
97
|
+
guideX,
|
|
98
|
+
mouseDown,
|
|
99
|
+
mouseMove,
|
|
100
|
+
mouseOut,
|
|
101
|
+
handleMenuItemClick,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const right = anchorPosition ? anchorPosition.offsetX : currentX || 0;
|
|
105
|
+
const left = Math.min(right, startX);
|
|
106
|
+
const width = Math.abs(right - startX);
|
|
107
|
+
const leftBpOffset = model.views.map(view => view.pxToBp(left));
|
|
108
|
+
const rightBpOffset = model.views.map(view => view.pxToBp(left + width));
|
|
109
|
+
const numOfBpSelected = model.views.map(view => Math.ceil(width * view.bpPerPx));
|
|
110
|
+
return {
|
|
111
|
+
open,
|
|
112
|
+
rubberbandOn: true,
|
|
113
|
+
mouseDown,
|
|
114
|
+
mouseMove,
|
|
115
|
+
mouseOut,
|
|
116
|
+
handleClose,
|
|
117
|
+
handleMenuItemClick,
|
|
118
|
+
leftBpOffset,
|
|
119
|
+
rightBpOffset,
|
|
120
|
+
anchorPosition,
|
|
121
|
+
numOfBpSelected,
|
|
122
|
+
width,
|
|
123
|
+
left,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -77,6 +77,7 @@ declare function stateModelFactory(pluginManager: PluginManager): import("mobx-s
|
|
|
77
77
|
readonly width: number;
|
|
78
78
|
readonly interRegionPaddingWidth: number;
|
|
79
79
|
readonly assemblyNames: string[];
|
|
80
|
+
readonly assemblyDisplayNames: string[];
|
|
80
81
|
readonly isTopLevelView: import("@jbrowse/core/util").AbstractViewModel | undefined;
|
|
81
82
|
readonly stickyViewHeaders: boolean | undefined;
|
|
82
83
|
readonly rubberbandTop: number;
|
|
@@ -393,6 +394,7 @@ declare function stateModelFactory(pluginManager: PluginManager): import("mobx-s
|
|
|
393
394
|
readonly width: number;
|
|
394
395
|
readonly interRegionPaddingWidth: number;
|
|
395
396
|
readonly assemblyNames: string[];
|
|
397
|
+
readonly assemblyDisplayNames: string[];
|
|
396
398
|
readonly isTopLevelView: import("@jbrowse/core/util").AbstractViewModel | undefined;
|
|
397
399
|
readonly stickyViewHeaders: boolean | undefined;
|
|
398
400
|
readonly rubberbandTop: number;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { makeStyles } from 'tss-react/mui';
|
|
3
|
+
const useStyles = makeStyles()({
|
|
4
|
+
blockError: {
|
|
5
|
+
background: '#f1f1f1',
|
|
6
|
+
padding: 10,
|
|
7
|
+
color: 'red',
|
|
8
|
+
},
|
|
9
|
+
});
|
|
10
|
+
export default function BlockError({ error }) {
|
|
11
|
+
const { classes } = useStyles();
|
|
12
|
+
return _jsx("div", { className: classes.blockError, children: `${error}` });
|
|
13
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { makeStyles } from 'tss-react/mui';
|
|
3
|
+
const useStyles = makeStyles()(theme => {
|
|
4
|
+
const bg = theme.palette.action.disabledBackground;
|
|
5
|
+
return {
|
|
6
|
+
loading: {
|
|
7
|
+
paddingLeft: '0.6em',
|
|
8
|
+
backgroundColor: theme.palette.background.default,
|
|
9
|
+
backgroundImage: `repeating-linear-gradient(45deg, transparent, transparent 5px, ${bg} 5px, ${bg} 10px)`,
|
|
10
|
+
textAlign: 'center',
|
|
11
|
+
},
|
|
12
|
+
blockMessage: {
|
|
13
|
+
background: '#f1f1f1',
|
|
14
|
+
padding: 10,
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
export default function BlockMessage({ messageText }) {
|
|
19
|
+
const { classes } = useStyles();
|
|
20
|
+
return _jsx("div", { className: classes.blockMessage, children: messageText });
|
|
21
|
+
}
|
|
@@ -1,60 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
3
|
-
import { LoadingEllipses } from '@jbrowse/core/ui';
|
|
4
2
|
import { observer } from 'mobx-react';
|
|
5
|
-
import
|
|
3
|
+
import BlockError from './BlockError';
|
|
4
|
+
import BlockMessage from './BlockMessage';
|
|
6
5
|
import LinearSyntenyRendering from './LinearSyntenyRendering';
|
|
7
|
-
|
|
8
|
-
const bg = theme.palette.action.disabledBackground;
|
|
9
|
-
return {
|
|
10
|
-
loading: {
|
|
11
|
-
paddingLeft: '0.6em',
|
|
12
|
-
backgroundColor: theme.palette.background.default,
|
|
13
|
-
backgroundImage: `repeating-linear-gradient(45deg, transparent, transparent 5px, ${bg} 5px, ${bg} 10px)`,
|
|
14
|
-
textAlign: 'center',
|
|
15
|
-
},
|
|
16
|
-
blockMessage: {
|
|
17
|
-
background: '#f1f1f1',
|
|
18
|
-
padding: 10,
|
|
19
|
-
},
|
|
20
|
-
blockError: {
|
|
21
|
-
background: '#f1f1f1',
|
|
22
|
-
padding: 10,
|
|
23
|
-
color: 'red',
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
});
|
|
27
|
-
function LoadingMessage() {
|
|
28
|
-
const [shown, setShown] = useState(false);
|
|
29
|
-
const { classes } = useStyles();
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
const timeout = setTimeout(() => {
|
|
32
|
-
setShown(true);
|
|
33
|
-
}, 300);
|
|
34
|
-
return () => {
|
|
35
|
-
clearTimeout(timeout);
|
|
36
|
-
};
|
|
37
|
-
});
|
|
38
|
-
return shown ? (_jsx("div", { className: classes.loading, children: _jsx(LoadingEllipses, {}) })) : null;
|
|
39
|
-
}
|
|
40
|
-
function BlockMessage({ messageText }) {
|
|
41
|
-
const { classes } = useStyles();
|
|
42
|
-
return _jsx("div", { className: classes.blockMessage, children: messageText });
|
|
43
|
-
}
|
|
44
|
-
function BlockError({ error }) {
|
|
45
|
-
const { classes } = useStyles();
|
|
46
|
-
return _jsx("div", { className: classes.blockError, children: `${error}` });
|
|
47
|
-
}
|
|
6
|
+
import LoadingMessage from './LoadingMessage';
|
|
48
7
|
const ServerSideRenderedBlockContent = observer(function ({ model, }) {
|
|
49
8
|
if (model.error) {
|
|
50
9
|
return _jsx(BlockError, { error: model.error });
|
|
51
10
|
}
|
|
52
|
-
if (model.message) {
|
|
11
|
+
else if (model.message) {
|
|
53
12
|
return _jsx(BlockMessage, { messageText: model.message });
|
|
54
13
|
}
|
|
55
|
-
if (!model.features) {
|
|
56
|
-
return _jsx(LoadingMessage, {});
|
|
14
|
+
else if (!model.features) {
|
|
15
|
+
return _jsx(LoadingMessage, { message: model.loadingStatus });
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
return _jsx(LinearSyntenyRendering, { model: model });
|
|
57
19
|
}
|
|
58
|
-
return _jsx(LinearSyntenyRendering, { model: model });
|
|
59
20
|
});
|
|
60
21
|
export default ServerSideRenderedBlockContent;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { LoadingEllipses } from '@jbrowse/core/ui';
|
|
3
|
+
import { makeStyles } from 'tss-react/mui';
|
|
4
|
+
const useStyles = makeStyles()(theme => {
|
|
5
|
+
const bg = theme.palette.action.disabledBackground;
|
|
6
|
+
return {
|
|
7
|
+
loading: {
|
|
8
|
+
paddingLeft: '0.6em',
|
|
9
|
+
backgroundColor: theme.palette.background.default,
|
|
10
|
+
backgroundImage: `repeating-linear-gradient(45deg, transparent, transparent 5px, ${bg} 5px, ${bg} 10px)`,
|
|
11
|
+
textAlign: 'center',
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
export default function LoadingMessage({ message }) {
|
|
16
|
+
const { classes } = useStyles();
|
|
17
|
+
return (_jsx("div", { className: classes.loading, children: _jsx(LoadingEllipses, { message: message }) }));
|
|
18
|
+
}
|
|
@@ -84,6 +84,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
84
84
|
renderInProgress: string | undefined;
|
|
85
85
|
features: Feature[] | undefined;
|
|
86
86
|
message: string | undefined;
|
|
87
|
+
loadingStatus: string | undefined;
|
|
87
88
|
} & {
|
|
88
89
|
readonly level: number;
|
|
89
90
|
readonly height: number;
|
|
@@ -166,6 +167,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
166
167
|
renderInProgress: string | undefined;
|
|
167
168
|
features: Feature[] | undefined;
|
|
168
169
|
message: string | undefined;
|
|
170
|
+
loadingStatus: string | undefined;
|
|
169
171
|
} & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
|
|
170
172
|
id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
171
173
|
type: import("mobx-state-tree").ISimpleType<string>;
|
|
@@ -235,12 +237,14 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
235
237
|
renderInProgress: string | undefined;
|
|
236
238
|
features: Feature[] | undefined;
|
|
237
239
|
message: string | undefined;
|
|
240
|
+
loadingStatus: string | undefined;
|
|
238
241
|
}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
|
|
239
242
|
highResolutionScaling: number;
|
|
240
243
|
};
|
|
241
244
|
} & {
|
|
242
245
|
setLoading(newStopToken: string): void;
|
|
243
246
|
setMessage(messageText: string): void;
|
|
247
|
+
setLoadingStatus(messageText: string): void;
|
|
244
248
|
setRendered(args?: {
|
|
245
249
|
features: Feature[];
|
|
246
250
|
}): void;
|