@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.
Files changed (55) hide show
  1. package/dist/BaseLinearDisplay/components/BlockMsg.d.ts +2 -4
  2. package/dist/BaseLinearDisplay/components/BlockMsg.js +2 -3
  3. package/dist/BaseLinearDisplay/components/LinearBlocks.js +2 -2
  4. package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.d.ts +10 -2
  5. package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +40 -4
  6. package/dist/BaseLinearDisplay/components/TooLargeMessage.js +5 -4
  7. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +18 -4
  8. package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +9 -2
  9. package/dist/BaseLinearDisplay/models/util.js +4 -2
  10. package/dist/LaunchLinearGenomeView/index.js +16 -2
  11. package/dist/LinearBareDisplay/model.d.ts +18 -4
  12. package/dist/LinearBasicDisplay/model.d.ts +32 -24
  13. package/dist/LinearBasicDisplay/model.js +2 -2
  14. package/dist/LinearGenomeView/components/ExportSvgDialog.d.ts +1 -1
  15. package/dist/LinearGenomeView/components/ExportSvgDialog.js +2 -2
  16. package/dist/LinearGenomeView/components/Highlight.d.ts +7 -0
  17. package/dist/LinearGenomeView/components/Highlight.js +122 -0
  18. package/dist/LinearGenomeView/components/OverviewHighlight.d.ts +9 -0
  19. package/dist/LinearGenomeView/components/OverviewHighlight.js +53 -0
  20. package/dist/LinearGenomeView/components/OverviewScalebar.js +3 -1
  21. package/dist/LinearGenomeView/components/RefNameAutocomplete/EndAdornment.js +1 -1
  22. package/dist/LinearGenomeView/components/RefNameAutocomplete/index.js +9 -10
  23. package/dist/LinearGenomeView/components/TracksContainer.js +2 -0
  24. package/dist/LinearGenomeView/model.d.ts +10 -5
  25. package/dist/LinearGenomeView/model.js +26 -15
  26. package/dist/LinearGenomeView/util.d.ts +1 -1
  27. package/dist/index.d.ts +96 -42
  28. package/esm/BaseLinearDisplay/components/BlockMsg.d.ts +2 -4
  29. package/esm/BaseLinearDisplay/components/BlockMsg.js +3 -4
  30. package/esm/BaseLinearDisplay/components/LinearBlocks.js +2 -2
  31. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.d.ts +10 -2
  32. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +17 -4
  33. package/esm/BaseLinearDisplay/components/TooLargeMessage.js +5 -4
  34. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +18 -4
  35. package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +9 -2
  36. package/esm/BaseLinearDisplay/models/util.js +4 -2
  37. package/esm/LaunchLinearGenomeView/index.js +17 -3
  38. package/esm/LinearBareDisplay/model.d.ts +18 -4
  39. package/esm/LinearBasicDisplay/model.d.ts +32 -24
  40. package/esm/LinearBasicDisplay/model.js +2 -2
  41. package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +1 -1
  42. package/esm/LinearGenomeView/components/ExportSvgDialog.js +1 -1
  43. package/esm/LinearGenomeView/components/Highlight.d.ts +7 -0
  44. package/esm/LinearGenomeView/components/Highlight.js +94 -0
  45. package/esm/LinearGenomeView/components/OverviewHighlight.d.ts +9 -0
  46. package/esm/LinearGenomeView/components/OverviewHighlight.js +48 -0
  47. package/esm/LinearGenomeView/components/OverviewScalebar.js +3 -1
  48. package/esm/LinearGenomeView/components/RefNameAutocomplete/EndAdornment.js +1 -1
  49. package/esm/LinearGenomeView/components/RefNameAutocomplete/index.js +10 -11
  50. package/esm/LinearGenomeView/components/TracksContainer.js +2 -0
  51. package/esm/LinearGenomeView/model.d.ts +10 -5
  52. package/esm/LinearGenomeView/model.js +26 -15
  53. package/esm/LinearGenomeView/util.d.ts +1 -1
  54. package/esm/index.d.ts +96 -42
  55. 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", buttonText: "reload", icon: React.createElement(RefreshIcon, null), action: model.reload }));
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
- model.setFeatureDensityStatsLimit(model.featureDensityStats);
8
- model.reload();
9
- }, buttonText: "Force load", message: [
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: any;
62
- }) => any;
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: any;
346
- }) => any;
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: any;
32
- }) => any;
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 isJest = typeof jest === 'undefined';
20
- return `clip-${isJest ? id : 'jest'}-${index}`;
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 '..//searchUtils';
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: any;
46
- }) => any;
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: any;
271
- }) => any;
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: any;
53
- }) => any;
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: any;
357
- }) => any;
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 SetMaxHeightDlg = lazy(() => import('./components/SetMaxHeight'));
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
- SetMaxHeightDlg,
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 ExportSvgDlg({ model, handleClose, }: {
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 ExportSvgDlg({ model, handleClose, }) {
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,7 @@
1
+ import React from 'react';
2
+ import { LinearGenomeViewModel } from '../model';
3
+ type LGV = LinearGenomeViewModel;
4
+ declare const Highlight: ({ model }: {
5
+ model: LGV;
6
+ }) => React.JSX.Element | null;
7
+ export default Highlight;
@@ -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: React.createElement("div", null) },
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: !(searchOptions === null || searchOptions === void 0 ? void 0 : searchOptions.length)
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
  });