@jbrowse/plugin-linear-genome-view 2.7.2 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +18 -11
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js +25 -2
- package/dist/BaseLinearDisplay/models/renderSvg.d.ts +1 -1
- package/dist/BaseLinearDisplay/models/renderSvg.js +2 -1
- package/dist/BasicTrack/configSchema.d.ts +5 -0
- package/dist/FeatureTrack/configSchema.d.ts +5 -0
- package/dist/LaunchLinearGenomeView/index.js +16 -14
- package/dist/LinearBasicDisplay/model.d.ts +3 -1
- package/dist/LinearGenomeView/components/CenterLine.js +1 -1
- package/dist/LinearGenomeView/components/ImportForm.js +26 -74
- package/dist/LinearGenomeView/components/ImportFormRefNameAutocomplete.d.ts +12 -0
- package/dist/LinearGenomeView/components/ImportFormRefNameAutocomplete.js +29 -0
- package/dist/LinearGenomeView/components/OverviewScalebar.js +1 -1
- package/dist/LinearGenomeView/components/SearchBox.js +18 -54
- package/dist/LinearGenomeView/model.d.ts +12 -1
- package/dist/LinearGenomeView/model.js +16 -0
- package/dist/index.d.ts +702 -6
- package/dist/index.js +2 -2
- package/dist/searchUtils.d.ts +26 -0
- package/dist/searchUtils.js +90 -0
- package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +18 -11
- package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +1 -1
- package/esm/BaseLinearDisplay/models/renderSvg.d.ts +1 -1
- package/esm/BaseLinearDisplay/models/renderSvg.js +1 -1
- package/esm/BasicTrack/configSchema.d.ts +5 -0
- package/esm/FeatureTrack/configSchema.d.ts +5 -0
- package/esm/LaunchLinearGenomeView/index.js +16 -14
- package/esm/LinearBasicDisplay/model.d.ts +3 -1
- package/esm/LinearGenomeView/components/CenterLine.js +1 -1
- package/esm/LinearGenomeView/components/ImportForm.js +26 -74
- package/esm/LinearGenomeView/components/ImportFormRefNameAutocomplete.d.ts +12 -0
- package/esm/LinearGenomeView/components/ImportFormRefNameAutocomplete.js +24 -0
- package/esm/LinearGenomeView/components/OverviewScalebar.js +1 -1
- package/esm/LinearGenomeView/components/SearchBox.js +19 -55
- package/esm/LinearGenomeView/model.d.ts +12 -1
- package/esm/LinearGenomeView/model.js +16 -0
- package/esm/index.d.ts +702 -6
- package/esm/index.js +2 -2
- package/esm/searchUtils.d.ts +26 -0
- package/esm/searchUtils.js +79 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -29,6 +29,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
29
29
|
exports.linearBasicDisplayModelFactory = exports.linearBasicDisplayConfigSchemaFactory = exports.SVGRuler = exports.totalHeight = exports.SVGTracks = exports.renderToSvg = exports.SearchBox = exports.RefNameAutocomplete = exports.TooLargeMessage = exports.FeatureDensityMixin = exports.TrackHeightMixin = exports.BaseLinearDisplayComponent = exports.BlockMsg = exports.BaseLinearDisplay = exports.baseLinearDisplayConfigSchema = exports.linearBareDisplayConfigSchemaFactory = void 0;
|
|
30
30
|
const Plugin_1 = __importDefault(require("@jbrowse/core/Plugin"));
|
|
31
31
|
const util_1 = require("@jbrowse/core/util");
|
|
32
|
+
const configuration_1 = require("@jbrowse/core/configuration");
|
|
33
|
+
const mobx_state_tree_1 = require("mobx-state-tree");
|
|
32
34
|
// icons
|
|
33
35
|
const LineStyle_1 = __importDefault(require("@mui/icons-material/LineStyle"));
|
|
34
36
|
// locals
|
|
@@ -39,8 +41,6 @@ const LinearBasicDisplay_1 = __importDefault(require("./LinearBasicDisplay"));
|
|
|
39
41
|
const FeatureTrack_1 = __importDefault(require("./FeatureTrack"));
|
|
40
42
|
const BasicTrack_1 = __importDefault(require("./BasicTrack"));
|
|
41
43
|
const LaunchLinearGenomeView_1 = __importDefault(require("./LaunchLinearGenomeView"));
|
|
42
|
-
const configuration_1 = require("@jbrowse/core/configuration");
|
|
43
|
-
const mobx_state_tree_1 = require("mobx-state-tree");
|
|
44
44
|
class LinearGenomeViewPlugin extends Plugin_1.default {
|
|
45
45
|
constructor() {
|
|
46
46
|
super(...arguments);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import BaseResult from '@jbrowse/core/TextSearch/BaseResults';
|
|
2
|
+
import { Assembly } from '@jbrowse/core/assemblyManager/assembly';
|
|
3
|
+
import { SearchType } from '@jbrowse/core/data_adapters/BaseAdapter';
|
|
4
|
+
import { SearchScope } from '@jbrowse/core/TextSearch/TextSearchManager';
|
|
5
|
+
import { TextSearchManager } from '@jbrowse/core/util';
|
|
6
|
+
import { LinearGenomeViewModel } from './LinearGenomeView';
|
|
7
|
+
export declare function navToOption({ option, model, assemblyName, }: {
|
|
8
|
+
model: LinearGenomeViewModel;
|
|
9
|
+
option: BaseResult;
|
|
10
|
+
assemblyName: string;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
export declare function handleSelectedRegion({ input, model, assembly, }: {
|
|
13
|
+
input: string;
|
|
14
|
+
model: LinearGenomeViewModel;
|
|
15
|
+
assembly: Assembly;
|
|
16
|
+
}): Promise<void>;
|
|
17
|
+
export declare function checkRef(str: string, allRefs: string[]): boolean;
|
|
18
|
+
export declare function fetchResults({ queryString, searchType, searchScope, rankSearchResults, textSearchManager, assembly, }: {
|
|
19
|
+
queryString: string;
|
|
20
|
+
searchScope: SearchScope;
|
|
21
|
+
rankSearchResults: (results: BaseResult[]) => BaseResult[];
|
|
22
|
+
searchType?: SearchType;
|
|
23
|
+
textSearchManager?: TextSearchManager;
|
|
24
|
+
assembly?: Assembly;
|
|
25
|
+
}): Promise<BaseResult[]>;
|
|
26
|
+
export declare function splitLast(str: string, split: string): [string, string];
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.splitLast = exports.fetchResults = exports.checkRef = exports.handleSelectedRegion = exports.navToOption = void 0;
|
|
7
|
+
const util_1 = require("@jbrowse/core/util");
|
|
8
|
+
const BaseResults_1 = __importDefault(require("@jbrowse/core/TextSearch/BaseResults"));
|
|
9
|
+
const util_2 = require("@jbrowse/core/util");
|
|
10
|
+
async function navToOption({ option, model, assemblyName, }) {
|
|
11
|
+
const location = option.getLocation();
|
|
12
|
+
const trackId = option.getTrackId();
|
|
13
|
+
if (location) {
|
|
14
|
+
await model.navToLocString(location, assemblyName);
|
|
15
|
+
if (trackId) {
|
|
16
|
+
model.showTrack(trackId);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.navToOption = navToOption;
|
|
21
|
+
// gets a string as input, or use stored option results from previous query,
|
|
22
|
+
// then re-query and
|
|
23
|
+
// 1) if it has multiple results: pop a dialog
|
|
24
|
+
// 2) if it's a single result navigate to it
|
|
25
|
+
// 3) else assume it's a locstring and navigate to it
|
|
26
|
+
async function handleSelectedRegion({ input, model, assembly, }) {
|
|
27
|
+
const allRefs = (assembly === null || assembly === void 0 ? void 0 : assembly.allRefNamesWithLowerCase) || [];
|
|
28
|
+
const assemblyName = assembly.name;
|
|
29
|
+
if (input.split(' ').every(entry => checkRef(entry, allRefs))) {
|
|
30
|
+
await model.navToLocString(input, assembly.name);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
const searchScope = model.searchScope(assemblyName);
|
|
34
|
+
const { textSearchManager } = (0, util_1.getSession)(model);
|
|
35
|
+
const results = await fetchResults({
|
|
36
|
+
queryString: input,
|
|
37
|
+
searchType: 'exact',
|
|
38
|
+
searchScope,
|
|
39
|
+
rankSearchResults: model.rankSearchResults,
|
|
40
|
+
textSearchManager,
|
|
41
|
+
assembly,
|
|
42
|
+
});
|
|
43
|
+
if (results.length > 1) {
|
|
44
|
+
model.setSearchResults(results, input.toLowerCase(), assemblyName);
|
|
45
|
+
}
|
|
46
|
+
else if (results.length === 1) {
|
|
47
|
+
await navToOption({
|
|
48
|
+
option: results[0],
|
|
49
|
+
model,
|
|
50
|
+
assemblyName,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
await model.navToLocString(input, assemblyName);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.handleSelectedRegion = handleSelectedRegion;
|
|
59
|
+
function checkRef(str, allRefs) {
|
|
60
|
+
const [ref, rest] = splitLast(str, ':');
|
|
61
|
+
return (allRefs.includes(str) ||
|
|
62
|
+
(allRefs.includes(ref) && !Number.isNaN(Number.parseInt(rest, 10))));
|
|
63
|
+
}
|
|
64
|
+
exports.checkRef = checkRef;
|
|
65
|
+
async function fetchResults({ queryString, searchType, searchScope, rankSearchResults, textSearchManager, assembly, }) {
|
|
66
|
+
var _a;
|
|
67
|
+
if (!textSearchManager) {
|
|
68
|
+
console.warn('No text search manager');
|
|
69
|
+
}
|
|
70
|
+
const textSearchResults = await (textSearchManager === null || textSearchManager === void 0 ? void 0 : textSearchManager.search({
|
|
71
|
+
queryString,
|
|
72
|
+
searchType,
|
|
73
|
+
}, searchScope, rankSearchResults));
|
|
74
|
+
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({ label: r }));
|
|
75
|
+
return (0, util_2.dedupe)([...(refNameResults || []), ...(textSearchResults || [])], elt => elt.getId());
|
|
76
|
+
}
|
|
77
|
+
exports.fetchResults = fetchResults;
|
|
78
|
+
// splits on the last instance of a character
|
|
79
|
+
function splitLast(str, split) {
|
|
80
|
+
const lastIndex = str.lastIndexOf(split);
|
|
81
|
+
if (lastIndex === -1) {
|
|
82
|
+
return [str, ''];
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const before = str.slice(0, lastIndex);
|
|
86
|
+
const after = str.slice(lastIndex + 1);
|
|
87
|
+
return [before, after];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.splitLast = splitLast;
|
|
@@ -69,7 +69,11 @@ export declare const BaseLinearDisplay: import("mobx-state-tree").IModelType<{
|
|
|
69
69
|
setMessage(messageText: string): void;
|
|
70
70
|
setRendered(props: {
|
|
71
71
|
reactElement: React.ReactElement<any, string | React.JSXElementConstructor<any>>;
|
|
72
|
-
features: Map<string, Feature>;
|
|
72
|
+
features: Map<string, Feature>; /**
|
|
73
|
+
* #getter
|
|
74
|
+
* a CompositeMap of `featureId -> feature obj` that
|
|
75
|
+
* just looks in all the block data for that feature
|
|
76
|
+
*/
|
|
73
77
|
layout: any;
|
|
74
78
|
maxHeightReached: boolean;
|
|
75
79
|
renderProps: any;
|
|
@@ -128,7 +132,9 @@ export declare const BaseLinearDisplay: import("mobx-state-tree").IModelType<{
|
|
|
128
132
|
message: string | undefined;
|
|
129
133
|
}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
|
|
130
134
|
onHorizontalScroll?: Function | undefined;
|
|
131
|
-
blockState?: Record<string, any> | undefined;
|
|
135
|
+
blockState?: Record<string, any> | undefined; /**
|
|
136
|
+
* #property
|
|
137
|
+
*/
|
|
132
138
|
}>;
|
|
133
139
|
readonly DisplayBlurb: React.FC<{
|
|
134
140
|
model: {
|
|
@@ -136,7 +142,9 @@ export declare const BaseLinearDisplay: import("mobx-state-tree").IModelType<{
|
|
|
136
142
|
type: string;
|
|
137
143
|
rpcDriverName: string | undefined;
|
|
138
144
|
} & import("mobx-state-tree/dist/internal").NonEmptyObject & {
|
|
139
|
-
rendererTypeName: string;
|
|
145
|
+
rendererTypeName: string; /**
|
|
146
|
+
* #getter
|
|
147
|
+
*/
|
|
140
148
|
error: unknown;
|
|
141
149
|
message: string | undefined;
|
|
142
150
|
} & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
|
|
@@ -144,9 +152,7 @@ export declare const BaseLinearDisplay: import("mobx-state-tree").IModelType<{
|
|
|
144
152
|
type: import("mobx-state-tree").ISimpleType<string>;
|
|
145
153
|
rpcDriverName: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
|
|
146
154
|
}, {
|
|
147
|
-
rendererTypeName: string;
|
|
148
|
-
* #getter
|
|
149
|
-
*/
|
|
155
|
+
rendererTypeName: string;
|
|
150
156
|
error: unknown;
|
|
151
157
|
message: string | undefined;
|
|
152
158
|
}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
|
|
@@ -179,10 +185,7 @@ export declare const BaseLinearDisplay: import("mobx-state-tree").IModelType<{
|
|
|
179
185
|
} & {
|
|
180
186
|
readonly currentBytesRequested: number;
|
|
181
187
|
readonly currentFeatureScreenDensity: number;
|
|
182
|
-
readonly maxFeatureScreenDensity: any;
|
|
183
|
-
* #property
|
|
184
|
-
* updated via autorun
|
|
185
|
-
*/
|
|
188
|
+
readonly maxFeatureScreenDensity: any;
|
|
186
189
|
readonly featureDensityStatsReady: boolean;
|
|
187
190
|
readonly maxAllowableBytes: number;
|
|
188
191
|
} & {
|
|
@@ -358,7 +361,11 @@ export declare const BaseLinearDisplay: import("mobx-state-tree").IModelType<{
|
|
|
358
361
|
setMessage(messageText: string): void;
|
|
359
362
|
setRendered(props: {
|
|
360
363
|
reactElement: React.ReactElement<any, string | React.JSXElementConstructor<any>>;
|
|
361
|
-
features: Map<string, Feature>;
|
|
364
|
+
features: Map<string, Feature>; /**
|
|
365
|
+
* #getter
|
|
366
|
+
* a CompositeMap of `featureId -> feature obj` that
|
|
367
|
+
* just looks in all the block data for that feature
|
|
368
|
+
*/
|
|
362
369
|
layout: any;
|
|
363
370
|
maxHeightReached: boolean;
|
|
364
371
|
renderProps: any;
|
|
@@ -10,7 +10,6 @@ import MenuOpenIcon from '@mui/icons-material/MenuOpen';
|
|
|
10
10
|
import { Tooltip } from '../components/BaseLinearDisplay';
|
|
11
11
|
import BlockState from './serverSideRenderedBlock';
|
|
12
12
|
import configSchema from './configSchema';
|
|
13
|
-
import renderBaseLinearDisplaySvg from './renderSvg';
|
|
14
13
|
import TrackHeightMixin from './TrackHeightMixin';
|
|
15
14
|
import FeatureDensityMixin from './FeatureDensityMixin';
|
|
16
15
|
/**
|
|
@@ -290,6 +289,7 @@ function stateModelFactory() {
|
|
|
290
289
|
* #method
|
|
291
290
|
*/
|
|
292
291
|
async renderSvg(opts) {
|
|
292
|
+
const { renderBaseLinearDisplaySvg } = await import('./renderSvg');
|
|
293
293
|
return renderBaseLinearDisplaySvg(self, opts);
|
|
294
294
|
},
|
|
295
295
|
afterAttach() {
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { ThemeOptions } from '@mui/material';
|
|
3
3
|
import { ExportSvgOptions } from '../../LinearGenomeView';
|
|
4
4
|
import { BaseLinearDisplayModel } from './BaseLinearDisplayModel';
|
|
5
|
-
export
|
|
5
|
+
export declare function renderBaseLinearDisplaySvg(self: BaseLinearDisplayModel, opts: ExportSvgOptions & {
|
|
6
6
|
overrideHeight: number;
|
|
7
7
|
theme: ThemeOptions;
|
|
8
8
|
}): Promise<React.JSX.Element>;
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { getContainingView, getViewParams, ReactRendering, } from '@jbrowse/core/util';
|
|
3
3
|
import BlockState, { renderBlockData } from './serverSideRenderedBlock';
|
|
4
4
|
import { getId } from './util';
|
|
5
|
-
export
|
|
5
|
+
export async function renderBaseLinearDisplaySvg(self, opts) {
|
|
6
6
|
const { height, id } = self;
|
|
7
7
|
const { overrideHeight } = opts;
|
|
8
8
|
const view = getContainingView(self);
|
|
@@ -58,6 +58,11 @@ declare const configSchema: (pluginManager: PluginManager) => import("@jbrowse/c
|
|
|
58
58
|
defaultValue: number;
|
|
59
59
|
description: string;
|
|
60
60
|
};
|
|
61
|
+
maxDepth: {
|
|
62
|
+
type: string;
|
|
63
|
+
defaultValue: number;
|
|
64
|
+
description: string;
|
|
65
|
+
};
|
|
61
66
|
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
|
|
62
67
|
formatAbout: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
63
68
|
config: {
|
|
@@ -58,6 +58,11 @@ declare const configSchema: (pluginManager: PluginManager) => import("@jbrowse/c
|
|
|
58
58
|
defaultValue: number;
|
|
59
59
|
description: string;
|
|
60
60
|
};
|
|
61
|
+
maxDepth: {
|
|
62
|
+
type: string;
|
|
63
|
+
defaultValue: number;
|
|
64
|
+
description: string;
|
|
65
|
+
};
|
|
61
66
|
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
|
|
62
67
|
formatAbout: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
63
68
|
config: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { when } from '@jbrowse/core/util';
|
|
2
|
+
import { handleSelectedRegion } from '..//searchUtils';
|
|
2
3
|
export default (pluginManager) => {
|
|
3
4
|
pluginManager.addToExtensionPoint('LaunchView-LinearGenomeView',
|
|
4
5
|
// @ts-expect-error
|
|
@@ -14,21 +15,9 @@ export default (pluginManager) => {
|
|
|
14
15
|
if (!asm) {
|
|
15
16
|
throw new Error(`Assembly "${assembly}" not found when launching linear genome view`);
|
|
16
17
|
}
|
|
17
|
-
await
|
|
18
|
+
await handleSelectedRegion({ input: loc, model: view, assembly: asm });
|
|
18
19
|
const idsNotFound = [];
|
|
19
|
-
tracks.forEach(track =>
|
|
20
|
-
try {
|
|
21
|
-
view.showTrack(track);
|
|
22
|
-
}
|
|
23
|
-
catch (e) {
|
|
24
|
-
if (`${e}`.match('Could not resolve identifier')) {
|
|
25
|
-
idsNotFound.push(track);
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
throw e;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
});
|
|
20
|
+
tracks.forEach(track => tryTrack(view, track, idsNotFound));
|
|
32
21
|
if (idsNotFound.length) {
|
|
33
22
|
throw new Error(`Could not resolve identifiers: ${idsNotFound.join(',')}`);
|
|
34
23
|
}
|
|
@@ -39,3 +28,16 @@ export default (pluginManager) => {
|
|
|
39
28
|
}
|
|
40
29
|
});
|
|
41
30
|
};
|
|
31
|
+
function tryTrack(model, trackId, idsNotFound) {
|
|
32
|
+
try {
|
|
33
|
+
model.showTrack(trackId);
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
if (`${e}`.match('Could not resolve identifier')) {
|
|
37
|
+
idsNotFound.push(trackId);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw e;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -219,7 +219,9 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
219
219
|
featureIdUnderMouse: string | undefined;
|
|
220
220
|
contextMenuFeature: import("@jbrowse/core/util").Feature | undefined;
|
|
221
221
|
} & {
|
|
222
|
-
readonly blockType: "dynamicBlocks" | "staticBlocks";
|
|
222
|
+
readonly blockType: "dynamicBlocks" | "staticBlocks"; /**
|
|
223
|
+
* #getter
|
|
224
|
+
*/
|
|
223
225
|
readonly blockDefinitions: import("@jbrowse/core/util/blockTypes").BlockSet;
|
|
224
226
|
} & {
|
|
225
227
|
readonly renderDelay: number;
|
|
@@ -5,7 +5,7 @@ const useStyles = makeStyles()(theme => ({
|
|
|
5
5
|
centerLineContainer: {
|
|
6
6
|
background: 'transparent',
|
|
7
7
|
height: '100%',
|
|
8
|
-
zIndex: 5,
|
|
8
|
+
zIndex: 5, // above the track but under menu
|
|
9
9
|
position: 'absolute',
|
|
10
10
|
border: `1px ${theme.palette.action.active} dashed`,
|
|
11
11
|
borderTop: 'none',
|
|
@@ -6,9 +6,8 @@ import { Button, FormControl, Container, Grid, CircularProgress, } from '@mui/ma
|
|
|
6
6
|
import { ErrorMessage, AssemblySelector } from '@jbrowse/core/ui';
|
|
7
7
|
// icons
|
|
8
8
|
import CloseIcon from '@mui/icons-material/Close';
|
|
9
|
-
|
|
10
|
-
import
|
|
11
|
-
import { fetchResults, splitLast } from './util';
|
|
9
|
+
import { handleSelectedRegion, navToOption } from '../../searchUtils';
|
|
10
|
+
import ImportFormRefNameAutocomplete from './ImportFormRefNameAutocomplete';
|
|
12
11
|
const useStyles = makeStyles()(theme => ({
|
|
13
12
|
importFormContainer: {
|
|
14
13
|
padding: theme.spacing(2),
|
|
@@ -24,11 +23,10 @@ const LinearGenomeViewImportForm = observer(function ({ model, }) {
|
|
|
24
23
|
var _a;
|
|
25
24
|
const { classes } = useStyles();
|
|
26
25
|
const session = getSession(model);
|
|
27
|
-
const { assemblyNames, assemblyManager
|
|
28
|
-
const {
|
|
26
|
+
const { assemblyNames, assemblyManager } = session;
|
|
27
|
+
const { error } = model;
|
|
29
28
|
const [selectedAsm, setSelectedAsm] = useState(assemblyNames[0]);
|
|
30
29
|
const [option, setOption] = useState();
|
|
31
|
-
const searchScope = model.searchScope(selectedAsm);
|
|
32
30
|
const assembly = assemblyManager.get(selectedAsm);
|
|
33
31
|
const assemblyError = assemblyNames.length
|
|
34
32
|
? assembly === null || assembly === void 0 ? void 0 : assembly.error
|
|
@@ -46,74 +44,37 @@ const LinearGenomeViewImportForm = observer(function ({ model, }) {
|
|
|
46
44
|
useEffect(() => {
|
|
47
45
|
setValue(r0);
|
|
48
46
|
}, [r0, selectedAsm]);
|
|
49
|
-
async function navToOption(option) {
|
|
50
|
-
const location = option.getLocation();
|
|
51
|
-
const trackId = option.getTrackId();
|
|
52
|
-
if (location) {
|
|
53
|
-
await model.navToLocString(location, selectedAsm);
|
|
54
|
-
if (trackId) {
|
|
55
|
-
model.showTrack(trackId);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
// gets a string as input, or use stored option results from previous query,
|
|
60
|
-
// then re-query and
|
|
61
|
-
// 1) if it has multiple results: pop a dialog
|
|
62
|
-
// 2) if it's a single result navigate to it
|
|
63
|
-
// 3) else assume it's a locstring and navigate to it
|
|
64
|
-
async function handleSelectedRegion(input) {
|
|
65
|
-
var _a;
|
|
66
|
-
try {
|
|
67
|
-
if ((option === null || option === void 0 ? void 0 : option.getDisplayString()) === input && option.hasLocation()) {
|
|
68
|
-
await navToOption(option);
|
|
69
|
-
}
|
|
70
|
-
else if ((_a = option === null || option === void 0 ? void 0 : option.results) === null || _a === void 0 ? void 0 : _a.length) {
|
|
71
|
-
model.setSearchResults(option.results, option.getLabel(), selectedAsm);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
const [ref, rest] = splitLast(input, ':');
|
|
75
|
-
const allRefs = (assembly === null || assembly === void 0 ? void 0 : assembly.allRefNamesWithLowerCase) || [];
|
|
76
|
-
if (allRefs.includes(input) ||
|
|
77
|
-
(allRefs.includes(ref) && !Number.isNaN(Number.parseInt(rest, 10)))) {
|
|
78
|
-
await model.navToLocString(input, selectedAsm);
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
const results = await fetchResults({
|
|
82
|
-
queryString: input,
|
|
83
|
-
searchType: 'exact',
|
|
84
|
-
searchScope,
|
|
85
|
-
rankSearchResults,
|
|
86
|
-
textSearchManager,
|
|
87
|
-
assembly,
|
|
88
|
-
});
|
|
89
|
-
if (results.length > 1) {
|
|
90
|
-
model.setSearchResults(results, input.toLowerCase(), selectedAsm);
|
|
91
|
-
}
|
|
92
|
-
else if (results.length === 1) {
|
|
93
|
-
await navToOption(results[0]);
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
await model.navToLocString(input, selectedAsm);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
catch (e) {
|
|
102
|
-
console.error(e);
|
|
103
|
-
session.notify(`${e}`, 'warning');
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
47
|
// implementation notes:
|
|
107
48
|
// having this wrapped in a form allows intuitive use of enter key to submit
|
|
108
49
|
return (React.createElement("div", { className: classes.container },
|
|
109
50
|
displayError ? React.createElement(ErrorMessage, { error: displayError }) : null,
|
|
110
51
|
React.createElement(Container, { className: classes.importFormContainer },
|
|
111
52
|
React.createElement("form", { onSubmit: async (event) => {
|
|
53
|
+
var _a;
|
|
112
54
|
event.preventDefault();
|
|
113
55
|
model.setError(undefined);
|
|
114
56
|
if (value) {
|
|
115
57
|
// has it's own error handling
|
|
116
|
-
|
|
58
|
+
try {
|
|
59
|
+
if ((option === null || option === void 0 ? void 0 : option.getDisplayString()) === value &&
|
|
60
|
+
option.hasLocation()) {
|
|
61
|
+
await navToOption({
|
|
62
|
+
option,
|
|
63
|
+
model,
|
|
64
|
+
assemblyName: selectedAsm,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else if ((_a = option === null || option === void 0 ? void 0 : option.results) === null || _a === void 0 ? void 0 : _a.length) {
|
|
68
|
+
model.setSearchResults(option.results, option.getLabel(), selectedAsm);
|
|
69
|
+
}
|
|
70
|
+
else if (assembly) {
|
|
71
|
+
await handleSelectedRegion({ input: value, assembly, model });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
console.error(e);
|
|
76
|
+
session.notify(`${e}`, 'warning');
|
|
77
|
+
}
|
|
117
78
|
}
|
|
118
79
|
} },
|
|
119
80
|
React.createElement(Grid, { container: true, spacing: 1, justifyContent: "center", alignItems: "center" },
|
|
@@ -121,16 +82,7 @@ const LinearGenomeViewImportForm = observer(function ({ model, }) {
|
|
|
121
82
|
React.createElement(FormControl, null,
|
|
122
83
|
React.createElement(AssemblySelector, { onChange: val => setSelectedAsm(val), localStorageKey: "lgv", session: session, selected: selectedAsm }))),
|
|
123
84
|
React.createElement(Grid, { item: true }, selectedAsm ? (assemblyError ? (React.createElement(CloseIcon, { style: { color: 'red' } })) : assemblyLoaded ? (React.createElement(FormControl, null,
|
|
124
|
-
React.createElement(
|
|
125
|
-
queryString,
|
|
126
|
-
assembly,
|
|
127
|
-
textSearchManager,
|
|
128
|
-
rankSearchResults,
|
|
129
|
-
searchScope,
|
|
130
|
-
}), model: model, assemblyName: selectedAsm, value: value, minWidth: 270, onChange: str => setValue(str), onSelect: val => setOption(val), TextFieldProps: {
|
|
131
|
-
variant: 'outlined',
|
|
132
|
-
helperText: 'Enter sequence name, feature name, or location',
|
|
133
|
-
} }))) : (React.createElement(CircularProgress, { size: 20, disableShrink: true }))) : null),
|
|
85
|
+
React.createElement(ImportFormRefNameAutocomplete, { value: value, setValue: setValue, selectedAsm: selectedAsm, setOption: setOption, model: model }))) : (React.createElement(CircularProgress, { size: 20, disableShrink: true }))) : null),
|
|
134
86
|
React.createElement(Grid, { item: true },
|
|
135
87
|
React.createElement(FormControl, null,
|
|
136
88
|
React.createElement(Button, { type: "submit", disabled: !value, className: classes.button, variant: "contained", color: "primary" }, "Open")),
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import BaseResult from '@jbrowse/core/TextSearch/BaseResults';
|
|
3
|
+
import { LinearGenomeViewModel } from '..';
|
|
4
|
+
type LGV = LinearGenomeViewModel;
|
|
5
|
+
declare const ImportFormRefNameAutocomplete: ({ model, selectedAsm, value, setValue, setOption, }: {
|
|
6
|
+
value: string;
|
|
7
|
+
setValue: (arg: string) => void;
|
|
8
|
+
model: LGV;
|
|
9
|
+
selectedAsm: string;
|
|
10
|
+
setOption: (arg: BaseResult) => void;
|
|
11
|
+
}) => React.JSX.Element;
|
|
12
|
+
export default ImportFormRefNameAutocomplete;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { observer } from 'mobx-react';
|
|
3
|
+
import { getSession } from '@jbrowse/core/util';
|
|
4
|
+
// locals
|
|
5
|
+
import RefNameAutocomplete from './RefNameAutocomplete';
|
|
6
|
+
import { fetchResults } from './util';
|
|
7
|
+
const ImportFormRefNameAutocomplete = observer(function ({ model, selectedAsm, value, setValue, setOption, }) {
|
|
8
|
+
const session = getSession(model);
|
|
9
|
+
const { assemblyManager, textSearchManager } = session;
|
|
10
|
+
const { rankSearchResults } = model;
|
|
11
|
+
const searchScope = model.searchScope(selectedAsm);
|
|
12
|
+
const assembly = assemblyManager.get(selectedAsm);
|
|
13
|
+
return (React.createElement(RefNameAutocomplete, { fetchResults: queryString => fetchResults({
|
|
14
|
+
queryString,
|
|
15
|
+
assembly,
|
|
16
|
+
textSearchManager,
|
|
17
|
+
rankSearchResults,
|
|
18
|
+
searchScope,
|
|
19
|
+
}), model: model, assemblyName: selectedAsm, value: value, minWidth: 270, onChange: str => setValue(str), onSelect: val => setOption(val), TextFieldProps: {
|
|
20
|
+
variant: 'outlined',
|
|
21
|
+
helperText: 'Enter sequence name, feature name, or location',
|
|
22
|
+
} }));
|
|
23
|
+
});
|
|
24
|
+
export default ImportFormRefNameAutocomplete;
|
|
@@ -195,7 +195,7 @@ const OverviewScalebar = observer(function ({ model, children, }) {
|
|
|
195
195
|
overview.showAllRegions();
|
|
196
196
|
return overview;
|
|
197
197
|
}, [
|
|
198
|
-
JSON.stringify(displayedRegions),
|
|
198
|
+
JSON.stringify(displayedRegions), // eslint-disable-line react-hooks/exhaustive-deps
|
|
199
199
|
model.minimumBlockWidth,
|
|
200
200
|
modWidth,
|
|
201
201
|
displayedRegions,
|
|
@@ -5,8 +5,9 @@ import { makeStyles } from 'tss-react/mui';
|
|
|
5
5
|
import { getSession } from '@jbrowse/core/util';
|
|
6
6
|
// locals
|
|
7
7
|
import RefNameAutocomplete from './RefNameAutocomplete';
|
|
8
|
-
import { fetchResults
|
|
8
|
+
import { fetchResults } from './util';
|
|
9
9
|
import { SPACING, WIDGET_HEIGHT } from '..';
|
|
10
|
+
import { handleSelectedRegion, navToOption } from '../../searchUtils';
|
|
10
11
|
const useStyles = makeStyles()(() => ({
|
|
11
12
|
headerRefName: {
|
|
12
13
|
minWidth: 100,
|
|
@@ -21,65 +22,28 @@ const SearchBox = observer(function ({ model, showHelp, }) {
|
|
|
21
22
|
const assemblyName = assemblyNames[0];
|
|
22
23
|
const assembly = assemblyManager.get(assemblyName);
|
|
23
24
|
const searchScope = model.searchScope(assemblyName);
|
|
24
|
-
async
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (trackId) {
|
|
30
|
-
model.showTrack(trackId);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
// gets a string as input, or use stored option results from previous query,
|
|
35
|
-
// then re-query and
|
|
36
|
-
// 1) if it has multiple results: pop a dialog
|
|
37
|
-
// 2) if it's a single result navigate to it
|
|
38
|
-
// 3) else assume it's a locstring and navigate to it
|
|
39
|
-
async function handleSelectedRegion(option) {
|
|
40
|
-
var _a;
|
|
41
|
-
try {
|
|
42
|
-
if (option.hasLocation()) {
|
|
43
|
-
await navToOption(option);
|
|
44
|
-
}
|
|
45
|
-
else if ((_a = option.results) === null || _a === void 0 ? void 0 : _a.length) {
|
|
46
|
-
model.setSearchResults(option.results, option.getLabel());
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
const input = option.getLabel();
|
|
50
|
-
const [ref, rest] = splitLast(input, ':');
|
|
51
|
-
const allRefs = (assembly === null || assembly === void 0 ? void 0 : assembly.allRefNamesWithLowerCase) || [];
|
|
52
|
-
if (allRefs.includes(input) ||
|
|
53
|
-
(allRefs.includes(ref) && !Number.isNaN(Number.parseInt(rest, 10)))) {
|
|
54
|
-
await model.navToLocString(input, assemblyName);
|
|
25
|
+
return (React.createElement(RefNameAutocomplete, { showHelp: showHelp, onSelect: async (option) => {
|
|
26
|
+
var _a;
|
|
27
|
+
try {
|
|
28
|
+
if (option.hasLocation()) {
|
|
29
|
+
await navToOption({ option, model, assemblyName });
|
|
55
30
|
}
|
|
56
|
-
else {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
textSearchManager,
|
|
31
|
+
else if ((_a = option.results) === null || _a === void 0 ? void 0 : _a.length) {
|
|
32
|
+
model.setSearchResults(option.results, option.getLabel());
|
|
33
|
+
}
|
|
34
|
+
else if (assembly) {
|
|
35
|
+
await handleSelectedRegion({
|
|
36
|
+
input: option.getLabel(),
|
|
63
37
|
assembly,
|
|
38
|
+
model,
|
|
64
39
|
});
|
|
65
|
-
if (results.length > 1) {
|
|
66
|
-
model.setSearchResults(results, input.toLowerCase());
|
|
67
|
-
}
|
|
68
|
-
else if (results.length === 1) {
|
|
69
|
-
await navToOption(results[0]);
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
await model.navToLocString(input, assemblyName);
|
|
73
|
-
}
|
|
74
40
|
}
|
|
75
41
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return (React.createElement(RefNameAutocomplete, { showHelp: showHelp, onSelect: handleSelectedRegion, assemblyName: assemblyName, fetchResults: queryString => fetchResults({
|
|
42
|
+
catch (e) {
|
|
43
|
+
console.error(e);
|
|
44
|
+
getSession(model).notify(`${e}`, 'warning');
|
|
45
|
+
}
|
|
46
|
+
}, assemblyName: assemblyName, fetchResults: queryString => fetchResults({
|
|
83
47
|
queryString,
|
|
84
48
|
searchScope,
|
|
85
49
|
rankSearchResults,
|
|
@@ -6,6 +6,7 @@ import BaseResult from '@jbrowse/core/TextSearch/BaseResults';
|
|
|
6
6
|
import { BlockSet, BaseBlock } from '@jbrowse/core/util/blockTypes';
|
|
7
7
|
import { Instance } from 'mobx-state-tree';
|
|
8
8
|
import PluginManager from '@jbrowse/core/PluginManager';
|
|
9
|
+
import { Assembly } from '@jbrowse/core/assemblyManager/assembly';
|
|
9
10
|
export interface BpOffset {
|
|
10
11
|
refName?: string;
|
|
11
12
|
index: number;
|
|
@@ -292,7 +293,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
292
293
|
/**
|
|
293
294
|
* #action
|
|
294
295
|
*/
|
|
295
|
-
toggleTrack(trackId: string):
|
|
296
|
+
toggleTrack(trackId: string): boolean;
|
|
296
297
|
/**
|
|
297
298
|
* #action
|
|
298
299
|
*/
|
|
@@ -463,6 +464,16 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
463
464
|
* navigating to the locstring
|
|
464
465
|
*/
|
|
465
466
|
navToLocString(input: string, optAssemblyName?: string): Promise<void>;
|
|
467
|
+
/**
|
|
468
|
+
* #action
|
|
469
|
+
* Performs a text index search, and navigates to it immediately if a
|
|
470
|
+
* single result is returned. Will pop up a search dialog if multiple
|
|
471
|
+
* results are returned
|
|
472
|
+
*/
|
|
473
|
+
navToSearchString({ input, assembly, }: {
|
|
474
|
+
input: string;
|
|
475
|
+
assembly: Assembly;
|
|
476
|
+
}): Promise<void>;
|
|
466
477
|
/**
|
|
467
478
|
* #action
|
|
468
479
|
* Similar to `navToLocString`, but accepts parsed location objects
|