@jbrowse/plugin-linear-comparative-view 2.9.0 → 2.10.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.
Files changed (41) hide show
  1. package/dist/LGVSyntenyDisplay/configSchemaF.d.ts +2 -0
  2. package/dist/LGVSyntenyDisplay/configSchemaF.js +2 -0
  3. package/dist/LGVSyntenyDisplay/model.d.ts +18 -7
  4. package/dist/LGVSyntenyDisplay/model.js +13 -3
  5. package/dist/LinearComparativeDisplay/stateModelFactory.d.ts +35 -27
  6. package/dist/LinearComparativeDisplay/stateModelFactory.js +2 -0
  7. package/dist/LinearComparativeView/model.d.ts +27 -12
  8. package/dist/LinearComparativeView/model.js +2 -0
  9. package/dist/LinearReadVsRef/index.js +26 -2
  10. package/dist/LinearSyntenyDisplay/components/LinearSyntenyRendering.js +185 -96
  11. package/dist/LinearSyntenyDisplay/components/SyntenyContextMenu.d.ts +13 -0
  12. package/dist/LinearSyntenyDisplay/components/SyntenyContextMenu.js +57 -0
  13. package/dist/LinearSyntenyDisplay/components/SyntenyTooltip.d.ts +1 -3
  14. package/dist/LinearSyntenyDisplay/components/SyntenyTooltip.js +19 -51
  15. package/dist/LinearSyntenyDisplay/model.d.ts +27 -17
  16. package/dist/LinearSyntenyDisplay/model.js +2 -1
  17. package/dist/LinearSyntenyView/model.d.ts +119 -54
  18. package/dist/LinearSyntenyView/model.js +8 -1
  19. package/dist/LinearSyntenyView/svgcomponents/SVGLinearSyntenyView.js +6 -6
  20. package/dist/SyntenyTrack/configSchema.js +2 -0
  21. package/esm/LGVSyntenyDisplay/configSchemaF.d.ts +2 -0
  22. package/esm/LGVSyntenyDisplay/configSchemaF.js +2 -0
  23. package/esm/LGVSyntenyDisplay/model.d.ts +18 -7
  24. package/esm/LGVSyntenyDisplay/model.js +13 -3
  25. package/esm/LinearComparativeDisplay/stateModelFactory.d.ts +35 -27
  26. package/esm/LinearComparativeDisplay/stateModelFactory.js +2 -0
  27. package/esm/LinearComparativeView/model.d.ts +27 -12
  28. package/esm/LinearComparativeView/model.js +2 -0
  29. package/esm/LinearReadVsRef/index.js +2 -1
  30. package/esm/LinearSyntenyDisplay/components/LinearSyntenyRendering.js +186 -97
  31. package/esm/LinearSyntenyDisplay/components/SyntenyContextMenu.d.ts +13 -0
  32. package/esm/LinearSyntenyDisplay/components/SyntenyContextMenu.js +51 -0
  33. package/esm/LinearSyntenyDisplay/components/SyntenyTooltip.d.ts +1 -3
  34. package/esm/LinearSyntenyDisplay/components/SyntenyTooltip.js +18 -30
  35. package/esm/LinearSyntenyDisplay/model.d.ts +27 -17
  36. package/esm/LinearSyntenyDisplay/model.js +2 -1
  37. package/esm/LinearSyntenyView/model.d.ts +119 -54
  38. package/esm/LinearSyntenyView/model.js +8 -1
  39. package/esm/LinearSyntenyView/svgcomponents/SVGLinearSyntenyView.js +6 -6
  40. package/esm/SyntenyTrack/configSchema.js +2 -0
  41. package/package.json +4 -5
@@ -4,6 +4,8 @@ import { Instance } from 'mobx-state-tree';
4
4
  import { Feature } from '@jbrowse/core/util';
5
5
  /**
6
6
  * #stateModel LinearComparativeDisplay
7
+ * extends
8
+ * - [BaseDisplay](../basedisplay)
7
9
  */
8
10
  declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): import("mobx-state-tree").IModelType<{
9
11
  id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
@@ -21,7 +23,9 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
21
23
  readonly RenderingComponent: import("react").FC<{
22
24
  model: {
23
25
  id: string;
24
- type: string;
26
+ type: string; /**
27
+ * #property
28
+ */
25
29
  rpcDriverName: string | undefined;
26
30
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
27
31
  rendererTypeName: string;
@@ -30,12 +34,12 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
30
34
  } & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
31
35
  id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
32
36
  type: import("mobx-state-tree").ISimpleType<string>;
33
- rpcDriverName: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>; /**
37
+ rpcDriverName: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
38
+ }, {
39
+ rendererTypeName: string; /**
34
40
  * #action
35
41
  * controlled by a reaction
36
42
  */
37
- }, {
38
- rendererTypeName: string;
39
43
  error: unknown;
40
44
  message: string | undefined;
41
45
  }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
@@ -48,10 +52,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
48
52
  type: string;
49
53
  rpcDriverName: string | undefined;
50
54
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
51
- rendererTypeName: string; /**
52
- * #action
53
- * controlled by a reaction
54
- */
55
+ rendererTypeName: string;
55
56
  error: unknown;
56
57
  message: string | undefined;
57
58
  } & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
@@ -67,7 +68,10 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
67
68
  readonly adapterConfig: any;
68
69
  readonly parentTrack: any;
69
70
  renderProps(): any;
70
- readonly rendererType: import("@jbrowse/core/pluggableElementTypes").RendererType;
71
+ readonly rendererType: import("@jbrowse/core/pluggableElementTypes").RendererType; /**
72
+ * #action
73
+ * controlled by a reaction
74
+ */
71
75
  readonly DisplayMessageComponent: import("react").FC<any> | undefined;
72
76
  trackMenuItems(): import("@jbrowse/core/ui").MenuItem[];
73
77
  readonly viewMenuActions: import("@jbrowse/core/ui").MenuItem[];
@@ -105,7 +109,9 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
105
109
  readonly RenderingComponent: import("react").FC<{
106
110
  model: {
107
111
  id: string;
108
- type: string;
112
+ type: string; /**
113
+ * #property
114
+ */
109
115
  rpcDriverName: string | undefined;
110
116
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
111
117
  rendererTypeName: string;
@@ -114,12 +120,12 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
114
120
  } & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
115
121
  id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
116
122
  type: import("mobx-state-tree").ISimpleType<string>;
117
- rpcDriverName: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>; /**
123
+ rpcDriverName: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
124
+ }, {
125
+ rendererTypeName: string; /**
118
126
  * #action
119
127
  * controlled by a reaction
120
128
  */
121
- }, {
122
- rendererTypeName: string;
123
129
  error: unknown;
124
130
  message: string | undefined;
125
131
  }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
@@ -132,10 +138,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
132
138
  type: string;
133
139
  rpcDriverName: string | undefined;
134
140
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
135
- rendererTypeName: string; /**
136
- * #action
137
- * controlled by a reaction
138
- */
141
+ rendererTypeName: string;
139
142
  error: unknown;
140
143
  message: string | undefined;
141
144
  } & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
@@ -151,7 +154,10 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
151
154
  readonly adapterConfig: any;
152
155
  readonly parentTrack: any;
153
156
  renderProps(): any;
154
- readonly rendererType: import("@jbrowse/core/pluggableElementTypes").RendererType;
157
+ readonly rendererType: import("@jbrowse/core/pluggableElementTypes").RendererType; /**
158
+ * #action
159
+ * controlled by a reaction
160
+ */
155
161
  readonly DisplayMessageComponent: import("react").FC<any> | undefined;
156
162
  trackMenuItems(): import("@jbrowse/core/ui").MenuItem[];
157
163
  readonly viewMenuActions: import("@jbrowse/core/ui").MenuItem[];
@@ -181,7 +187,9 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
181
187
  readonly RenderingComponent: import("react").FC<{
182
188
  model: {
183
189
  id: string;
184
- type: string;
190
+ type: string; /**
191
+ * #property
192
+ */
185
193
  rpcDriverName: string | undefined;
186
194
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
187
195
  rendererTypeName: string;
@@ -190,12 +198,12 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
190
198
  } & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
191
199
  id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
192
200
  type: import("mobx-state-tree").ISimpleType<string>;
193
- rpcDriverName: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>; /**
201
+ rpcDriverName: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
202
+ }, {
203
+ rendererTypeName: string; /**
194
204
  * #action
195
205
  * controlled by a reaction
196
206
  */
197
- }, {
198
- rendererTypeName: string;
199
207
  error: unknown;
200
208
  message: string | undefined;
201
209
  }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
@@ -208,10 +216,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
208
216
  type: string;
209
217
  rpcDriverName: string | undefined;
210
218
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
211
- rendererTypeName: string; /**
212
- * #action
213
- * controlled by a reaction
214
- */
219
+ rendererTypeName: string;
215
220
  error: unknown;
216
221
  message: string | undefined;
217
222
  } & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
@@ -227,7 +232,10 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
227
232
  readonly adapterConfig: any;
228
233
  readonly parentTrack: any;
229
234
  renderProps(): any;
230
- readonly rendererType: import("@jbrowse/core/pluggableElementTypes").RendererType;
235
+ readonly rendererType: import("@jbrowse/core/pluggableElementTypes").RendererType; /**
236
+ * #action
237
+ * controlled by a reaction
238
+ */
231
239
  readonly DisplayMessageComponent: import("react").FC<any> | undefined;
232
240
  trackMenuItems(): import("@jbrowse/core/ui").MenuItem[];
233
241
  readonly viewMenuActions: import("@jbrowse/core/ui").MenuItem[];
@@ -5,6 +5,8 @@ import { getRpcSessionId } from '@jbrowse/core/util/tracks';
5
5
  import { BaseDisplay } from '@jbrowse/core/pluggableElementTypes/models';
6
6
  /**
7
7
  * #stateModel LinearComparativeDisplay
8
+ * extends
9
+ * - [BaseDisplay](../basedisplay)
8
10
  */
9
11
  function stateModelFactory(configSchema) {
10
12
  return types
@@ -5,6 +5,8 @@ import PluginManager from '@jbrowse/core/PluginManager';
5
5
  import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
6
6
  /**
7
7
  * #stateModel LinearComparativeView
8
+ * extends
9
+ * - [BaseViewModel](../baseviewmodel)
8
10
  */
9
11
  declare function stateModelFactory(pluginManager: PluginManager): import("mobx-state-tree").IModelType<{
10
12
  id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
@@ -34,9 +36,6 @@ declare function stateModelFactory(pluginManager: PluginManager): import("mobx-s
34
36
  end: import("mobx-state-tree").ISimpleType<number>;
35
37
  reversed: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<boolean>, [undefined]>;
36
38
  } & {
37
- /**
38
- * #property
39
- */
40
39
  assemblyName: import("mobx-state-tree").ISimpleType<string>;
41
40
  }, {
42
41
  setRefName(newRefName: string): void;
@@ -64,7 +63,9 @@ declare function stateModelFactory(pluginManager: PluginManager): import("mobx-s
64
63
  draggingTrackId: string | undefined;
65
64
  volatileError: unknown;
66
65
  afterDisplayedRegionsSetCallbacks: Function[];
67
- scaleFactor: number;
66
+ scaleFactor: number; /**
67
+ * #action
68
+ */
68
69
  trackRefs: Record<string, HTMLDivElement>;
69
70
  coarseDynamicBlocks: import("@jbrowse/core/util/blockTypes").BaseBlock[];
70
71
  coarseTotalBp: number;
@@ -192,7 +193,8 @@ declare function stateModelFactory(pluginManager: PluginManager): import("mobx-s
192
193
  } & {
193
194
  getConf(arg: string): any;
194
195
  } & {
195
- readonly initialized: boolean; /**
196
+ readonly initialized: boolean;
197
+ /**
196
198
  * #property
197
199
  */
198
200
  readonly name: string;
@@ -226,11 +228,17 @@ declare function stateModelFactory(pluginManager: PluginManager): import("mobx-s
226
228
  load(): Promise<void>;
227
229
  loadPre(): Promise<void>;
228
230
  } & {
229
- getAdapterMapEntry(adapterConf: unknown, options: import("@jbrowse/core/data_adapters/BaseAdapter").BaseOptions): Promise<import("@jbrowse/core/assemblyManager/assembly").RefNameMap>;
230
- getRefNameMapForAdapter(adapterConf: unknown, opts: import("@jbrowse/core/data_adapters/BaseAdapter").BaseOptions): Promise<{
231
+ getAdapterMapEntry(adapterConf: {
232
+ [x: string]: unknown;
233
+ }, options: import("@jbrowse/core/data_adapters/BaseAdapter").BaseOptions): Promise<import("@jbrowse/core/assemblyManager/assembly").RefNameMap>;
234
+ getRefNameMapForAdapter(adapterConf: {
235
+ [x: string]: unknown;
236
+ }, opts: import("@jbrowse/core/data_adapters/BaseAdapter").BaseOptions): Promise<{
231
237
  [x: string]: string | undefined;
232
238
  }>;
233
- getReverseRefNameMapForAdapter(adapterConf: unknown, opts: import("@jbrowse/core/data_adapters/BaseAdapter").BaseOptions): Promise<{
239
+ getReverseRefNameMapForAdapter(adapterConf: {
240
+ [x: string]: unknown;
241
+ }, opts: import("@jbrowse/core/data_adapters/BaseAdapter").BaseOptions): Promise<{
234
242
  [x: string]: string | undefined;
235
243
  }>;
236
244
  } & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
@@ -250,7 +258,8 @@ declare function stateModelFactory(pluginManager: PluginManager): import("mobx-s
250
258
  } & {
251
259
  getConf(arg: string): any;
252
260
  } & {
253
- readonly initialized: boolean; /**
261
+ readonly initialized: boolean;
262
+ /**
254
263
  * #property
255
264
  */
256
265
  readonly name: string;
@@ -284,11 +293,17 @@ declare function stateModelFactory(pluginManager: PluginManager): import("mobx-s
284
293
  load(): Promise<void>;
285
294
  loadPre(): Promise<void>;
286
295
  } & {
287
- getAdapterMapEntry(adapterConf: unknown, options: import("@jbrowse/core/data_adapters/BaseAdapter").BaseOptions): Promise<import("@jbrowse/core/assemblyManager/assembly").RefNameMap>;
288
- getRefNameMapForAdapter(adapterConf: unknown, opts: import("@jbrowse/core/data_adapters/BaseAdapter").BaseOptions): Promise<{
296
+ getAdapterMapEntry(adapterConf: {
297
+ [x: string]: unknown;
298
+ }, options: import("@jbrowse/core/data_adapters/BaseAdapter").BaseOptions): Promise<import("@jbrowse/core/assemblyManager/assembly").RefNameMap>;
299
+ getRefNameMapForAdapter(adapterConf: {
300
+ [x: string]: unknown;
301
+ }, opts: import("@jbrowse/core/data_adapters/BaseAdapter").BaseOptions): Promise<{
289
302
  [x: string]: string | undefined;
290
303
  }>;
291
- getReverseRefNameMapForAdapter(adapterConf: unknown, opts: import("@jbrowse/core/data_adapters/BaseAdapter").BaseOptions): Promise<{
304
+ getReverseRefNameMapForAdapter(adapterConf: {
305
+ [x: string]: unknown;
306
+ }, opts: import("@jbrowse/core/data_adapters/BaseAdapter").BaseOptions): Promise<{
292
307
  [x: string]: string | undefined;
293
308
  }>;
294
309
  }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
@@ -12,6 +12,8 @@ import FolderOpenIcon from '@mui/icons-material/FolderOpen';
12
12
  const ReturnToImportFormDialog = lazy(() => import('@jbrowse/core/ui/ReturnToImportFormDialog'));
13
13
  /**
14
14
  * #stateModel LinearComparativeView
15
+ * extends
16
+ * - [BaseViewModel](../baseviewmodel)
15
17
  */
16
18
  function stateModelFactory(pluginManager) {
17
19
  return types
@@ -1,8 +1,9 @@
1
+ import { lazy } from 'react';
1
2
  import { getSession, getContainingTrack } from '@jbrowse/core/util';
2
3
  // icons
3
4
  import AddIcon from '@mui/icons-material/Add';
4
5
  // locals
5
- import ReadVsRefDialog from './LinearReadVsRef';
6
+ const ReadVsRefDialog = lazy(() => import('./LinearReadVsRef'));
6
7
  function isDisplay(elt) {
7
8
  return elt.name === 'LinearPileupDisplay';
8
9
  }
@@ -1,16 +1,41 @@
1
- import React, { useState, useCallback } from 'react';
1
+ import React, { useState, useCallback, useRef } from 'react';
2
2
  import { observer } from 'mobx-react';
3
3
  import { assembleLocString, getContainingView, getSession, isSessionModelWithWidgets, } from '@jbrowse/core/util';
4
+ import { transaction } from 'mobx';
5
+ import { makeStyles } from 'tss-react/mui';
4
6
  // locals
5
7
  import SyntenyTooltip from './SyntenyTooltip';
6
8
  import { getId, MAX_COLOR_RANGE } from '../drawSynteny';
9
+ import SyntenyContextMenu from './SyntenyContextMenu';
10
+ const useStyles = makeStyles()({
11
+ pix: {
12
+ imageRendering: 'pixelated',
13
+ pointerEvents: 'none',
14
+ visibility: 'hidden',
15
+ position: 'absolute',
16
+ },
17
+ rel: {
18
+ position: 'relative',
19
+ },
20
+ abs: {
21
+ position: 'absolute',
22
+ },
23
+ none: {
24
+ pointEvents: 'none',
25
+ },
26
+ });
7
27
  const LinearSyntenyRendering = observer(function ({ model, }) {
8
- const highResolutionScaling = 1;
28
+ const { classes, cx } = useStyles();
29
+ const xOffset = useRef(0);
30
+ const currScrollFrame = useRef();
9
31
  const view = getContainingView(model);
10
32
  const height = view.middleComparativeHeight;
11
33
  const width = view.width;
34
+ const [anchorEl, setAnchorEl] = useState();
12
35
  const [tooltip, setTooltip] = useState('');
13
36
  const [currX, setCurrX] = useState();
37
+ const [mouseCurrDownX, setMouseCurrDownX] = useState();
38
+ const [mouseInitialDownX, setMouseInitialDownX] = useState();
14
39
  const [currY, setCurrY] = useState();
15
40
  // these useCallbacks avoid new refs from being created on any mouseover, etc.
16
41
  const k1 = useCallback((ref) => model.setMouseoverCanvasRef(ref),
@@ -25,108 +50,172 @@ const LinearSyntenyRendering = observer(function ({ model, }) {
25
50
  const k4 = useCallback((ref) => model.setCigarClickMapCanvasRef(ref),
26
51
  // eslint-disable-next-line react-hooks/exhaustive-deps
27
52
  [model, height, width]);
28
- return (React.createElement("div", { style: { position: 'relative' } },
29
- React.createElement("canvas", { ref: k1, width: width, height: height, style: { width, height, position: 'absolute', pointerEvents: 'none' } }),
30
- React.createElement("canvas", { ref: k2, onMouseMove: event => {
31
- var _a;
32
- const ref1 = model.clickMapCanvas;
33
- const ref2 = model.cigarClickMapCanvas;
34
- if (!ref1 || !ref2) {
35
- return;
53
+ return (React.createElement("div", { className: classes.rel },
54
+ React.createElement("canvas", { ref: k1, width: width, height: height, className: cx(classes.abs, classes.none) }),
55
+ React.createElement("canvas", { ref: k2, onWheel: event => {
56
+ if (Math.abs(event.deltaY) < Math.abs(event.deltaX)) {
57
+ xOffset.current += event.deltaX;
36
58
  }
37
- const rect = ref1.getBoundingClientRect();
38
- const ctx1 = ref1.getContext('2d');
39
- const ctx2 = ref2.getContext('2d');
40
- if (!ctx1 || !ctx2) {
41
- return;
59
+ if (currScrollFrame.current === undefined) {
60
+ currScrollFrame.current = requestAnimationFrame(() => {
61
+ transaction(() => {
62
+ for (const v of view.views) {
63
+ v.horizontalScroll(xOffset.current);
64
+ }
65
+ xOffset.current = 0;
66
+ currScrollFrame.current = undefined;
67
+ });
68
+ });
42
69
  }
43
- const { clientX, clientY } = event;
44
- const x = clientX - rect.left;
45
- const y = clientY - rect.top;
46
- setCurrX(clientX);
47
- setCurrY(clientY);
48
- const [r1, g1, b1] = ctx1.getImageData(x, y, 1, 1).data;
49
- const [r2, g2, b2] = ctx2.getImageData(x, y, 1, 1).data;
50
- const unitMultiplier = Math.floor(MAX_COLOR_RANGE / model.numFeats);
51
- const id = getId(r1, g1, b1, unitMultiplier);
52
- model.setMouseoverId((_a = model.featPositions[id]) === null || _a === void 0 ? void 0 : _a.f.id());
53
- if (id === -1) {
54
- setTooltip('');
70
+ }, onMouseMove: event => {
71
+ var _a;
72
+ if (mouseCurrDownX !== undefined) {
73
+ xOffset.current += mouseCurrDownX - event.clientX;
74
+ setMouseCurrDownX(event.clientX);
75
+ if (currScrollFrame.current === undefined) {
76
+ currScrollFrame.current = requestAnimationFrame(() => {
77
+ transaction(() => {
78
+ for (const v of view.views) {
79
+ v.horizontalScroll(xOffset.current);
80
+ }
81
+ xOffset.current = 0;
82
+ currScrollFrame.current = undefined;
83
+ });
84
+ });
85
+ }
55
86
  }
56
- else if (model.featPositions[id]) {
57
- const { f, cigar } = model.featPositions[id];
58
- // @ts-expect-error
59
- const f1 = f.toJSON();
60
- const f2 = f1.mate;
61
- const unitMultiplier2 = Math.floor(MAX_COLOR_RANGE / cigar.length);
62
- const cigarIdx = getId(r2, g2, b2, unitMultiplier2);
63
- const l1 = f1.end - f1.start;
64
- const l2 = f2.end - f2.start;
65
- const identity = f1.identity;
66
- const n1 = f1.name;
67
- const n2 = f2.name;
68
- const tooltip = [
69
- `Loc1: ${assembleLocString(f1)}`,
70
- `Loc2: ${assembleLocString(f2)}`,
71
- `Inverted: ${f1.strand === -1}`,
72
- `Query len: ${l1}`,
73
- `Target len: ${l2}`,
74
- ];
75
- if (identity) {
76
- tooltip.push(`Identity: ${identity}`);
87
+ else {
88
+ const ref1 = model.clickMapCanvas;
89
+ const ref2 = model.cigarClickMapCanvas;
90
+ if (!ref1 || !ref2) {
91
+ return;
77
92
  }
78
- if (cigar[cigarIdx]) {
79
- tooltip.push(`CIGAR operator: ${cigar[cigarIdx]}${cigar[cigarIdx + 1]}`);
93
+ const rect = ref1.getBoundingClientRect();
94
+ const ctx1 = ref1.getContext('2d');
95
+ const ctx2 = ref2.getContext('2d');
96
+ if (!ctx1 || !ctx2) {
97
+ return;
80
98
  }
81
- if (n1 && n2) {
82
- tooltip.push(`Name 1: ${n1}`, `Name 2: ${n2}`);
99
+ const { clientX, clientY } = event;
100
+ const x = clientX - rect.left;
101
+ const y = clientY - rect.top;
102
+ setCurrX(clientX);
103
+ setCurrY(clientY);
104
+ const [r1, g1, b1] = ctx1.getImageData(x, y, 1, 1).data;
105
+ const [r2, g2, b2] = ctx2.getImageData(x, y, 1, 1).data;
106
+ const unitMultiplier = Math.floor(MAX_COLOR_RANGE / model.numFeats);
107
+ const id = getId(r1, g1, b1, unitMultiplier);
108
+ model.setMouseoverId((_a = model.featPositions[id]) === null || _a === void 0 ? void 0 : _a.f.id());
109
+ if (id === -1) {
110
+ setTooltip('');
111
+ }
112
+ else if (model.featPositions[id]) {
113
+ const { f, cigar } = model.featPositions[id];
114
+ const unitMultiplier2 = Math.floor(MAX_COLOR_RANGE / cigar.length);
115
+ const cigarIdx = getId(r2, g2, b2, unitMultiplier2);
116
+ setTooltip(getTooltip(f, cigar[cigarIdx], cigar[cigarIdx + 1]));
83
117
  }
84
- setTooltip(tooltip.join('<br/>'));
85
- }
86
- }, onMouseLeave: () => model.setMouseoverId(undefined), onClick: event => {
87
- const ref1 = model.clickMapCanvas;
88
- const ref2 = model.cigarClickMapCanvas;
89
- if (!ref1 || !ref2) {
90
- return;
91
- }
92
- const rect = ref1.getBoundingClientRect();
93
- const ctx1 = ref1.getContext('2d');
94
- const ctx2 = ref2.getContext('2d');
95
- if (!ctx1 || !ctx2) {
96
- return;
97
- }
98
- const x = event.clientX - rect.left;
99
- const y = event.clientY - rect.top;
100
- const [r1, g1, b1] = ctx1.getImageData(x, y, 1, 1).data;
101
- const unitMultiplier = Math.floor(MAX_COLOR_RANGE / model.numFeats);
102
- const id = getId(r1, g1, b1, unitMultiplier);
103
- const f = model.featPositions[id];
104
- if (!f) {
105
- return;
106
118
  }
107
- model.setClickId(f.f.id());
108
- const session = getSession(model);
109
- if (isSessionModelWithWidgets(session)) {
110
- session.showWidget(session.addWidget('SyntenyFeatureWidget', 'syntenyFeature', {
111
- featureData: {
112
- feature1: f.f.toJSON(),
113
- feature2: f.f.get('mate'),
114
- },
115
- }));
119
+ }, onMouseLeave: () => {
120
+ model.setMouseoverId(undefined);
121
+ setMouseInitialDownX(undefined);
122
+ setMouseCurrDownX(undefined);
123
+ }, onMouseDown: evt => {
124
+ setMouseCurrDownX(evt.clientX);
125
+ setMouseInitialDownX(evt.clientX);
126
+ }, onMouseUp: evt => {
127
+ setMouseCurrDownX(undefined);
128
+ if (mouseInitialDownX !== undefined &&
129
+ Math.abs(evt.clientX - mouseInitialDownX) < 5) {
130
+ onSyntenyClick(evt, model);
116
131
  }
117
- }, "data-testid": "synteny_canvas", style: { width, height, position: 'absolute' }, width: width * highResolutionScaling, height: height * highResolutionScaling }),
118
- React.createElement("canvas", { ref: k3, style: {
119
- imageRendering: 'pixelated',
120
- pointerEvents: 'none',
121
- visibility: 'hidden',
122
- position: 'absolute',
123
- }, width: width, height: height }),
124
- React.createElement("canvas", { ref: k4, style: {
125
- imageRendering: 'pixelated',
126
- pointerEvents: 'none',
127
- visibility: 'hidden',
128
- position: 'absolute',
129
- }, width: width, height: height }),
130
- model.mouseoverId && tooltip && currX && currY ? (React.createElement(SyntenyTooltip, { x: currX, y: currY, title: tooltip })) : null));
132
+ }, onContextMenu: evt => {
133
+ onSyntenyContextClick(evt, model, setAnchorEl);
134
+ }, "data-testid": "synteny_canvas", className: classes.abs, width: width, height: height }),
135
+ React.createElement("canvas", { ref: k3, className: classes.pix, width: width, height: height }),
136
+ React.createElement("canvas", { ref: k4, className: classes.pix, width: width, height: height }),
137
+ model.mouseoverId && tooltip && currX && currY ? (React.createElement(SyntenyTooltip, { title: tooltip })) : null,
138
+ anchorEl ? (React.createElement(SyntenyContextMenu, { model: model, anchorEl: anchorEl, onClose: () => setAnchorEl(undefined) })) : null));
131
139
  });
140
+ function onSyntenyClick(event, model) {
141
+ const ref1 = model.clickMapCanvas;
142
+ const ref2 = model.cigarClickMapCanvas;
143
+ if (!ref1 || !ref2) {
144
+ return;
145
+ }
146
+ const rect = ref1.getBoundingClientRect();
147
+ const ctx1 = ref1.getContext('2d');
148
+ const ctx2 = ref2.getContext('2d');
149
+ if (!ctx1 || !ctx2) {
150
+ return;
151
+ }
152
+ const x = event.clientX - rect.left;
153
+ const y = event.clientY - rect.top;
154
+ const [r1, g1, b1] = ctx1.getImageData(x, y, 1, 1).data;
155
+ const unitMultiplier = Math.floor(MAX_COLOR_RANGE / model.numFeats);
156
+ const id = getId(r1, g1, b1, unitMultiplier);
157
+ const feat = model.featPositions[id];
158
+ if (feat) {
159
+ const { f } = feat;
160
+ model.setClickId(f.id());
161
+ const session = getSession(model);
162
+ if (isSessionModelWithWidgets(session)) {
163
+ session.showWidget(session.addWidget('SyntenyFeatureWidget', 'syntenyFeature', {
164
+ featureData: {
165
+ feature1: f.toJSON(),
166
+ feature2: f.get('mate'),
167
+ },
168
+ }));
169
+ }
170
+ }
171
+ return feat;
172
+ }
173
+ function onSyntenyContextClick(event, model, setAnchorEl) {
174
+ event.preventDefault();
175
+ const ref1 = model.clickMapCanvas;
176
+ const ref2 = model.cigarClickMapCanvas;
177
+ if (!ref1 || !ref2) {
178
+ return;
179
+ }
180
+ const rect = ref1.getBoundingClientRect();
181
+ const ctx1 = ref1.getContext('2d');
182
+ const ctx2 = ref2.getContext('2d');
183
+ if (!ctx1 || !ctx2) {
184
+ return;
185
+ }
186
+ const { clientX, clientY } = event;
187
+ const x = clientX - rect.left;
188
+ const y = clientY - rect.top;
189
+ const [r1, g1, b1] = ctx1.getImageData(x, y, 1, 1).data;
190
+ const unitMultiplier = Math.floor(MAX_COLOR_RANGE / model.numFeats);
191
+ const id = getId(r1, g1, b1, unitMultiplier);
192
+ const f = model.featPositions[id];
193
+ if (f) {
194
+ model.setClickId(f.f.id());
195
+ setAnchorEl({ clientX, clientY, feature: f });
196
+ }
197
+ }
198
+ function getTooltip(f, cigarOp, cigarOpLen) {
199
+ // @ts-expect-error
200
+ const f1 = f.toJSON();
201
+ const f2 = f1.mate;
202
+ const l1 = f1.end - f1.start;
203
+ const l2 = f2.end - f2.start;
204
+ const identity = f1.identity;
205
+ const n1 = f1.name;
206
+ const n2 = f2.name;
207
+ return [
208
+ `Loc1: ${assembleLocString(f1)}`,
209
+ `Loc2: ${assembleLocString(f2)}`,
210
+ `Inverted: ${f1.strand === -1}`,
211
+ `Query len: ${l1.toLocaleString('en-US')}`,
212
+ `Target len: ${l2.toLocaleString('en-US')}`,
213
+ identity ? `Identity: ${identity.toPrecision(2)}` : '',
214
+ cigarOp ? `CIGAR operator: ${cigarOp}${cigarOpLen}` : '',
215
+ n1 ? `Name 1: ${n1}` : '',
216
+ n2 ? `Name 1: ${n2}` : '',
217
+ ]
218
+ .filter(f => !!f)
219
+ .join('<br/>');
220
+ }
132
221
  export default LinearSyntenyRendering;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { LinearSyntenyDisplayModel } from '../model';
3
+ interface ClickCoord {
4
+ clientX: number;
5
+ clientY: number;
6
+ feature: any;
7
+ }
8
+ export default function SyntenyContextMenu({ model, onClose, anchorEl, }: {
9
+ onClose: () => void;
10
+ model: LinearSyntenyDisplayModel;
11
+ anchorEl: ClickCoord;
12
+ }): React.JSX.Element;
13
+ export {};