@jbrowse/plugin-linear-genome-view 3.3.0 → 3.5.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/BaseLinearDisplayModel.js +22 -6
- package/dist/LinearGenomeView/components/Cytobands.js +18 -4
- package/dist/LinearGenomeView/components/GetSequenceDialog.js +3 -2
- package/dist/LinearGenomeView/components/HeaderPanControls.js +0 -3
- package/dist/LinearGenomeView/components/MiniControls.js +1 -0
- package/dist/LinearGenomeView/components/OverviewRubberband.js +0 -6
- package/dist/LinearGenomeView/components/OverviewRubberbandHoverTooltip.js +1 -9
- package/dist/LinearGenomeView/components/RefNameAutocomplete/index.js +16 -10
- package/dist/LinearGenomeView/components/SearchBox.d.ts +1 -1
- package/dist/LinearGenomeView/components/SearchBox.js +29 -16
- package/dist/LinearGenomeView/components/TrackLabel.js +1 -0
- package/dist/LinearGenomeView/components/TrackLabelMenu.js +2 -9
- package/dist/LinearGenomeView/components/useWheelScroll.js +13 -1
- package/dist/LinearGenomeView/components/util.d.ts +1 -0
- package/dist/LinearGenomeView/components/util.js +7 -4
- package/dist/LinearGenomeView/model.d.ts +6 -5
- package/dist/LinearGenomeView/model.js +16 -13
- package/dist/LinearGenomeView/util.d.ts +6 -1
- package/dist/LinearGenomeView/util.js +20 -6
- package/dist/searchUtils.js +1 -1
- package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +24 -8
- package/esm/LinearGenomeView/components/Cytobands.js +18 -4
- package/esm/LinearGenomeView/components/GetSequenceDialog.js +3 -2
- package/esm/LinearGenomeView/components/HeaderPanControls.js +0 -3
- package/esm/LinearGenomeView/components/MiniControls.js +1 -0
- package/esm/LinearGenomeView/components/OverviewRubberband.js +0 -6
- package/esm/LinearGenomeView/components/OverviewRubberbandHoverTooltip.js +1 -9
- package/esm/LinearGenomeView/components/RefNameAutocomplete/index.js +16 -10
- package/esm/LinearGenomeView/components/SearchBox.d.ts +1 -1
- package/esm/LinearGenomeView/components/SearchBox.js +29 -16
- package/esm/LinearGenomeView/components/TrackLabel.js +1 -0
- package/esm/LinearGenomeView/components/TrackLabelMenu.js +2 -9
- package/esm/LinearGenomeView/components/useWheelScroll.js +13 -1
- package/esm/LinearGenomeView/components/util.d.ts +1 -0
- package/esm/LinearGenomeView/components/util.js +7 -4
- package/esm/LinearGenomeView/model.d.ts +6 -5
- package/esm/LinearGenomeView/model.js +16 -13
- package/esm/LinearGenomeView/util.d.ts +6 -1
- package/esm/LinearGenomeView/util.js +20 -6
- package/esm/searchUtils.js +1 -1
- package/package.json +3 -3
|
@@ -141,12 +141,28 @@ function stateModelFactory() {
|
|
|
141
141
|
selectFeature(feature) {
|
|
142
142
|
const session = (0, util_1.getSession)(self);
|
|
143
143
|
if ((0, util_1.isSessionModelWithWidgets)(session)) {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
144
|
+
const { rpcManager } = session;
|
|
145
|
+
const sessionId = (0, tracks_1.getRpcSessionId)(self);
|
|
146
|
+
const track = (0, util_1.getContainingTrack)(self);
|
|
147
|
+
const view = (0, util_1.getContainingView)(self);
|
|
148
|
+
const adapterConfig = (0, configuration_1.getConf)(track, 'adapter');
|
|
149
|
+
(async () => {
|
|
150
|
+
try {
|
|
151
|
+
const descriptions = await rpcManager.call(sessionId, 'CoreGetMetadata', {
|
|
152
|
+
adapterConfig,
|
|
153
|
+
});
|
|
154
|
+
session.showWidget(session.addWidget('BaseFeatureWidget', 'baseFeature', {
|
|
155
|
+
featureData: feature.toJSON(),
|
|
156
|
+
view,
|
|
157
|
+
track,
|
|
158
|
+
descriptions,
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
catch (e) {
|
|
162
|
+
console.error(e);
|
|
163
|
+
(0, util_1.getSession)(e).notifyError(`${e}`, e);
|
|
164
|
+
}
|
|
165
|
+
})();
|
|
150
166
|
}
|
|
151
167
|
if ((0, util_1.isSelectionContainer)(session)) {
|
|
152
168
|
session.setSelection(feature);
|
|
@@ -11,14 +11,14 @@ function rightRoundedRect(x, y, width, height, radius) {
|
|
|
11
11
|
function leftRoundedRect(x, y, width, height, radius) {
|
|
12
12
|
return `M${x + radius},${y}h${width - radius}v${height}h${radius - width}a${radius},${radius} 0 0 1 ${-radius},${-radius}v${2 * radius - height}a${radius},${radius} 0 0 1 ${radius},${-radius}z`;
|
|
13
13
|
}
|
|
14
|
-
function leftTriangle(x,
|
|
14
|
+
function leftTriangle(x, _y, width, height) {
|
|
15
15
|
return [
|
|
16
16
|
[x, 0],
|
|
17
17
|
[x + width, height / 2],
|
|
18
18
|
[x, height],
|
|
19
19
|
].toString();
|
|
20
20
|
}
|
|
21
|
-
function rightTriangle(x,
|
|
21
|
+
function rightTriangle(x, _y, width, height) {
|
|
22
22
|
return [
|
|
23
23
|
[x, height / 2],
|
|
24
24
|
[x + width, 0],
|
|
@@ -42,14 +42,28 @@ const Cytobands = (0, mobx_react_1.observer)(function ({ overview, block, assemb
|
|
|
42
42
|
const rcap = reversed ? 0 : cytobands.length - 1;
|
|
43
43
|
const h = consts_1.HEADER_OVERVIEW_HEIGHT;
|
|
44
44
|
let centromereSeen = false;
|
|
45
|
+
let curr = '';
|
|
46
|
+
let idx = 0;
|
|
45
47
|
return ((0, jsx_runtime_1.jsx)("g", { transform: `translate(-${offsetPx})`, children: cytobands.map((args, index) => {
|
|
46
48
|
const k = JSON.stringify(args);
|
|
47
|
-
const { refName, type, start, end } = args;
|
|
49
|
+
const { refName, name, type, start, end } = args;
|
|
48
50
|
const s = overview.bpToPx({ refName, coord: start }) || 0;
|
|
49
51
|
const e = overview.bpToPx({ refName, coord: end }) || 0;
|
|
50
52
|
const l = Math.min(s, e);
|
|
51
53
|
const w = Math.abs(e - s);
|
|
52
|
-
|
|
54
|
+
if (type === 'n/a') {
|
|
55
|
+
const match = name === null || name === void 0 ? void 0 : name.match(/^(\d+)([A-Za-z])/);
|
|
56
|
+
const ret = match[1] + match[2];
|
|
57
|
+
if (ret && ret !== curr) {
|
|
58
|
+
curr = ret;
|
|
59
|
+
idx++;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const c = type === 'n/a'
|
|
63
|
+
? idx % 2
|
|
64
|
+
? 'black'
|
|
65
|
+
: '#a77'
|
|
66
|
+
: colorMap[type] || 'black';
|
|
53
67
|
if (type === 'acen' && !centromereSeen) {
|
|
54
68
|
centromereSeen = true;
|
|
55
69
|
return ((0, jsx_runtime_1.jsx)("polygon", { points: reversed
|
|
@@ -33,8 +33,9 @@ async function fetchSequence(model, regions) {
|
|
|
33
33
|
if (!leftOffset || !rightOffset) {
|
|
34
34
|
throw new Error('no offsets on model to use for range');
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
const assemblyNames = new Set(regions.map(r => r.assemblyName));
|
|
37
|
+
if (assemblyNames.size > 1) {
|
|
38
|
+
throw new Error('not able to fetch sequences from multiple assemblies currently');
|
|
38
39
|
}
|
|
39
40
|
const { rpcManager, assemblyManager } = session;
|
|
40
41
|
const assemblyName = leftOffset.assemblyName || rightOffset.assemblyName || '';
|
|
@@ -16,9 +16,6 @@ const useStyles = (0, mui_1.makeStyles)()(theme => ({
|
|
|
16
16
|
color: theme.palette.text.primary,
|
|
17
17
|
margin: consts_1.SPACING,
|
|
18
18
|
},
|
|
19
|
-
buttonSpacer: {
|
|
20
|
-
marginRight: theme.spacing(2),
|
|
21
|
-
},
|
|
22
19
|
}));
|
|
23
20
|
function HeaderPanControls({ model }) {
|
|
24
21
|
const { classes } = useStyles();
|
|
@@ -6,20 +6,12 @@ const material_1 = require("@mui/material");
|
|
|
6
6
|
const mobx_react_1 = require("mobx-react");
|
|
7
7
|
const mui_1 = require("tss-react/mui");
|
|
8
8
|
const useStyles = (0, mui_1.makeStyles)()({
|
|
9
|
-
rubberbandControl: {
|
|
10
|
-
cursor: 'crosshair',
|
|
11
|
-
width: '100%',
|
|
12
|
-
minHeight: 8,
|
|
13
|
-
},
|
|
14
9
|
guide: {
|
|
15
10
|
pointerEvents: 'none',
|
|
16
11
|
height: '100%',
|
|
17
12
|
width: 1,
|
|
18
13
|
position: 'absolute',
|
|
19
14
|
},
|
|
20
|
-
rel: {
|
|
21
|
-
position: 'relative',
|
|
22
|
-
},
|
|
23
15
|
});
|
|
24
16
|
const OverviewRubberbandHoverTooltip = (0, mobx_react_1.observer)(function ({ model, open, guideX, overview, }) {
|
|
25
17
|
var _a;
|
|
@@ -31,6 +23,6 @@ const OverviewRubberbandHoverTooltip = (0, mobx_react_1.observer)(function ({ mo
|
|
|
31
23
|
const cytoband = (_a = assembly === null || assembly === void 0 ? void 0 : assembly.cytobands) === null || _a === void 0 ? void 0 : _a.find(f => px.coord > f.get('start') &&
|
|
32
24
|
px.coord < f.get('end') &&
|
|
33
25
|
px.refName === assembly.getCanonicalRefName(f.get('refName')));
|
|
34
|
-
return ((0, jsx_runtime_1.jsx)(material_1.Tooltip, { open: open, placement: "top", title: [(0, util_1.stringify)(px), cytoband === null || cytoband === void 0 ? void 0 : cytoband.get('name')].join(' '), arrow: true, children: (0, jsx_runtime_1.jsx)("div", { className: classes.guide, style: { left: guideX } }) }));
|
|
26
|
+
return ((0, jsx_runtime_1.jsx)(material_1.Tooltip, { open: open, placement: "top", title: [(0, util_1.stringify)(px), cytoband === null || cytoband === void 0 ? void 0 : cytoband.get('name'), cytoband === null || cytoband === void 0 ? void 0 : cytoband.get('type')].join(' '), arrow: true, children: (0, jsx_runtime_1.jsx)("div", { className: classes.guide, style: { left: guideX } }) }));
|
|
35
27
|
});
|
|
36
28
|
exports.default = OverviewRubberbandHoverTooltip;
|
|
@@ -63,27 +63,31 @@ const RefNameAutocomplete = (0, mobx_react_1.observer)(function ({ model, onSele
|
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
65
|
setLoaded(false);
|
|
66
|
-
|
|
67
|
-
setLoaded(true);
|
|
68
|
-
setSearchOptions((0, util_2.getDeduplicatedResult)(results));
|
|
66
|
+
setSearchOptions((0, util_2.getDeduplicatedResult)(await fetchResults(debouncedSearch)));
|
|
69
67
|
}
|
|
70
68
|
catch (e) {
|
|
71
69
|
console.error(e);
|
|
72
70
|
session.notifyError(`${e}`, e);
|
|
73
71
|
}
|
|
72
|
+
finally {
|
|
73
|
+
setLoaded(true);
|
|
74
|
+
}
|
|
74
75
|
})();
|
|
75
76
|
}, [assemblyName, fetchResults, debouncedSearch, session]);
|
|
76
77
|
const inputBoxVal = coarseVisibleLocStrings || value || '';
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const regionOptions = (refNames === null || refNames === void 0 ? void 0 : refNames.map(refName => ({
|
|
78
|
+
const regions = assembly === null || assembly === void 0 ? void 0 : assembly.regions;
|
|
79
|
+
const regionOptions = (regions === null || regions === void 0 ? void 0 : regions.map(region => ({
|
|
80
80
|
result: new BaseResults_1.RefSequenceResult({
|
|
81
|
-
refName,
|
|
82
|
-
label: refName,
|
|
81
|
+
refName: region.refName,
|
|
82
|
+
label: region.refName,
|
|
83
|
+
displayString: region.refName,
|
|
83
84
|
matchedAttribute: 'refName',
|
|
84
85
|
}),
|
|
85
86
|
}))) || [];
|
|
86
|
-
return ((0, jsx_runtime_1.jsx)(material_1.Autocomplete, { "data-testid": "autocomplete", disableListWrap: true, disableClearable: true, disabled: !assemblyName, freeSolo: true, includeInputInList: true, selectOnFocus: true, style: {
|
|
87
|
+
return ((0, jsx_runtime_1.jsx)(material_1.Autocomplete, { "data-testid": "autocomplete", disableListWrap: true, disableClearable: true, disabled: !assemblyName, freeSolo: true, includeInputInList: true, selectOnFocus: true, style: {
|
|
88
|
+
...style,
|
|
89
|
+
width: Math.min(Math.max((0, util_1.measureText)(inputBoxVal, 14) + 100, minWidth), maxWidth),
|
|
90
|
+
}, value: inputBoxVal, loading: !loaded, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
|
|
87
91
|
setInputValue(newInputValue);
|
|
88
92
|
onChange === null || onChange === void 0 ? void 0 : onChange(newInputValue);
|
|
89
93
|
}, loadingText: "loading results", open: open, onOpen: () => {
|
|
@@ -100,7 +104,9 @@ const RefNameAutocomplete = (0, mobx_react_1.observer)(function ({ model, onSele
|
|
|
100
104
|
return;
|
|
101
105
|
}
|
|
102
106
|
if (typeof selectedOption === 'string') {
|
|
103
|
-
onSelect === null || onSelect === void 0 ? void 0 : onSelect(new BaseResults_1.default({
|
|
107
|
+
onSelect === null || onSelect === void 0 ? void 0 : onSelect(new BaseResults_1.default({
|
|
108
|
+
label: selectedOption,
|
|
109
|
+
}));
|
|
104
110
|
}
|
|
105
111
|
else {
|
|
106
112
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(selectedOption.result);
|
|
@@ -13,11 +13,33 @@ const EndAdornment_1 = __importDefault(require("./RefNameAutocomplete/EndAdornme
|
|
|
13
13
|
const util_2 = require("./util");
|
|
14
14
|
const searchUtils_1 = require("../../searchUtils");
|
|
15
15
|
const consts_1 = require("../consts");
|
|
16
|
-
const useStyles = (0, mui_1.makeStyles)()(
|
|
16
|
+
const useStyles = (0, mui_1.makeStyles)()({
|
|
17
17
|
headerRefName: {
|
|
18
18
|
minWidth: 100,
|
|
19
19
|
},
|
|
20
|
-
})
|
|
20
|
+
});
|
|
21
|
+
async function onSelect({ option, model, assemblyName, }) {
|
|
22
|
+
var _a;
|
|
23
|
+
const { assemblyManager } = (0, util_1.getSession)(model);
|
|
24
|
+
const assembly = assemblyManager.get(assemblyName);
|
|
25
|
+
if (option.hasLocation()) {
|
|
26
|
+
await (0, searchUtils_1.navToOption)({
|
|
27
|
+
option,
|
|
28
|
+
model,
|
|
29
|
+
assemblyName,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
else if ((_a = option.results) === null || _a === void 0 ? void 0 : _a.length) {
|
|
33
|
+
model.setSearchResults(option.results, option.getLabel());
|
|
34
|
+
}
|
|
35
|
+
else if (assembly) {
|
|
36
|
+
await (0, searchUtils_1.handleSelectedRegion)({
|
|
37
|
+
input: option.getLabel(),
|
|
38
|
+
assembly,
|
|
39
|
+
model,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
21
43
|
const SearchBox = (0, mobx_react_1.observer)(function ({ model, showHelp = true, }) {
|
|
22
44
|
const { classes } = useStyles();
|
|
23
45
|
const theme = (0, material_1.useTheme)();
|
|
@@ -28,21 +50,12 @@ const SearchBox = (0, mobx_react_1.observer)(function ({ model, showHelp = true,
|
|
|
28
50
|
const assembly = assemblyManager.get(assemblyName);
|
|
29
51
|
const searchScope = model.searchScope(assemblyName);
|
|
30
52
|
return ((0, jsx_runtime_1.jsx)(RefNameAutocomplete_1.default, { onSelect: async (option) => {
|
|
31
|
-
var _a;
|
|
32
53
|
try {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
else if (assembly) {
|
|
40
|
-
await (0, searchUtils_1.handleSelectedRegion)({
|
|
41
|
-
input: option.getLabel(),
|
|
42
|
-
assembly,
|
|
43
|
-
model,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
54
|
+
await onSelect({
|
|
55
|
+
model,
|
|
56
|
+
assemblyName,
|
|
57
|
+
option,
|
|
58
|
+
});
|
|
46
59
|
}
|
|
47
60
|
catch (e) {
|
|
48
61
|
console.error(e);
|
|
@@ -17,6 +17,7 @@ const TrackLabelDragHandle_1 = __importDefault(require("./TrackLabelDragHandle")
|
|
|
17
17
|
const TrackLabelMenu_1 = __importDefault(require("./TrackLabelMenu"));
|
|
18
18
|
const useStyles = (0, mui_1.makeStyles)()(theme => ({
|
|
19
19
|
root: {
|
|
20
|
+
zIndex: 200,
|
|
20
21
|
background: (0, material_1.alpha)(theme.palette.background.paper, 0.8),
|
|
21
22
|
'&:hover': {
|
|
22
23
|
background: theme.palette.background.paper,
|
|
@@ -22,16 +22,9 @@ const TrackLabelMenu = (0, mobx_react_1.observer)(function ({ track, }) {
|
|
|
22
22
|
const trackConf = track.configuration;
|
|
23
23
|
const minimized = track.minimized;
|
|
24
24
|
const pinned = track.pinned;
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
(0, util_1.getContainingView)(view);
|
|
28
|
-
lgvHasParentView = true;
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
lgvHasParentView = false;
|
|
32
|
-
}
|
|
25
|
+
const { isTopLevelView } = view;
|
|
33
26
|
const items = [
|
|
34
|
-
...(
|
|
27
|
+
...(isTopLevelView
|
|
35
28
|
? []
|
|
36
29
|
: [
|
|
37
30
|
{
|
|
@@ -2,16 +2,27 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.useWheelScroll = useWheelScroll;
|
|
4
4
|
const react_1 = require("react");
|
|
5
|
+
const util_1 = require("@jbrowse/core/util");
|
|
5
6
|
function useWheelScroll(ref, model) {
|
|
6
7
|
const delta = (0, react_1.useRef)(0);
|
|
7
8
|
const timeout = (0, react_1.useRef)(null);
|
|
8
9
|
const scheduled = (0, react_1.useRef)(false);
|
|
9
10
|
(0, react_1.useEffect)(() => {
|
|
11
|
+
let samples = [];
|
|
10
12
|
const curr = ref.current;
|
|
11
13
|
function onWheel(event) {
|
|
12
14
|
if (event.ctrlKey) {
|
|
13
15
|
event.preventDefault();
|
|
14
|
-
|
|
16
|
+
samples.push(event.deltaY);
|
|
17
|
+
const averageDeltaY = Math.abs((0, util_1.sum)(samples)) / samples.length;
|
|
18
|
+
const normalizer = averageDeltaY < 6
|
|
19
|
+
? 25
|
|
20
|
+
: averageDeltaY > 30
|
|
21
|
+
? averageDeltaY > 150
|
|
22
|
+
? 500
|
|
23
|
+
: 150
|
|
24
|
+
: 75;
|
|
25
|
+
delta.current += event.deltaY / normalizer;
|
|
15
26
|
model.setScaleFactor(delta.current < 0 ? 1 - delta.current : 1 / (1 + delta.current));
|
|
16
27
|
if (timeout.current) {
|
|
17
28
|
clearTimeout(timeout.current);
|
|
@@ -22,6 +33,7 @@ function useWheelScroll(ref, model) {
|
|
|
22
33
|
? model.bpPerPx * (1 + delta.current)
|
|
23
34
|
: model.bpPerPx / (1 - delta.current), event.clientX - ((curr === null || curr === void 0 ? void 0 : curr.getBoundingClientRect().left) || 0));
|
|
24
35
|
delta.current = 0;
|
|
36
|
+
samples = [];
|
|
25
37
|
}, 300);
|
|
26
38
|
}
|
|
27
39
|
else {
|
|
@@ -14,12 +14,14 @@ async function fetchResults({ queryString, searchType, searchScope, rankSearchRe
|
|
|
14
14
|
if (!textSearchManager) {
|
|
15
15
|
console.warn('No text search manager');
|
|
16
16
|
}
|
|
17
|
-
const textSearchResults = await (textSearchManager === null || textSearchManager === void 0 ? void 0 : textSearchManager.search({
|
|
17
|
+
const textSearchResults = (await (textSearchManager === null || textSearchManager === void 0 ? void 0 : textSearchManager.search({
|
|
18
18
|
queryString,
|
|
19
19
|
searchType,
|
|
20
|
-
}, searchScope, rankSearchResults));
|
|
21
|
-
const refNameResults = (_a = assembly === null || assembly === void 0 ? void 0 : assembly.allRefNames) === null || _a === void 0 ? void 0 : _a.filter(ref => ref.toLowerCase().startsWith(queryString.toLowerCase())).slice(0, 10).map(r => new BaseResults_1.default({
|
|
22
|
-
|
|
20
|
+
}, searchScope, rankSearchResults))) || [];
|
|
21
|
+
const refNameResults = ((_a = assembly === null || assembly === void 0 ? void 0 : assembly.allRefNames) === null || _a === void 0 ? void 0 : _a.filter(ref => ref.toLowerCase().startsWith(queryString.toLowerCase())).slice(0, 10).map(r => new BaseResults_1.default({
|
|
22
|
+
label: r,
|
|
23
|
+
}))) || [];
|
|
24
|
+
return (0, util_1.dedupe)([...refNameResults, ...textSearchResults], elt => elt.getId());
|
|
23
25
|
}
|
|
24
26
|
function splitLast(str, split) {
|
|
25
27
|
const lastIndex = str.lastIndexOf(split);
|
|
@@ -42,5 +44,6 @@ function getCytobands(assembly, refName) {
|
|
|
42
44
|
start: f.get('start'),
|
|
43
45
|
end: f.get('end'),
|
|
44
46
|
type: f.get('gieStain'),
|
|
47
|
+
name: f.get('name'),
|
|
45
48
|
})).filter(f => f.refName === refName)) || []);
|
|
46
49
|
}
|
|
@@ -58,7 +58,8 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
58
58
|
readonly width: number;
|
|
59
59
|
readonly interRegionPaddingWidth: number;
|
|
60
60
|
readonly assemblyNames: string[];
|
|
61
|
-
readonly
|
|
61
|
+
readonly isTopLevelView: import("@jbrowse/core/util").AbstractViewModel | undefined;
|
|
62
|
+
readonly stickyViewHeaders: boolean | undefined;
|
|
62
63
|
readonly rubberbandTop: number;
|
|
63
64
|
readonly pinnedTracksTop: number;
|
|
64
65
|
} & {
|
|
@@ -169,13 +170,13 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
169
170
|
setCoarseDynamicBlocks(blocks: BlockSet): void;
|
|
170
171
|
} & {
|
|
171
172
|
moveTo(start?: BpOffset, end?: BpOffset): void;
|
|
172
|
-
navToLocString(input: string, optAssemblyName?: string): Promise<void>;
|
|
173
|
+
navToLocString(input: string, optAssemblyName?: string, grow?: number): Promise<void>;
|
|
173
174
|
navToSearchString({ input, assembly, }: {
|
|
174
175
|
input: string;
|
|
175
176
|
assembly: Assembly;
|
|
176
177
|
}): Promise<void>;
|
|
177
|
-
navToLocation(parsedLocString: ParsedLocString, assemblyName?: string): Promise<void>;
|
|
178
|
-
navToLocations(
|
|
178
|
+
navToLocation(parsedLocString: ParsedLocString, assemblyName?: string, grow?: number): Promise<void>;
|
|
179
|
+
navToLocations(regions: ParsedLocString[], assemblyName?: string, grow?: number): Promise<void>;
|
|
179
180
|
navTo(query: NavLocation): void;
|
|
180
181
|
navToMultiple(locations: NavLocation[]): void;
|
|
181
182
|
} & {
|
|
@@ -243,9 +244,9 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
243
244
|
displayName: string | undefined;
|
|
244
245
|
tracks: any[];
|
|
245
246
|
minimized: boolean;
|
|
246
|
-
displayedRegions: Region[];
|
|
247
247
|
offsetPx: number;
|
|
248
248
|
bpPerPx: number;
|
|
249
|
+
displayedRegions: Region[];
|
|
249
250
|
hideHeader: boolean;
|
|
250
251
|
hideHeaderOverview: boolean;
|
|
251
252
|
hideNoTracksActive: boolean;
|
|
@@ -136,10 +136,14 @@ function stateModelFactory(pluginManager) {
|
|
|
136
136
|
...new Set(self.displayedRegions.map(region => region.assemblyName)),
|
|
137
137
|
];
|
|
138
138
|
},
|
|
139
|
+
get isTopLevelView() {
|
|
140
|
+
const session = (0, util_1.getSession)(self);
|
|
141
|
+
return session.views.find(r => r.id === self.id);
|
|
142
|
+
},
|
|
139
143
|
get stickyViewHeaders() {
|
|
140
144
|
const session = (0, util_1.getSession)(self);
|
|
141
145
|
return (0, product_core_1.isSessionWithMultipleViews)(session)
|
|
142
|
-
? session.stickyViewHeaders
|
|
146
|
+
? this.isTopLevelView && session.stickyViewHeaders
|
|
143
147
|
: false;
|
|
144
148
|
},
|
|
145
149
|
get rubberbandTop() {
|
|
@@ -262,12 +266,6 @@ function stateModelFactory(pluginManager) {
|
|
|
262
266
|
return self.tracks.find(t => t.configuration.trackId === id);
|
|
263
267
|
},
|
|
264
268
|
rankSearchResults(results) {
|
|
265
|
-
const openTrackIds = new Set(self.tracks.map(track => track.configuration.trackId));
|
|
266
|
-
for (const result of results) {
|
|
267
|
-
if (openTrackIds.has(result.trackId)) {
|
|
268
|
-
result.updateScore(result.getScore() + 1);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
269
|
return results;
|
|
272
270
|
},
|
|
273
271
|
rewriteOnClicks(trackType, viewMenuActions) {
|
|
@@ -840,14 +838,14 @@ function stateModelFactory(pluginManager) {
|
|
|
840
838
|
moveTo(start, end) {
|
|
841
839
|
(0, Base1DUtils_1.moveTo)(self, start, end);
|
|
842
840
|
},
|
|
843
|
-
async navToLocString(input, optAssemblyName) {
|
|
841
|
+
async navToLocString(input, optAssemblyName, grow) {
|
|
844
842
|
const { assemblyNames } = self;
|
|
845
843
|
const { assemblyManager } = (0, util_1.getSession)(self);
|
|
846
844
|
const assemblyName = optAssemblyName || assemblyNames[0];
|
|
847
845
|
if (assemblyName) {
|
|
848
846
|
await assemblyManager.waitForAssembly(assemblyName);
|
|
849
847
|
}
|
|
850
|
-
return this.navToLocations((0, util_2.parseLocStrings)(input, assemblyName, (ref, asm) => assemblyManager.isValidRefName(ref, asm)), assemblyName);
|
|
848
|
+
return this.navToLocations((0, util_2.parseLocStrings)(input, assemblyName, (ref, asm) => assemblyManager.isValidRefName(ref, asm)), assemblyName, grow);
|
|
851
849
|
},
|
|
852
850
|
async navToSearchString({ input, assembly, }) {
|
|
853
851
|
await (0, searchUtils_1.handleSelectedRegion)({
|
|
@@ -856,13 +854,18 @@ function stateModelFactory(pluginManager) {
|
|
|
856
854
|
model: self,
|
|
857
855
|
});
|
|
858
856
|
},
|
|
859
|
-
async navToLocation(parsedLocString, assemblyName) {
|
|
860
|
-
return this.navToLocations([parsedLocString], assemblyName);
|
|
857
|
+
async navToLocation(parsedLocString, assemblyName, grow) {
|
|
858
|
+
return this.navToLocations([parsedLocString], assemblyName, grow);
|
|
861
859
|
},
|
|
862
|
-
async navToLocations(
|
|
860
|
+
async navToLocations(regions, assemblyName, grow) {
|
|
863
861
|
const { assemblyManager } = (0, util_1.getSession)(self);
|
|
864
862
|
await (0, mobx_1.when)(() => self.volatileWidth !== undefined);
|
|
865
|
-
const locations = await (0, util_2.generateLocations)(
|
|
863
|
+
const locations = await (0, util_2.generateLocations)({
|
|
864
|
+
regions,
|
|
865
|
+
assemblyManager,
|
|
866
|
+
assemblyName,
|
|
867
|
+
grow,
|
|
868
|
+
});
|
|
866
869
|
if (locations.length === 1) {
|
|
867
870
|
const loc = locations[0];
|
|
868
871
|
const { reversed, parentRegion, start, end } = loc;
|
|
@@ -9,7 +9,12 @@ export declare function makeTicks(start: number, end: number, bpPerPx: number, e
|
|
|
9
9
|
base: number;
|
|
10
10
|
index: number;
|
|
11
11
|
}[];
|
|
12
|
-
export declare function generateLocations(regions
|
|
12
|
+
export declare function generateLocations({ regions, assemblyManager, assemblyName, grow, }: {
|
|
13
|
+
regions: ParsedLocString[];
|
|
14
|
+
assemblyManager: AssemblyManager;
|
|
15
|
+
assemblyName?: string;
|
|
16
|
+
grow?: number;
|
|
17
|
+
}): Promise<{
|
|
13
18
|
assemblyName: string;
|
|
14
19
|
parentRegion: import("@jbrowse/core/assemblyManager/assembly").BasicRegion;
|
|
15
20
|
end?: number | undefined;
|
|
@@ -57,7 +57,7 @@ function makeTicks(start, end, bpPerPx, emitMajor = true, emitMinor = true) {
|
|
|
57
57
|
}
|
|
58
58
|
return ticks;
|
|
59
59
|
}
|
|
60
|
-
async function generateLocations(regions, assemblyManager, assemblyName) {
|
|
60
|
+
async function generateLocations({ regions, assemblyManager, assemblyName, grow, }) {
|
|
61
61
|
return Promise.all(regions.map(async (region) => {
|
|
62
62
|
const asmName = region.assemblyName || assemblyName;
|
|
63
63
|
if (!asmName) {
|
|
@@ -80,11 +80,25 @@ async function generateLocations(regions, assemblyManager, assemblyName) {
|
|
|
80
80
|
if (!parentRegion) {
|
|
81
81
|
throw new Error(`Could not find refName ${refName} in ${asmName}`);
|
|
82
82
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
const { start, end } = region;
|
|
84
|
+
if (grow && start && end) {
|
|
85
|
+
const len = end - start;
|
|
86
|
+
const margin = len * grow;
|
|
87
|
+
return {
|
|
88
|
+
...region,
|
|
89
|
+
start: Math.max(0, start - margin),
|
|
90
|
+
end: end + margin,
|
|
91
|
+
assemblyName: asmName,
|
|
92
|
+
parentRegion,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
return {
|
|
97
|
+
...region,
|
|
98
|
+
assemblyName: asmName,
|
|
99
|
+
parentRegion,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
88
102
|
}));
|
|
89
103
|
}
|
|
90
104
|
function parseLocStrings(input, assemblyName, isValidRefName) {
|
package/dist/searchUtils.js
CHANGED
|
@@ -14,7 +14,7 @@ async function navToOption({ option, model, assemblyName, }) {
|
|
|
14
14
|
const location = option.getLocation();
|
|
15
15
|
const trackId = option.getTrackId();
|
|
16
16
|
if (location) {
|
|
17
|
-
await model.navToLocString(location, assemblyName);
|
|
17
|
+
await model.navToLocString(location, assemblyName, 0.2);
|
|
18
18
|
if (trackId) {
|
|
19
19
|
model.showTrack(trackId);
|
|
20
20
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { lazy } from 'react';
|
|
2
|
-
import { ConfigurationReference } from '@jbrowse/core/configuration';
|
|
2
|
+
import { ConfigurationReference, getConf } from '@jbrowse/core/configuration';
|
|
3
3
|
import { BaseDisplay } from '@jbrowse/core/pluggableElementTypes/models';
|
|
4
4
|
import { getContainingTrack, getContainingView, getSession, isFeature, isSelectionContainer, isSessionModelWithWidgets, } from '@jbrowse/core/util';
|
|
5
5
|
import CompositeMap from '@jbrowse/core/util/compositeMap';
|
|
6
|
-
import { getParentRenderProps } from '@jbrowse/core/util/tracks';
|
|
6
|
+
import { getParentRenderProps, getRpcSessionId, } from '@jbrowse/core/util/tracks';
|
|
7
7
|
import CenterFocusStrongIcon from '@mui/icons-material/CenterFocusStrong';
|
|
8
8
|
import MenuOpenIcon from '@mui/icons-material/MenuOpen';
|
|
9
9
|
import { autorun } from 'mobx';
|
|
@@ -102,12 +102,28 @@ function stateModelFactory() {
|
|
|
102
102
|
selectFeature(feature) {
|
|
103
103
|
const session = getSession(self);
|
|
104
104
|
if (isSessionModelWithWidgets(session)) {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
105
|
+
const { rpcManager } = session;
|
|
106
|
+
const sessionId = getRpcSessionId(self);
|
|
107
|
+
const track = getContainingTrack(self);
|
|
108
|
+
const view = getContainingView(self);
|
|
109
|
+
const adapterConfig = getConf(track, 'adapter');
|
|
110
|
+
(async () => {
|
|
111
|
+
try {
|
|
112
|
+
const descriptions = await rpcManager.call(sessionId, 'CoreGetMetadata', {
|
|
113
|
+
adapterConfig,
|
|
114
|
+
});
|
|
115
|
+
session.showWidget(session.addWidget('BaseFeatureWidget', 'baseFeature', {
|
|
116
|
+
featureData: feature.toJSON(),
|
|
117
|
+
view,
|
|
118
|
+
track,
|
|
119
|
+
descriptions,
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
console.error(e);
|
|
124
|
+
getSession(e).notifyError(`${e}`, e);
|
|
125
|
+
}
|
|
126
|
+
})();
|
|
111
127
|
}
|
|
112
128
|
if (isSelectionContainer(session)) {
|
|
113
129
|
session.setSelection(feature);
|
|
@@ -9,14 +9,14 @@ function rightRoundedRect(x, y, width, height, radius) {
|
|
|
9
9
|
function leftRoundedRect(x, y, width, height, radius) {
|
|
10
10
|
return `M${x + radius},${y}h${width - radius}v${height}h${radius - width}a${radius},${radius} 0 0 1 ${-radius},${-radius}v${2 * radius - height}a${radius},${radius} 0 0 1 ${radius},${-radius}z`;
|
|
11
11
|
}
|
|
12
|
-
function leftTriangle(x,
|
|
12
|
+
function leftTriangle(x, _y, width, height) {
|
|
13
13
|
return [
|
|
14
14
|
[x, 0],
|
|
15
15
|
[x + width, height / 2],
|
|
16
16
|
[x, height],
|
|
17
17
|
].toString();
|
|
18
18
|
}
|
|
19
|
-
function rightTriangle(x,
|
|
19
|
+
function rightTriangle(x, _y, width, height) {
|
|
20
20
|
return [
|
|
21
21
|
[x, height / 2],
|
|
22
22
|
[x + width, 0],
|
|
@@ -40,14 +40,28 @@ const Cytobands = observer(function ({ overview, block, assembly, }) {
|
|
|
40
40
|
const rcap = reversed ? 0 : cytobands.length - 1;
|
|
41
41
|
const h = HEADER_OVERVIEW_HEIGHT;
|
|
42
42
|
let centromereSeen = false;
|
|
43
|
+
let curr = '';
|
|
44
|
+
let idx = 0;
|
|
43
45
|
return (_jsx("g", { transform: `translate(-${offsetPx})`, children: cytobands.map((args, index) => {
|
|
44
46
|
const k = JSON.stringify(args);
|
|
45
|
-
const { refName, type, start, end } = args;
|
|
47
|
+
const { refName, name, type, start, end } = args;
|
|
46
48
|
const s = overview.bpToPx({ refName, coord: start }) || 0;
|
|
47
49
|
const e = overview.bpToPx({ refName, coord: end }) || 0;
|
|
48
50
|
const l = Math.min(s, e);
|
|
49
51
|
const w = Math.abs(e - s);
|
|
50
|
-
|
|
52
|
+
if (type === 'n/a') {
|
|
53
|
+
const match = name === null || name === void 0 ? void 0 : name.match(/^(\d+)([A-Za-z])/);
|
|
54
|
+
const ret = match[1] + match[2];
|
|
55
|
+
if (ret && ret !== curr) {
|
|
56
|
+
curr = ret;
|
|
57
|
+
idx++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const c = type === 'n/a'
|
|
61
|
+
? idx % 2
|
|
62
|
+
? 'black'
|
|
63
|
+
: '#a77'
|
|
64
|
+
: colorMap[type] || 'black';
|
|
51
65
|
if (type === 'acen' && !centromereSeen) {
|
|
52
66
|
centromereSeen = true;
|
|
53
67
|
return (_jsx("polygon", { points: reversed
|
|
@@ -28,8 +28,9 @@ async function fetchSequence(model, regions) {
|
|
|
28
28
|
if (!leftOffset || !rightOffset) {
|
|
29
29
|
throw new Error('no offsets on model to use for range');
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
const assemblyNames = new Set(regions.map(r => r.assemblyName));
|
|
32
|
+
if (assemblyNames.size > 1) {
|
|
33
|
+
throw new Error('not able to fetch sequences from multiple assemblies currently');
|
|
33
34
|
}
|
|
34
35
|
const { rpcManager, assemblyManager } = session;
|
|
35
36
|
const assemblyName = leftOffset.assemblyName || rightOffset.assemblyName || '';
|
|
@@ -10,9 +10,6 @@ const useStyles = makeStyles()(theme => ({
|
|
|
10
10
|
color: theme.palette.text.primary,
|
|
11
11
|
margin: SPACING,
|
|
12
12
|
},
|
|
13
|
-
buttonSpacer: {
|
|
14
|
-
marginRight: theme.spacing(2),
|
|
15
|
-
},
|
|
16
13
|
}));
|
|
17
14
|
export default function HeaderPanControls({ model }) {
|
|
18
15
|
const { classes } = useStyles();
|
|
@@ -4,20 +4,12 @@ import { Tooltip } from '@mui/material';
|
|
|
4
4
|
import { observer } from 'mobx-react';
|
|
5
5
|
import { makeStyles } from 'tss-react/mui';
|
|
6
6
|
const useStyles = makeStyles()({
|
|
7
|
-
rubberbandControl: {
|
|
8
|
-
cursor: 'crosshair',
|
|
9
|
-
width: '100%',
|
|
10
|
-
minHeight: 8,
|
|
11
|
-
},
|
|
12
7
|
guide: {
|
|
13
8
|
pointerEvents: 'none',
|
|
14
9
|
height: '100%',
|
|
15
10
|
width: 1,
|
|
16
11
|
position: 'absolute',
|
|
17
12
|
},
|
|
18
|
-
rel: {
|
|
19
|
-
position: 'relative',
|
|
20
|
-
},
|
|
21
13
|
});
|
|
22
14
|
const OverviewRubberbandHoverTooltip = observer(function ({ model, open, guideX, overview, }) {
|
|
23
15
|
var _a;
|
|
@@ -29,6 +21,6 @@ const OverviewRubberbandHoverTooltip = observer(function ({ model, open, guideX,
|
|
|
29
21
|
const cytoband = (_a = assembly === null || assembly === void 0 ? void 0 : assembly.cytobands) === null || _a === void 0 ? void 0 : _a.find(f => px.coord > f.get('start') &&
|
|
30
22
|
px.coord < f.get('end') &&
|
|
31
23
|
px.refName === assembly.getCanonicalRefName(f.get('refName')));
|
|
32
|
-
return (_jsx(Tooltip, { open: open, placement: "top", title: [stringify(px), cytoband === null || cytoband === void 0 ? void 0 : cytoband.get('name')].join(' '), arrow: true, children: _jsx("div", { className: classes.guide, style: { left: guideX } }) }));
|
|
24
|
+
return (_jsx(Tooltip, { open: open, placement: "top", title: [stringify(px), cytoband === null || cytoband === void 0 ? void 0 : cytoband.get('name'), cytoband === null || cytoband === void 0 ? void 0 : cytoband.get('type')].join(' '), arrow: true, children: _jsx("div", { className: classes.guide, style: { left: guideX } }) }));
|
|
33
25
|
});
|
|
34
26
|
export default OverviewRubberbandHoverTooltip;
|
|
@@ -25,27 +25,31 @@ const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName,
|
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
27
|
setLoaded(false);
|
|
28
|
-
|
|
29
|
-
setLoaded(true);
|
|
30
|
-
setSearchOptions(getDeduplicatedResult(results));
|
|
28
|
+
setSearchOptions(getDeduplicatedResult(await fetchResults(debouncedSearch)));
|
|
31
29
|
}
|
|
32
30
|
catch (e) {
|
|
33
31
|
console.error(e);
|
|
34
32
|
session.notifyError(`${e}`, e);
|
|
35
33
|
}
|
|
34
|
+
finally {
|
|
35
|
+
setLoaded(true);
|
|
36
|
+
}
|
|
36
37
|
})();
|
|
37
38
|
}, [assemblyName, fetchResults, debouncedSearch, session]);
|
|
38
39
|
const inputBoxVal = coarseVisibleLocStrings || value || '';
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const regionOptions = (refNames === null || refNames === void 0 ? void 0 : refNames.map(refName => ({
|
|
40
|
+
const regions = assembly === null || assembly === void 0 ? void 0 : assembly.regions;
|
|
41
|
+
const regionOptions = (regions === null || regions === void 0 ? void 0 : regions.map(region => ({
|
|
42
42
|
result: new RefSequenceResult({
|
|
43
|
-
refName,
|
|
44
|
-
label: refName,
|
|
43
|
+
refName: region.refName,
|
|
44
|
+
label: region.refName,
|
|
45
|
+
displayString: region.refName,
|
|
45
46
|
matchedAttribute: 'refName',
|
|
46
47
|
}),
|
|
47
48
|
}))) || [];
|
|
48
|
-
return (_jsx(Autocomplete, { "data-testid": "autocomplete", disableListWrap: true, disableClearable: true, disabled: !assemblyName, freeSolo: true, includeInputInList: true, selectOnFocus: true, style: {
|
|
49
|
+
return (_jsx(Autocomplete, { "data-testid": "autocomplete", disableListWrap: true, disableClearable: true, disabled: !assemblyName, freeSolo: true, includeInputInList: true, selectOnFocus: true, style: {
|
|
50
|
+
...style,
|
|
51
|
+
width: Math.min(Math.max(measureText(inputBoxVal, 14) + 100, minWidth), maxWidth),
|
|
52
|
+
}, value: inputBoxVal, loading: !loaded, inputValue: inputValue, onInputChange: (_event, newInputValue) => {
|
|
49
53
|
setInputValue(newInputValue);
|
|
50
54
|
onChange === null || onChange === void 0 ? void 0 : onChange(newInputValue);
|
|
51
55
|
}, loadingText: "loading results", open: open, onOpen: () => {
|
|
@@ -62,7 +66,9 @@ const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName,
|
|
|
62
66
|
return;
|
|
63
67
|
}
|
|
64
68
|
if (typeof selectedOption === 'string') {
|
|
65
|
-
onSelect === null || onSelect === void 0 ? void 0 : onSelect(new BaseResult({
|
|
69
|
+
onSelect === null || onSelect === void 0 ? void 0 : onSelect(new BaseResult({
|
|
70
|
+
label: selectedOption,
|
|
71
|
+
}));
|
|
66
72
|
}
|
|
67
73
|
else {
|
|
68
74
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(selectedOption.result);
|
|
@@ -8,11 +8,33 @@ import EndAdornment from './RefNameAutocomplete/EndAdornment';
|
|
|
8
8
|
import { fetchResults } from './util';
|
|
9
9
|
import { handleSelectedRegion, navToOption } from '../../searchUtils';
|
|
10
10
|
import { SPACING, WIDGET_HEIGHT } from '../consts';
|
|
11
|
-
const useStyles = makeStyles()(
|
|
11
|
+
const useStyles = makeStyles()({
|
|
12
12
|
headerRefName: {
|
|
13
13
|
minWidth: 100,
|
|
14
14
|
},
|
|
15
|
-
})
|
|
15
|
+
});
|
|
16
|
+
async function onSelect({ option, model, assemblyName, }) {
|
|
17
|
+
var _a;
|
|
18
|
+
const { assemblyManager } = getSession(model);
|
|
19
|
+
const assembly = assemblyManager.get(assemblyName);
|
|
20
|
+
if (option.hasLocation()) {
|
|
21
|
+
await navToOption({
|
|
22
|
+
option,
|
|
23
|
+
model,
|
|
24
|
+
assemblyName,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
else if ((_a = option.results) === null || _a === void 0 ? void 0 : _a.length) {
|
|
28
|
+
model.setSearchResults(option.results, option.getLabel());
|
|
29
|
+
}
|
|
30
|
+
else if (assembly) {
|
|
31
|
+
await handleSelectedRegion({
|
|
32
|
+
input: option.getLabel(),
|
|
33
|
+
assembly,
|
|
34
|
+
model,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
16
38
|
const SearchBox = observer(function ({ model, showHelp = true, }) {
|
|
17
39
|
const { classes } = useStyles();
|
|
18
40
|
const theme = useTheme();
|
|
@@ -23,21 +45,12 @@ const SearchBox = observer(function ({ model, showHelp = true, }) {
|
|
|
23
45
|
const assembly = assemblyManager.get(assemblyName);
|
|
24
46
|
const searchScope = model.searchScope(assemblyName);
|
|
25
47
|
return (_jsx(RefNameAutocomplete, { onSelect: async (option) => {
|
|
26
|
-
var _a;
|
|
27
48
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
else if (assembly) {
|
|
35
|
-
await handleSelectedRegion({
|
|
36
|
-
input: option.getLabel(),
|
|
37
|
-
assembly,
|
|
38
|
-
model,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
49
|
+
await onSelect({
|
|
50
|
+
model,
|
|
51
|
+
assemblyName,
|
|
52
|
+
option,
|
|
53
|
+
});
|
|
41
54
|
}
|
|
42
55
|
catch (e) {
|
|
43
56
|
console.error(e);
|
|
@@ -12,6 +12,7 @@ import TrackLabelDragHandle from './TrackLabelDragHandle';
|
|
|
12
12
|
import TrackLabelMenu from './TrackLabelMenu';
|
|
13
13
|
const useStyles = makeStyles()(theme => ({
|
|
14
14
|
root: {
|
|
15
|
+
zIndex: 200,
|
|
15
16
|
background: alpha(theme.palette.background.paper, 0.8),
|
|
16
17
|
'&:hover': {
|
|
17
18
|
background: theme.palette.background.paper,
|
|
@@ -17,16 +17,9 @@ const TrackLabelMenu = observer(function ({ track, }) {
|
|
|
17
17
|
const trackConf = track.configuration;
|
|
18
18
|
const minimized = track.minimized;
|
|
19
19
|
const pinned = track.pinned;
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
getContainingView(view);
|
|
23
|
-
lgvHasParentView = true;
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
lgvHasParentView = false;
|
|
27
|
-
}
|
|
20
|
+
const { isTopLevelView } = view;
|
|
28
21
|
const items = [
|
|
29
|
-
...(
|
|
22
|
+
...(isTopLevelView
|
|
30
23
|
? []
|
|
31
24
|
: [
|
|
32
25
|
{
|
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { sum } from '@jbrowse/core/util';
|
|
2
3
|
export function useWheelScroll(ref, model) {
|
|
3
4
|
const delta = useRef(0);
|
|
4
5
|
const timeout = useRef(null);
|
|
5
6
|
const scheduled = useRef(false);
|
|
6
7
|
useEffect(() => {
|
|
8
|
+
let samples = [];
|
|
7
9
|
const curr = ref.current;
|
|
8
10
|
function onWheel(event) {
|
|
9
11
|
if (event.ctrlKey) {
|
|
10
12
|
event.preventDefault();
|
|
11
|
-
|
|
13
|
+
samples.push(event.deltaY);
|
|
14
|
+
const averageDeltaY = Math.abs(sum(samples)) / samples.length;
|
|
15
|
+
const normalizer = averageDeltaY < 6
|
|
16
|
+
? 25
|
|
17
|
+
: averageDeltaY > 30
|
|
18
|
+
? averageDeltaY > 150
|
|
19
|
+
? 500
|
|
20
|
+
: 150
|
|
21
|
+
: 75;
|
|
22
|
+
delta.current += event.deltaY / normalizer;
|
|
12
23
|
model.setScaleFactor(delta.current < 0 ? 1 - delta.current : 1 / (1 + delta.current));
|
|
13
24
|
if (timeout.current) {
|
|
14
25
|
clearTimeout(timeout.current);
|
|
@@ -19,6 +30,7 @@ export function useWheelScroll(ref, model) {
|
|
|
19
30
|
? model.bpPerPx * (1 + delta.current)
|
|
20
31
|
: model.bpPerPx / (1 - delta.current), event.clientX - ((curr === null || curr === void 0 ? void 0 : curr.getBoundingClientRect().left) || 0));
|
|
21
32
|
delta.current = 0;
|
|
33
|
+
samples = [];
|
|
22
34
|
}, 300);
|
|
23
35
|
}
|
|
24
36
|
else {
|
|
@@ -5,12 +5,14 @@ export async function fetchResults({ queryString, searchType, searchScope, rankS
|
|
|
5
5
|
if (!textSearchManager) {
|
|
6
6
|
console.warn('No text search manager');
|
|
7
7
|
}
|
|
8
|
-
const textSearchResults = await (textSearchManager === null || textSearchManager === void 0 ? void 0 : textSearchManager.search({
|
|
8
|
+
const textSearchResults = (await (textSearchManager === null || textSearchManager === void 0 ? void 0 : textSearchManager.search({
|
|
9
9
|
queryString,
|
|
10
10
|
searchType,
|
|
11
|
-
}, searchScope, rankSearchResults));
|
|
12
|
-
const refNameResults = (_a = assembly === null || assembly === void 0 ? void 0 : assembly.allRefNames) === null || _a === void 0 ? void 0 : _a.filter(ref => ref.toLowerCase().startsWith(queryString.toLowerCase())).slice(0, 10).map(r => new BaseResult({
|
|
13
|
-
|
|
11
|
+
}, searchScope, rankSearchResults))) || [];
|
|
12
|
+
const refNameResults = ((_a = assembly === null || assembly === void 0 ? void 0 : assembly.allRefNames) === null || _a === void 0 ? void 0 : _a.filter(ref => ref.toLowerCase().startsWith(queryString.toLowerCase())).slice(0, 10).map(r => new BaseResult({
|
|
13
|
+
label: r,
|
|
14
|
+
}))) || [];
|
|
15
|
+
return dedupe([...refNameResults, ...textSearchResults], elt => elt.getId());
|
|
14
16
|
}
|
|
15
17
|
export function splitLast(str, split) {
|
|
16
18
|
const lastIndex = str.lastIndexOf(split);
|
|
@@ -33,5 +35,6 @@ export function getCytobands(assembly, refName) {
|
|
|
33
35
|
start: f.get('start'),
|
|
34
36
|
end: f.get('end'),
|
|
35
37
|
type: f.get('gieStain'),
|
|
38
|
+
name: f.get('name'),
|
|
36
39
|
})).filter(f => f.refName === refName)) || []);
|
|
37
40
|
}
|
|
@@ -58,7 +58,8 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
58
58
|
readonly width: number;
|
|
59
59
|
readonly interRegionPaddingWidth: number;
|
|
60
60
|
readonly assemblyNames: string[];
|
|
61
|
-
readonly
|
|
61
|
+
readonly isTopLevelView: import("@jbrowse/core/util").AbstractViewModel | undefined;
|
|
62
|
+
readonly stickyViewHeaders: boolean | undefined;
|
|
62
63
|
readonly rubberbandTop: number;
|
|
63
64
|
readonly pinnedTracksTop: number;
|
|
64
65
|
} & {
|
|
@@ -169,13 +170,13 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
169
170
|
setCoarseDynamicBlocks(blocks: BlockSet): void;
|
|
170
171
|
} & {
|
|
171
172
|
moveTo(start?: BpOffset, end?: BpOffset): void;
|
|
172
|
-
navToLocString(input: string, optAssemblyName?: string): Promise<void>;
|
|
173
|
+
navToLocString(input: string, optAssemblyName?: string, grow?: number): Promise<void>;
|
|
173
174
|
navToSearchString({ input, assembly, }: {
|
|
174
175
|
input: string;
|
|
175
176
|
assembly: Assembly;
|
|
176
177
|
}): Promise<void>;
|
|
177
|
-
navToLocation(parsedLocString: ParsedLocString, assemblyName?: string): Promise<void>;
|
|
178
|
-
navToLocations(
|
|
178
|
+
navToLocation(parsedLocString: ParsedLocString, assemblyName?: string, grow?: number): Promise<void>;
|
|
179
|
+
navToLocations(regions: ParsedLocString[], assemblyName?: string, grow?: number): Promise<void>;
|
|
179
180
|
navTo(query: NavLocation): void;
|
|
180
181
|
navToMultiple(locations: NavLocation[]): void;
|
|
181
182
|
} & {
|
|
@@ -243,9 +244,9 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
243
244
|
displayName: string | undefined;
|
|
244
245
|
tracks: any[];
|
|
245
246
|
minimized: boolean;
|
|
246
|
-
displayedRegions: Region[];
|
|
247
247
|
offsetPx: number;
|
|
248
248
|
bpPerPx: number;
|
|
249
|
+
displayedRegions: Region[];
|
|
249
250
|
hideHeader: boolean;
|
|
250
251
|
hideHeaderOverview: boolean;
|
|
251
252
|
hideNoTracksActive: boolean;
|
|
@@ -96,10 +96,14 @@ export function stateModelFactory(pluginManager) {
|
|
|
96
96
|
...new Set(self.displayedRegions.map(region => region.assemblyName)),
|
|
97
97
|
];
|
|
98
98
|
},
|
|
99
|
+
get isTopLevelView() {
|
|
100
|
+
const session = getSession(self);
|
|
101
|
+
return session.views.find(r => r.id === self.id);
|
|
102
|
+
},
|
|
99
103
|
get stickyViewHeaders() {
|
|
100
104
|
const session = getSession(self);
|
|
101
105
|
return isSessionWithMultipleViews(session)
|
|
102
|
-
? session.stickyViewHeaders
|
|
106
|
+
? this.isTopLevelView && session.stickyViewHeaders
|
|
103
107
|
: false;
|
|
104
108
|
},
|
|
105
109
|
get rubberbandTop() {
|
|
@@ -222,12 +226,6 @@ export function stateModelFactory(pluginManager) {
|
|
|
222
226
|
return self.tracks.find(t => t.configuration.trackId === id);
|
|
223
227
|
},
|
|
224
228
|
rankSearchResults(results) {
|
|
225
|
-
const openTrackIds = new Set(self.tracks.map(track => track.configuration.trackId));
|
|
226
|
-
for (const result of results) {
|
|
227
|
-
if (openTrackIds.has(result.trackId)) {
|
|
228
|
-
result.updateScore(result.getScore() + 1);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
229
|
return results;
|
|
232
230
|
},
|
|
233
231
|
rewriteOnClicks(trackType, viewMenuActions) {
|
|
@@ -800,14 +798,14 @@ export function stateModelFactory(pluginManager) {
|
|
|
800
798
|
moveTo(start, end) {
|
|
801
799
|
moveTo(self, start, end);
|
|
802
800
|
},
|
|
803
|
-
async navToLocString(input, optAssemblyName) {
|
|
801
|
+
async navToLocString(input, optAssemblyName, grow) {
|
|
804
802
|
const { assemblyNames } = self;
|
|
805
803
|
const { assemblyManager } = getSession(self);
|
|
806
804
|
const assemblyName = optAssemblyName || assemblyNames[0];
|
|
807
805
|
if (assemblyName) {
|
|
808
806
|
await assemblyManager.waitForAssembly(assemblyName);
|
|
809
807
|
}
|
|
810
|
-
return this.navToLocations(parseLocStrings(input, assemblyName, (ref, asm) => assemblyManager.isValidRefName(ref, asm)), assemblyName);
|
|
808
|
+
return this.navToLocations(parseLocStrings(input, assemblyName, (ref, asm) => assemblyManager.isValidRefName(ref, asm)), assemblyName, grow);
|
|
811
809
|
},
|
|
812
810
|
async navToSearchString({ input, assembly, }) {
|
|
813
811
|
await handleSelectedRegion({
|
|
@@ -816,13 +814,18 @@ export function stateModelFactory(pluginManager) {
|
|
|
816
814
|
model: self,
|
|
817
815
|
});
|
|
818
816
|
},
|
|
819
|
-
async navToLocation(parsedLocString, assemblyName) {
|
|
820
|
-
return this.navToLocations([parsedLocString], assemblyName);
|
|
817
|
+
async navToLocation(parsedLocString, assemblyName, grow) {
|
|
818
|
+
return this.navToLocations([parsedLocString], assemblyName, grow);
|
|
821
819
|
},
|
|
822
|
-
async navToLocations(
|
|
820
|
+
async navToLocations(regions, assemblyName, grow) {
|
|
823
821
|
const { assemblyManager } = getSession(self);
|
|
824
822
|
await when(() => self.volatileWidth !== undefined);
|
|
825
|
-
const locations = await generateLocations(
|
|
823
|
+
const locations = await generateLocations({
|
|
824
|
+
regions,
|
|
825
|
+
assemblyManager,
|
|
826
|
+
assemblyName,
|
|
827
|
+
grow,
|
|
828
|
+
});
|
|
826
829
|
if (locations.length === 1) {
|
|
827
830
|
const loc = locations[0];
|
|
828
831
|
const { reversed, parentRegion, start, end } = loc;
|
|
@@ -9,7 +9,12 @@ export declare function makeTicks(start: number, end: number, bpPerPx: number, e
|
|
|
9
9
|
base: number;
|
|
10
10
|
index: number;
|
|
11
11
|
}[];
|
|
12
|
-
export declare function generateLocations(regions
|
|
12
|
+
export declare function generateLocations({ regions, assemblyManager, assemblyName, grow, }: {
|
|
13
|
+
regions: ParsedLocString[];
|
|
14
|
+
assemblyManager: AssemblyManager;
|
|
15
|
+
assemblyName?: string;
|
|
16
|
+
grow?: number;
|
|
17
|
+
}): Promise<{
|
|
13
18
|
assemblyName: string;
|
|
14
19
|
parentRegion: import("@jbrowse/core/assemblyManager/assembly").BasicRegion;
|
|
15
20
|
end?: number | undefined;
|
|
@@ -50,7 +50,7 @@ export function makeTicks(start, end, bpPerPx, emitMajor = true, emitMinor = tru
|
|
|
50
50
|
}
|
|
51
51
|
return ticks;
|
|
52
52
|
}
|
|
53
|
-
export async function generateLocations(regions, assemblyManager, assemblyName) {
|
|
53
|
+
export async function generateLocations({ regions, assemblyManager, assemblyName, grow, }) {
|
|
54
54
|
return Promise.all(regions.map(async (region) => {
|
|
55
55
|
const asmName = region.assemblyName || assemblyName;
|
|
56
56
|
if (!asmName) {
|
|
@@ -73,11 +73,25 @@ export async function generateLocations(regions, assemblyManager, assemblyName)
|
|
|
73
73
|
if (!parentRegion) {
|
|
74
74
|
throw new Error(`Could not find refName ${refName} in ${asmName}`);
|
|
75
75
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
const { start, end } = region;
|
|
77
|
+
if (grow && start && end) {
|
|
78
|
+
const len = end - start;
|
|
79
|
+
const margin = len * grow;
|
|
80
|
+
return {
|
|
81
|
+
...region,
|
|
82
|
+
start: Math.max(0, start - margin),
|
|
83
|
+
end: end + margin,
|
|
84
|
+
assemblyName: asmName,
|
|
85
|
+
parentRegion,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
return {
|
|
90
|
+
...region,
|
|
91
|
+
assemblyName: asmName,
|
|
92
|
+
parentRegion,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
81
95
|
}));
|
|
82
96
|
}
|
|
83
97
|
export function parseLocStrings(input, assemblyName, isValidRefName) {
|
package/esm/searchUtils.js
CHANGED
|
@@ -4,7 +4,7 @@ export async function navToOption({ option, model, assemblyName, }) {
|
|
|
4
4
|
const location = option.getLocation();
|
|
5
5
|
const trackId = option.getTrackId();
|
|
6
6
|
if (location) {
|
|
7
|
-
await model.navToLocString(location, assemblyName);
|
|
7
|
+
await model.navToLocString(location, assemblyName, 0.2);
|
|
8
8
|
if (trackId) {
|
|
9
9
|
model.showTrack(trackId);
|
|
10
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-linear-genome-view",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "JBrowse 2 linear genome view",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"useSrc": "node ../../scripts/useSrc.js"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@jbrowse/core": "^3.
|
|
41
|
+
"@jbrowse/core": "^3.5.0",
|
|
42
42
|
"@mui/icons-material": "^7.0.0",
|
|
43
43
|
"@mui/material": "^7.0.0",
|
|
44
44
|
"@types/file-saver": "^2.0.1",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"access": "public"
|
|
59
59
|
},
|
|
60
60
|
"module": "esm/index.js",
|
|
61
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "8a8aa0aab2229dece106a5715a767e649e2fe92b"
|
|
62
62
|
}
|