@jbrowse/plugin-linear-genome-view 2.10.1 → 2.10.3
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/components/BlockMsg.d.ts +2 -4
- package/dist/BaseLinearDisplay/components/BlockMsg.js +2 -3
- package/dist/BaseLinearDisplay/components/LinearBlocks.js +2 -2
- package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.d.ts +10 -2
- package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +40 -4
- package/dist/BaseLinearDisplay/components/TooLargeMessage.js +5 -4
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +18 -4
- package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +9 -2
- package/dist/BaseLinearDisplay/models/util.js +4 -2
- package/dist/LaunchLinearGenomeView/index.js +16 -2
- package/dist/LinearBareDisplay/model.d.ts +18 -4
- package/dist/LinearBasicDisplay/model.d.ts +32 -24
- package/dist/LinearBasicDisplay/model.js +2 -2
- package/dist/LinearGenomeView/components/ExportSvgDialog.d.ts +1 -1
- package/dist/LinearGenomeView/components/ExportSvgDialog.js +2 -2
- package/dist/LinearGenomeView/components/Highlight.d.ts +7 -0
- package/dist/LinearGenomeView/components/Highlight.js +122 -0
- package/dist/LinearGenomeView/components/OverviewHighlight.d.ts +9 -0
- package/dist/LinearGenomeView/components/OverviewHighlight.js +53 -0
- package/dist/LinearGenomeView/components/OverviewScalebar.js +3 -1
- package/dist/LinearGenomeView/components/RefNameAutocomplete/EndAdornment.js +1 -1
- package/dist/LinearGenomeView/components/RefNameAutocomplete/index.js +9 -10
- package/dist/LinearGenomeView/components/TracksContainer.js +2 -0
- package/dist/LinearGenomeView/model.d.ts +10 -5
- package/dist/LinearGenomeView/model.js +26 -15
- package/dist/LinearGenomeView/util.d.ts +1 -1
- package/dist/index.d.ts +96 -42
- package/esm/BaseLinearDisplay/components/BlockMsg.d.ts +2 -4
- package/esm/BaseLinearDisplay/components/BlockMsg.js +3 -4
- package/esm/BaseLinearDisplay/components/LinearBlocks.js +2 -2
- package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.d.ts +10 -2
- package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +17 -4
- package/esm/BaseLinearDisplay/components/TooLargeMessage.js +5 -4
- package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +18 -4
- package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +9 -2
- package/esm/BaseLinearDisplay/models/util.js +4 -2
- package/esm/LaunchLinearGenomeView/index.js +17 -3
- package/esm/LinearBareDisplay/model.d.ts +18 -4
- package/esm/LinearBasicDisplay/model.d.ts +32 -24
- package/esm/LinearBasicDisplay/model.js +2 -2
- package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +1 -1
- package/esm/LinearGenomeView/components/ExportSvgDialog.js +1 -1
- package/esm/LinearGenomeView/components/Highlight.d.ts +7 -0
- package/esm/LinearGenomeView/components/Highlight.js +94 -0
- package/esm/LinearGenomeView/components/OverviewHighlight.d.ts +9 -0
- package/esm/LinearGenomeView/components/OverviewHighlight.js +48 -0
- package/esm/LinearGenomeView/components/OverviewScalebar.js +3 -1
- package/esm/LinearGenomeView/components/RefNameAutocomplete/EndAdornment.js +1 -1
- package/esm/LinearGenomeView/components/RefNameAutocomplete/index.js +10 -11
- package/esm/LinearGenomeView/components/TracksContainer.js +2 -0
- package/esm/LinearGenomeView/model.d.ts +10 -5
- package/esm/LinearGenomeView/model.js +26 -15
- package/esm/LinearGenomeView/util.d.ts +1 -1
- package/esm/index.d.ts +96 -42
- package/package.json +2 -2
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { lazy } from 'react';
|
|
2
|
+
import { Tooltip, IconButton } from '@mui/material';
|
|
2
3
|
import { makeStyles } from 'tss-react/mui';
|
|
3
4
|
import { observer } from 'mobx-react';
|
|
4
5
|
import { getParent } from 'mobx-state-tree';
|
|
5
6
|
import { LoadingEllipses } from '@jbrowse/core/ui';
|
|
7
|
+
import { getSession } from '@jbrowse/core/util';
|
|
6
8
|
// icons
|
|
7
9
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
|
10
|
+
import ReportIcon from '@mui/icons-material/Report';
|
|
8
11
|
// locals
|
|
9
12
|
import BlockMsg from './BlockMsg';
|
|
13
|
+
const ErrorMessageStackTraceDialog = lazy(() => import('@jbrowse/core/ui/ErrorMessageStackTraceDialog'));
|
|
10
14
|
const useStyles = makeStyles()(theme => {
|
|
11
15
|
const bg = theme.palette.action.disabledBackground;
|
|
12
16
|
return {
|
|
@@ -18,11 +22,9 @@ const useStyles = makeStyles()(theme => {
|
|
|
18
22
|
},
|
|
19
23
|
};
|
|
20
24
|
});
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
25
|
const LoadingMessage = observer(({ model }) => {
|
|
23
26
|
const { classes } = useStyles();
|
|
24
27
|
const { status: blockStatus } = model;
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
28
|
const { message: displayStatus } = getParent(model, 2);
|
|
27
29
|
const status = displayStatus || blockStatus;
|
|
28
30
|
return (React.createElement("div", { className: classes.loading },
|
|
@@ -30,7 +32,18 @@ const LoadingMessage = observer(({ model }) => {
|
|
|
30
32
|
});
|
|
31
33
|
const ServerSideRenderedBlockContent = observer(function ({ model, }) {
|
|
32
34
|
if (model.error) {
|
|
33
|
-
return (React.createElement(BlockMsg, { message: `${model.error}`, severity: "error",
|
|
35
|
+
return (React.createElement(BlockMsg, { message: `${model.error}`, severity: "error", action: React.createElement(React.Fragment, null,
|
|
36
|
+
React.createElement(Tooltip, { title: "Reload track" },
|
|
37
|
+
React.createElement(IconButton, { "data-testid": "reload_button", onClick: () => model.reload() },
|
|
38
|
+
React.createElement(RefreshIcon, null))),
|
|
39
|
+
React.createElement(Tooltip, { title: "Show stack trace" },
|
|
40
|
+
React.createElement(IconButton, { onClick: () => {
|
|
41
|
+
getSession(model).queueDialog(onClose => [
|
|
42
|
+
ErrorMessageStackTraceDialog,
|
|
43
|
+
{ onClose, error: model.error },
|
|
44
|
+
]);
|
|
45
|
+
} },
|
|
46
|
+
React.createElement(ReportIcon, null)))) }));
|
|
34
47
|
}
|
|
35
48
|
else if (model.message) {
|
|
36
49
|
// the message can be a fully rendered react component, e.g. the region too large message
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
// locals
|
|
3
3
|
import BlockMsg from '../components/BlockMsg';
|
|
4
|
+
import { Button } from '@mui/material';
|
|
4
5
|
function TooLargeMessage({ model, }) {
|
|
5
6
|
const { regionTooLargeReason } = model;
|
|
6
|
-
return (React.createElement(BlockMsg, { severity: "warning", action: () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
return (React.createElement(BlockMsg, { severity: "warning", action: React.createElement(Button, { onClick: () => {
|
|
8
|
+
model.setFeatureDensityStatsLimit(model.featureDensityStats);
|
|
9
|
+
model.reload();
|
|
10
|
+
} }, "Force load"), message: [
|
|
10
11
|
regionTooLargeReason,
|
|
11
12
|
'Zoom in to see features or force load (may be slow)',
|
|
12
13
|
]
|
|
@@ -58,8 +58,15 @@ export declare const BaseLinearDisplay: import("mobx-state-tree").IModelType<{
|
|
|
58
58
|
message: string | undefined;
|
|
59
59
|
maxHeightReached: boolean;
|
|
60
60
|
ReactComponent: ({ model, }: {
|
|
61
|
-
model:
|
|
62
|
-
|
|
61
|
+
model: {
|
|
62
|
+
error?: unknown;
|
|
63
|
+
reload: () => void;
|
|
64
|
+
message: React.ReactNode;
|
|
65
|
+
filled?: boolean | undefined;
|
|
66
|
+
status?: string | undefined;
|
|
67
|
+
reactElement?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | undefined;
|
|
68
|
+
};
|
|
69
|
+
}) => React.JSX.Element | undefined;
|
|
63
70
|
renderProps: any;
|
|
64
71
|
} & {
|
|
65
72
|
doReload(): void;
|
|
@@ -342,8 +349,15 @@ export declare const BaseLinearDisplay: import("mobx-state-tree").IModelType<{
|
|
|
342
349
|
message: string | undefined;
|
|
343
350
|
maxHeightReached: boolean;
|
|
344
351
|
ReactComponent: ({ model, }: {
|
|
345
|
-
model:
|
|
346
|
-
|
|
352
|
+
model: {
|
|
353
|
+
error?: unknown;
|
|
354
|
+
reload: () => void;
|
|
355
|
+
message: React.ReactNode;
|
|
356
|
+
filled?: boolean | undefined;
|
|
357
|
+
status?: string | undefined;
|
|
358
|
+
reactElement?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | undefined;
|
|
359
|
+
};
|
|
360
|
+
}) => React.JSX.Element | undefined;
|
|
347
361
|
renderProps: any;
|
|
348
362
|
} & {
|
|
349
363
|
doReload(): void;
|
|
@@ -28,8 +28,15 @@ declare const blockState: import("mobx-state-tree").IModelType<{
|
|
|
28
28
|
message: string | undefined;
|
|
29
29
|
maxHeightReached: boolean;
|
|
30
30
|
ReactComponent: ({ model, }: {
|
|
31
|
-
model:
|
|
32
|
-
|
|
31
|
+
model: {
|
|
32
|
+
error?: unknown;
|
|
33
|
+
reload: () => void;
|
|
34
|
+
message: React.ReactNode;
|
|
35
|
+
filled?: boolean | undefined;
|
|
36
|
+
status?: string | undefined;
|
|
37
|
+
reactElement?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | undefined;
|
|
38
|
+
};
|
|
39
|
+
}) => React.JSX.Element | undefined;
|
|
33
40
|
renderProps: any;
|
|
34
41
|
} & {
|
|
35
42
|
doReload(): void;
|
|
@@ -16,8 +16,10 @@ export function getDisplayStr(totalBytes) {
|
|
|
16
16
|
}
|
|
17
17
|
// stabilize clipid under test for snapshot
|
|
18
18
|
export function getId(id, index) {
|
|
19
|
-
const
|
|
20
|
-
return
|
|
19
|
+
const notJest = typeof jest === 'undefined';
|
|
20
|
+
return ['clip', notJest ? id : 'jest', index, notJest ? Math.random() : '']
|
|
21
|
+
.filter(f => !!f)
|
|
22
|
+
.join('-');
|
|
21
23
|
}
|
|
22
24
|
export async function getFeatureDensityStatsPre(self) {
|
|
23
25
|
const view = getContainingView(self);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { when } from '@jbrowse/core/util';
|
|
2
|
-
import { handleSelectedRegion } from '
|
|
1
|
+
import { when, parseLocString, } from '@jbrowse/core/util';
|
|
2
|
+
import { handleSelectedRegion } from '../searchUtils';
|
|
3
3
|
export default (pluginManager) => {
|
|
4
4
|
pluginManager.addToExtensionPoint('LaunchView-LinearGenomeView',
|
|
5
5
|
// @ts-expect-error
|
|
6
|
-
async ({ session, assembly, loc, tracks = [], }) => {
|
|
6
|
+
async ({ session, assembly, loc, tracks = [], tracklist, nav, highlight, }) => {
|
|
7
7
|
try {
|
|
8
8
|
const { assemblyManager } = session;
|
|
9
|
+
const { isValidRefName } = assemblyManager;
|
|
9
10
|
const view = session.addView('LinearGenomeView', {});
|
|
10
11
|
await when(() => !!view.volatileWidth);
|
|
11
12
|
if (!assembly) {
|
|
@@ -15,6 +16,19 @@ export default (pluginManager) => {
|
|
|
15
16
|
if (!asm) {
|
|
16
17
|
throw new Error(`Assembly "${assembly}" not found when launching linear genome view`);
|
|
17
18
|
}
|
|
19
|
+
if (tracklist) {
|
|
20
|
+
view.activateTrackSelector();
|
|
21
|
+
}
|
|
22
|
+
if (nav !== undefined) {
|
|
23
|
+
view.setHideHeader(!nav);
|
|
24
|
+
}
|
|
25
|
+
if (highlight !== undefined) {
|
|
26
|
+
const location = parseLocString(highlight, refName => isValidRefName(refName, assembly));
|
|
27
|
+
if ((location === null || location === void 0 ? void 0 : location.start) !== undefined && (location === null || location === void 0 ? void 0 : location.end) !== undefined) {
|
|
28
|
+
location.assemblyName = assembly;
|
|
29
|
+
view.setHighlight(location);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
18
32
|
await handleSelectedRegion({ input: loc, model: view, assembly: asm });
|
|
19
33
|
const idsNotFound = [];
|
|
20
34
|
tracks.forEach(track => tryTrack(view, track, idsNotFound));
|
|
@@ -42,8 +42,15 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
|
|
|
42
42
|
message: string | undefined;
|
|
43
43
|
maxHeightReached: boolean;
|
|
44
44
|
ReactComponent: ({ model, }: {
|
|
45
|
-
model:
|
|
46
|
-
|
|
45
|
+
model: {
|
|
46
|
+
error?: unknown;
|
|
47
|
+
reload: () => void;
|
|
48
|
+
message: import("react").ReactNode;
|
|
49
|
+
filled?: boolean | undefined;
|
|
50
|
+
status?: string | undefined;
|
|
51
|
+
reactElement?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
52
|
+
};
|
|
53
|
+
}) => import("react").JSX.Element | undefined;
|
|
47
54
|
renderProps: any;
|
|
48
55
|
} & {
|
|
49
56
|
doReload(): void;
|
|
@@ -267,8 +274,15 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
|
|
|
267
274
|
message: string | undefined;
|
|
268
275
|
maxHeightReached: boolean;
|
|
269
276
|
ReactComponent: ({ model, }: {
|
|
270
|
-
model:
|
|
271
|
-
|
|
277
|
+
model: {
|
|
278
|
+
error?: unknown;
|
|
279
|
+
reload: () => void;
|
|
280
|
+
message: import("react").ReactNode;
|
|
281
|
+
filled?: boolean | undefined;
|
|
282
|
+
status?: string | undefined;
|
|
283
|
+
reactElement?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
284
|
+
};
|
|
285
|
+
}) => import("react").JSX.Element | undefined;
|
|
272
286
|
renderProps: any;
|
|
273
287
|
} & {
|
|
274
288
|
doReload(): void;
|
|
@@ -37,9 +37,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
37
37
|
isLeftEndOfDisplayedRegion: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
38
38
|
isRightEndOfDisplayedRegion: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
39
39
|
}, {
|
|
40
|
-
renderInProgress: AbortController | undefined;
|
|
41
|
-
* #property
|
|
42
|
-
*/
|
|
40
|
+
renderInProgress: AbortController | undefined;
|
|
43
41
|
filled: boolean;
|
|
44
42
|
reactElement: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
45
43
|
features: Map<string, import("@jbrowse/core/util").Feature> | undefined;
|
|
@@ -49,8 +47,17 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
49
47
|
message: string | undefined;
|
|
50
48
|
maxHeightReached: boolean;
|
|
51
49
|
ReactComponent: ({ model, }: {
|
|
52
|
-
model:
|
|
53
|
-
|
|
50
|
+
model: {
|
|
51
|
+
error?: unknown;
|
|
52
|
+
reload: () => void;
|
|
53
|
+
message: import("react").ReactNode;
|
|
54
|
+
filled?: boolean | undefined;
|
|
55
|
+
status?: string | undefined; /**
|
|
56
|
+
* #getter
|
|
57
|
+
*/
|
|
58
|
+
reactElement?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
59
|
+
};
|
|
60
|
+
}) => import("react").JSX.Element | undefined;
|
|
54
61
|
renderProps: any;
|
|
55
62
|
} & {
|
|
56
63
|
doReload(): void;
|
|
@@ -159,12 +166,11 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
159
166
|
}, {
|
|
160
167
|
rendererTypeName: string;
|
|
161
168
|
error: unknown;
|
|
162
|
-
message: string | undefined;
|
|
169
|
+
message: string | undefined; /**
|
|
170
|
+
* #action
|
|
171
|
+
*/
|
|
163
172
|
}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
|
|
164
173
|
}> | null;
|
|
165
|
-
/**
|
|
166
|
-
* #action
|
|
167
|
-
*/
|
|
168
174
|
readonly adapterConfig: any;
|
|
169
175
|
readonly parentTrack: any;
|
|
170
176
|
renderProps(): any;
|
|
@@ -193,9 +199,6 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
193
199
|
} & {
|
|
194
200
|
readonly currentBytesRequested: number;
|
|
195
201
|
readonly currentFeatureScreenDensity: number;
|
|
196
|
-
/**
|
|
197
|
-
* #getter
|
|
198
|
-
*/
|
|
199
202
|
readonly maxFeatureScreenDensity: any;
|
|
200
203
|
readonly featureDensityStatsReady: boolean;
|
|
201
204
|
readonly maxAllowableBytes: number;
|
|
@@ -205,7 +208,9 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
205
208
|
setCurrStatsBpPerPx(n: number): void;
|
|
206
209
|
setFeatureDensityStatsLimit(stats?: import("@jbrowse/core/data_adapters/BaseAdapter").FeatureDensityStats | undefined): void;
|
|
207
210
|
getFeatureDensityStats(): Promise<import("@jbrowse/core/data_adapters/BaseAdapter").FeatureDensityStats>;
|
|
208
|
-
setFeatureDensityStatsP(arg: any): void;
|
|
211
|
+
setFeatureDensityStatsP(arg: any): void; /**
|
|
212
|
+
* #method
|
|
213
|
+
*/
|
|
209
214
|
setFeatureDensityStats(featureDensityStats?: import("@jbrowse/core/data_adapters/BaseAdapter").FeatureDensityStats | undefined): void;
|
|
210
215
|
clearFeatureDensityStats(): void;
|
|
211
216
|
} & {
|
|
@@ -275,9 +280,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
275
280
|
readonly rendererConfig: {
|
|
276
281
|
[x: string]: any;
|
|
277
282
|
} & import("mobx-state-tree/dist/internal").NonEmptyObject & {
|
|
278
|
-
setSubschema(slotName: string, data: unknown): any;
|
|
279
|
-
* #property
|
|
280
|
-
*/
|
|
283
|
+
setSubschema(slotName: string, data: unknown): any;
|
|
281
284
|
} & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>;
|
|
282
285
|
} & {
|
|
283
286
|
/**
|
|
@@ -304,9 +307,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
304
307
|
config: {
|
|
305
308
|
[x: string]: any;
|
|
306
309
|
} & import("mobx-state-tree/dist/internal").NonEmptyObject & {
|
|
307
|
-
setSubschema(slotName: string, data: unknown): any;
|
|
308
|
-
* #property
|
|
309
|
-
*/
|
|
310
|
+
setSubschema(slotName: string, data: unknown): any;
|
|
310
311
|
} & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>;
|
|
311
312
|
};
|
|
312
313
|
/**
|
|
@@ -341,9 +342,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
341
342
|
isLeftEndOfDisplayedRegion: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
342
343
|
isRightEndOfDisplayedRegion: import("mobx-state-tree").IType<boolean | undefined, boolean, boolean>;
|
|
343
344
|
}, {
|
|
344
|
-
renderInProgress: AbortController | undefined;
|
|
345
|
-
* #property
|
|
346
|
-
*/
|
|
345
|
+
renderInProgress: AbortController | undefined;
|
|
347
346
|
filled: boolean;
|
|
348
347
|
reactElement: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
349
348
|
features: Map<string, import("@jbrowse/core/util").Feature> | undefined;
|
|
@@ -353,8 +352,17 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
353
352
|
message: string | undefined;
|
|
354
353
|
maxHeightReached: boolean;
|
|
355
354
|
ReactComponent: ({ model, }: {
|
|
356
|
-
model:
|
|
357
|
-
|
|
355
|
+
model: {
|
|
356
|
+
error?: unknown;
|
|
357
|
+
reload: () => void;
|
|
358
|
+
message: import("react").ReactNode;
|
|
359
|
+
filled?: boolean | undefined;
|
|
360
|
+
status?: string | undefined; /**
|
|
361
|
+
* #getter
|
|
362
|
+
*/
|
|
363
|
+
reactElement?: import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>> | undefined;
|
|
364
|
+
};
|
|
365
|
+
}) => import("react").JSX.Element | undefined;
|
|
358
366
|
renderProps: any;
|
|
359
367
|
} & {
|
|
360
368
|
doReload(): void;
|
|
@@ -6,7 +6,7 @@ import { types, getEnv } from 'mobx-state-tree';
|
|
|
6
6
|
import VisibilityIcon from '@mui/icons-material/Visibility';
|
|
7
7
|
// locals
|
|
8
8
|
import { BaseLinearDisplay } from '../BaseLinearDisplay';
|
|
9
|
-
const
|
|
9
|
+
const SetMaxHeightDialog = lazy(() => import('./components/SetMaxHeight'));
|
|
10
10
|
/**
|
|
11
11
|
* #stateModel LinearBasicDisplay
|
|
12
12
|
* #category display
|
|
@@ -174,7 +174,7 @@ function stateModelFactory(configSchema) {
|
|
|
174
174
|
label: 'Set max height',
|
|
175
175
|
onClick: () => {
|
|
176
176
|
getSession(self).queueDialog(handleClose => [
|
|
177
|
-
|
|
177
|
+
SetMaxHeightDialog,
|
|
178
178
|
{ model: self, handleClose },
|
|
179
179
|
]);
|
|
180
180
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ExportSvgOptions } from '..';
|
|
3
|
-
export default function
|
|
3
|
+
export default function ExportSvgDialog({ model, handleClose, }: {
|
|
4
4
|
model: {
|
|
5
5
|
exportSvg(opts: ExportSvgOptions): Promise<void>;
|
|
6
6
|
};
|
|
@@ -10,7 +10,7 @@ function LoadingMessage() {
|
|
|
10
10
|
function useSvgLocal(key, val) {
|
|
11
11
|
return useLocalStorage('svg-' + key, val);
|
|
12
12
|
}
|
|
13
|
-
export default function
|
|
13
|
+
export default function ExportSvgDialog({ model, handleClose, }) {
|
|
14
14
|
const session = getSession(model);
|
|
15
15
|
const offscreenCanvas = typeof OffscreenCanvas !== 'undefined';
|
|
16
16
|
const [rasterizeLayers, setRasterizeLayers] = useState(offscreenCanvas);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React, { useRef, useState } from 'react';
|
|
2
|
+
import { observer } from 'mobx-react';
|
|
3
|
+
import { makeStyles } from 'tss-react/mui';
|
|
4
|
+
import { colord } from '@jbrowse/core/util/colord';
|
|
5
|
+
import { getSession, } from '@jbrowse/core/util';
|
|
6
|
+
import { Menu } from '@jbrowse/core/ui';
|
|
7
|
+
import { IconButton, Tooltip, useTheme } from '@mui/material';
|
|
8
|
+
// icons
|
|
9
|
+
import LinkIcon from '@mui/icons-material/Link';
|
|
10
|
+
import CloseIcon from '@mui/icons-material/Close';
|
|
11
|
+
import BookmarkIcon from '@mui/icons-material/Bookmark';
|
|
12
|
+
const useStyles = makeStyles()(theme => {
|
|
13
|
+
var _a, _b, _c, _d, _e, _f;
|
|
14
|
+
return ({
|
|
15
|
+
highlight: {
|
|
16
|
+
height: '100%',
|
|
17
|
+
position: 'absolute',
|
|
18
|
+
background: `${colord((_b = (_a = theme.palette.quaternary) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod')
|
|
19
|
+
.alpha(0.35)
|
|
20
|
+
.toRgbString()}`,
|
|
21
|
+
borderLeft: `1px solid ${(_d = (_c = theme.palette.quaternary) === null || _c === void 0 ? void 0 : _c.main) !== null && _d !== void 0 ? _d : 'goldenrod'}`,
|
|
22
|
+
borderRight: `1px solid ${(_f = (_e = theme.palette.quaternary) === null || _e === void 0 ? void 0 : _e.main) !== null && _f !== void 0 ? _f : 'goldenrod'}`,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
const Highlight = observer(function Highlight({ model }) {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
const { classes } = useStyles();
|
|
29
|
+
const [open, setOpen] = useState(false);
|
|
30
|
+
const anchorEl = useRef(null);
|
|
31
|
+
const color = (_b = (_a = useTheme().palette.quaternary) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod';
|
|
32
|
+
const session = getSession(model);
|
|
33
|
+
const dismissHighlight = () => {
|
|
34
|
+
model.setHighlight(undefined);
|
|
35
|
+
};
|
|
36
|
+
const menuItems = [
|
|
37
|
+
{
|
|
38
|
+
label: 'Dismiss highlight',
|
|
39
|
+
icon: CloseIcon,
|
|
40
|
+
onClick: () => dismissHighlight(),
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
label: 'Bookmark highlighted region',
|
|
44
|
+
icon: BookmarkIcon,
|
|
45
|
+
onClick: () => {
|
|
46
|
+
let bookmarkWidget = session.widgets.get('GridBookmark');
|
|
47
|
+
if (!bookmarkWidget) {
|
|
48
|
+
bookmarkWidget = session.addWidget('GridBookmarkWidget', 'GridBookmark');
|
|
49
|
+
}
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
bookmarkWidget.addBookmark(model.highlight);
|
|
52
|
+
dismissHighlight();
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
function handleClose() {
|
|
57
|
+
setOpen(false);
|
|
58
|
+
}
|
|
59
|
+
if (!model.highlight) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
// coords
|
|
63
|
+
const mapCoords = (r) => {
|
|
64
|
+
const s = model.bpToPx({
|
|
65
|
+
refName: r.refName,
|
|
66
|
+
coord: r.start,
|
|
67
|
+
});
|
|
68
|
+
const e = model.bpToPx({
|
|
69
|
+
refName: r.refName,
|
|
70
|
+
coord: r.end,
|
|
71
|
+
});
|
|
72
|
+
return s && e
|
|
73
|
+
? {
|
|
74
|
+
width: Math.max(Math.abs(e.offsetPx - s.offsetPx), 3),
|
|
75
|
+
left: Math.min(s.offsetPx, e.offsetPx) - model.offsetPx,
|
|
76
|
+
}
|
|
77
|
+
: undefined;
|
|
78
|
+
};
|
|
79
|
+
const h = mapCoords(model.highlight);
|
|
80
|
+
return (React.createElement(React.Fragment, null, h ? (React.createElement("div", { className: classes.highlight, style: {
|
|
81
|
+
left: h.left,
|
|
82
|
+
width: h.width,
|
|
83
|
+
} },
|
|
84
|
+
React.createElement(Tooltip, { title: 'Highlighted from URL parameter', arrow: true },
|
|
85
|
+
React.createElement(IconButton, { ref: anchorEl, onClick: () => setOpen(true) },
|
|
86
|
+
React.createElement(LinkIcon, { fontSize: "small", sx: {
|
|
87
|
+
color: `${colord(color).darken(0.2).toRgbString()}`,
|
|
88
|
+
} }))),
|
|
89
|
+
React.createElement(Menu, { anchorEl: anchorEl.current, onMenuItemClick: (_event, callback) => {
|
|
90
|
+
callback(session);
|
|
91
|
+
handleClose();
|
|
92
|
+
}, open: open, onClose: handleClose, menuItems: menuItems }))) : null));
|
|
93
|
+
});
|
|
94
|
+
export default Highlight;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Base1DViewModel } from '@jbrowse/core/util/Base1DViewModel';
|
|
3
|
+
import { LinearGenomeViewModel } from '../model';
|
|
4
|
+
type LGV = LinearGenomeViewModel;
|
|
5
|
+
declare const OverviewHighlight: ({ model, overview, }: {
|
|
6
|
+
model: LGV;
|
|
7
|
+
overview: Base1DViewModel;
|
|
8
|
+
}) => React.JSX.Element | null;
|
|
9
|
+
export default OverviewHighlight;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { observer } from 'mobx-react';
|
|
3
|
+
import { makeStyles } from 'tss-react/mui';
|
|
4
|
+
import { colord } from '@jbrowse/core/util/colord';
|
|
5
|
+
const useStyles = makeStyles()(theme => {
|
|
6
|
+
var _a, _b, _c, _d, _e, _f;
|
|
7
|
+
return ({
|
|
8
|
+
highlight: {
|
|
9
|
+
height: '100%',
|
|
10
|
+
position: 'absolute',
|
|
11
|
+
background: `${colord((_b = (_a = theme.palette.quaternary) === null || _a === void 0 ? void 0 : _a.main) !== null && _b !== void 0 ? _b : 'goldenrod')
|
|
12
|
+
.alpha(0.35)
|
|
13
|
+
.toRgbString()}`,
|
|
14
|
+
borderLeft: `1px solid ${(_d = (_c = theme.palette.quaternary) === null || _c === void 0 ? void 0 : _c.main) !== null && _d !== void 0 ? _d : 'goldenrod'}`,
|
|
15
|
+
borderRight: `1px solid ${(_f = (_e = theme.palette.quaternary) === null || _e === void 0 ? void 0 : _e.main) !== null && _f !== void 0 ? _f : 'goldenrod'}`,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
const OverviewHighlight = observer(function OverviewHighlight({ model, overview, }) {
|
|
20
|
+
const { classes } = useStyles();
|
|
21
|
+
const { cytobandOffset } = model;
|
|
22
|
+
// coords
|
|
23
|
+
const mapCoords = (r) => {
|
|
24
|
+
const s = overview.bpToPx({
|
|
25
|
+
...r,
|
|
26
|
+
coord: r.reversed ? r.end : r.start,
|
|
27
|
+
});
|
|
28
|
+
const e = overview.bpToPx({
|
|
29
|
+
...r,
|
|
30
|
+
coord: r.reversed ? r.start : r.end,
|
|
31
|
+
});
|
|
32
|
+
return s !== undefined && e != undefined
|
|
33
|
+
? {
|
|
34
|
+
width: Math.abs(e - s),
|
|
35
|
+
left: s + cytobandOffset,
|
|
36
|
+
}
|
|
37
|
+
: undefined;
|
|
38
|
+
};
|
|
39
|
+
if (!model.highlight) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const h = mapCoords(model.highlight);
|
|
43
|
+
return (React.createElement(React.Fragment, null, h ? (React.createElement("div", { className: classes.highlight, style: {
|
|
44
|
+
width: h.width,
|
|
45
|
+
left: h.left,
|
|
46
|
+
} })) : null));
|
|
47
|
+
});
|
|
48
|
+
export default OverviewHighlight;
|
|
@@ -13,6 +13,7 @@ import { getCytobands } from './util';
|
|
|
13
13
|
import OverviewRubberband from './OverviewRubberband';
|
|
14
14
|
import Cytobands from './Cytobands';
|
|
15
15
|
import OverviewScalebarPolygon from './OverviewScalebarPolygon';
|
|
16
|
+
import OverviewHighlight from './OverviewHighlight';
|
|
16
17
|
const wholeSeqSpacer = 2;
|
|
17
18
|
const useStyles = makeStyles()(theme => ({
|
|
18
19
|
scalebar: {
|
|
@@ -151,7 +152,8 @@ const Scalebar = observer(function ({ model, scale, overview, }) {
|
|
|
151
152
|
backgroundColor: '#999',
|
|
152
153
|
backgroundImage: 'repeating-linear-gradient(90deg, transparent, transparent 1px, rgba(255,255,255,.5) 1px, rgba(255,255,255,.5) 3px)',
|
|
153
154
|
} })) : (React.createElement(OverviewBox, { scale: scale, block: block, model: model, overview: overview, key: `${JSON.stringify(block)}-${idx}` }));
|
|
154
|
-
})
|
|
155
|
+
}),
|
|
156
|
+
React.createElement(OverviewHighlight, { model: model, overview: overview })));
|
|
155
157
|
});
|
|
156
158
|
const OverviewScalebar = observer(function ({ model, children, }) {
|
|
157
159
|
const { classes } = useStyles();
|
|
@@ -10,7 +10,7 @@ function HelpAdornment() {
|
|
|
10
10
|
return (React.createElement(React.Fragment, null,
|
|
11
11
|
React.createElement(IconButton, { onClick: () => setHelpDialogDisplayed(true), size: "small" },
|
|
12
12
|
React.createElement(HelpIcon, { fontSize: "small" })),
|
|
13
|
-
isHelpDialogDisplayed ? (React.createElement(Suspense, { fallback:
|
|
13
|
+
isHelpDialogDisplayed ? (React.createElement(Suspense, { fallback: null },
|
|
14
14
|
React.createElement(HelpDialog, { handleClose: () => setHelpDialogDisplayed(false) }))) : null));
|
|
15
15
|
}
|
|
16
16
|
export default function EndAdornment({ showHelp, endAdornment, }) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import { observer } from 'mobx-react';
|
|
3
3
|
import { getSession, useDebounce, measureText } from '@jbrowse/core/util';
|
|
4
4
|
import BaseResult, { RefSequenceResult, } from '@jbrowse/core/TextSearch/BaseResults';
|
|
@@ -6,7 +6,6 @@ import { Autocomplete } from '@mui/material';
|
|
|
6
6
|
import { getDeduplicatedResult, getFiltered } from './util';
|
|
7
7
|
import AutocompleteTextField from './AutocompleteTextField';
|
|
8
8
|
const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName, style, fetchResults, onChange, value, showHelp = true, minWidth = 200, maxWidth = 550, TextFieldProps = {}, }) {
|
|
9
|
-
var _a;
|
|
10
9
|
const session = getSession(model);
|
|
11
10
|
const { assemblyManager } = session;
|
|
12
11
|
const [open, setOpen] = useState(false);
|
|
@@ -45,6 +44,14 @@ const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName,
|
|
|
45
44
|
const inputBoxVal = coarseVisibleLocStrings || value || '';
|
|
46
45
|
// heuristic, text width + 60 accommodates help icon and search icon
|
|
47
46
|
const width = Math.min(Math.max(measureText(inputBoxVal, 14) + 100, minWidth), maxWidth);
|
|
47
|
+
const regions = assembly === null || assembly === void 0 ? void 0 : assembly.regions;
|
|
48
|
+
const regionOptions = useMemo(() => (regions === null || regions === void 0 ? void 0 : regions.map(option => ({
|
|
49
|
+
result: new RefSequenceResult({
|
|
50
|
+
refName: option.refName,
|
|
51
|
+
label: option.refName,
|
|
52
|
+
matchedAttribute: 'refName',
|
|
53
|
+
}),
|
|
54
|
+
}))) || [], [regions]);
|
|
48
55
|
// notes on implementation:
|
|
49
56
|
// The selectOnFocus setting helps highlight the field when clicked
|
|
50
57
|
return (React.createElement(React.Fragment, null,
|
|
@@ -70,14 +77,6 @@ const RefNameAutocomplete = observer(function ({ model, onSelect, assemblyName,
|
|
|
70
77
|
onSelect === null || onSelect === void 0 ? void 0 : onSelect(selectedOption.result);
|
|
71
78
|
}
|
|
72
79
|
setInputValue(inputBoxVal);
|
|
73
|
-
}, options:
|
|
74
|
-
? ((_a = assembly === null || assembly === void 0 ? void 0 : assembly.regions) === null || _a === void 0 ? void 0 : _a.map(option => ({
|
|
75
|
-
result: new RefSequenceResult({
|
|
76
|
-
refName: option.refName,
|
|
77
|
-
label: option.refName,
|
|
78
|
-
matchedAttribute: 'refName',
|
|
79
|
-
}),
|
|
80
|
-
}))) || []
|
|
81
|
-
: searchOptions, getOptionDisabled: option => option.group === 'limitOption', filterOptions: (opts, { inputValue }) => getFiltered(opts, inputValue), renderInput: params => (React.createElement(AutocompleteTextField, { showHelp: showHelp, params: params, inputBoxVal: inputBoxVal, TextFieldProps: TextFieldProps, setCurrentSearch: setCurrentSearch, setInputValue: setInputValue })), getOptionLabel: opt => typeof opt === 'string' ? opt : opt.result.getDisplayString() })));
|
|
80
|
+
}, options: (searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.length) ? searchOptions : regionOptions, getOptionDisabled: option => option.group === 'limitOption', filterOptions: (opts, { inputValue }) => getFiltered(opts, inputValue), renderInput: params => (React.createElement(AutocompleteTextField, { showHelp: showHelp, params: params, inputBoxVal: inputBoxVal, TextFieldProps: TextFieldProps, setCurrentSearch: setCurrentSearch, setInputValue: setInputValue })), getOptionLabel: opt => typeof opt === 'string' ? opt : opt.result.getDisplayString() })));
|
|
82
81
|
});
|
|
83
82
|
export default RefNameAutocomplete;
|
|
@@ -13,6 +13,7 @@ import Gridlines from './Gridlines';
|
|
|
13
13
|
import CenterLine from './CenterLine';
|
|
14
14
|
import VerticalGuide from './VerticalGuide';
|
|
15
15
|
import RubberbandSpan from './RubberbandSpan';
|
|
16
|
+
import Highlight from './Highlight';
|
|
16
17
|
const useStyles = makeStyles()({
|
|
17
18
|
tracksContainer: {
|
|
18
19
|
position: 'relative',
|
|
@@ -39,6 +40,7 @@ const TracksContainer = observer(function TracksContainer({ children, model, })
|
|
|
39
40
|
top: anchorPosition.clientY,
|
|
40
41
|
}, onMenuItemClick: handleMenuItemClick, open: open, onClose: handleClose, menuItems: model.rubberBandMenuItems() })) : null,
|
|
41
42
|
React.createElement(Rubberband, { model: model, ControlComponent: React.createElement(Scalebar, { model: model, style: { height: SCALE_BAR_HEIGHT, boxSizing: 'border-box' } }) }),
|
|
43
|
+
React.createElement(Highlight, { model: model }),
|
|
42
44
|
additional,
|
|
43
45
|
children));
|
|
44
46
|
});
|