@jbrowse/plugin-linear-comparative-view 2.16.0 → 2.16.1
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/LGVSyntenyDisplay/components/LaunchSyntenyViewDialog.d.ts +2 -1
- package/dist/LGVSyntenyDisplay/components/LaunchSyntenyViewDialog.js +4 -2
- package/dist/LGVSyntenyDisplay/components/util.d.ts +4 -1
- package/dist/LGVSyntenyDisplay/components/util.js +7 -15
- package/dist/LGVSyntenyDisplay/model.d.ts +26 -14
- package/dist/LGVSyntenyDisplay/model.js +23 -1
- package/dist/LinearComparativeDisplay/stateModelFactory.d.ts +28 -5
- package/dist/LinearComparativeDisplay/stateModelFactory.js +14 -3
- package/dist/LinearSyntenyDisplay/afterAttach.js +2 -3
- package/dist/LinearSyntenyDisplay/components/LinearSyntenyRendering.js +22 -15
- package/dist/LinearSyntenyDisplay/components/SyntenyContextMenu.js +2 -4
- package/dist/LinearSyntenyDisplay/components/util.d.ts +5 -1
- package/dist/LinearSyntenyDisplay/components/util.js +7 -9
- package/dist/LinearSyntenyDisplay/drawSynteny.js +7 -9
- package/dist/LinearSyntenyDisplay/model.d.ts +48 -11
- package/dist/LinearSyntenyDisplay/model.js +38 -22
- package/dist/SyntenyFeatureDetail/SyntenyFeatureDetail.d.ts +14 -0
- package/dist/SyntenyFeatureDetail/SyntenyFeatureDetail.js +100 -0
- package/dist/SyntenyFeatureDetail/index.d.ts +2 -0
- package/dist/SyntenyFeatureDetail/index.js +56 -0
- package/dist/index.js +2 -0
- package/esm/LGVSyntenyDisplay/components/LaunchSyntenyViewDialog.d.ts +2 -1
- package/esm/LGVSyntenyDisplay/components/LaunchSyntenyViewDialog.js +4 -2
- package/esm/LGVSyntenyDisplay/components/util.d.ts +4 -1
- package/esm/LGVSyntenyDisplay/components/util.js +8 -16
- package/esm/LGVSyntenyDisplay/model.d.ts +26 -14
- package/esm/LGVSyntenyDisplay/model.js +25 -3
- package/esm/LinearComparativeDisplay/stateModelFactory.d.ts +28 -5
- package/esm/LinearComparativeDisplay/stateModelFactory.js +14 -3
- package/esm/LinearSyntenyDisplay/afterAttach.js +3 -4
- package/esm/LinearSyntenyDisplay/components/LinearSyntenyRendering.js +22 -15
- package/esm/LinearSyntenyDisplay/components/SyntenyContextMenu.js +2 -4
- package/esm/LinearSyntenyDisplay/components/util.d.ts +5 -1
- package/esm/LinearSyntenyDisplay/components/util.js +7 -9
- package/esm/LinearSyntenyDisplay/drawSynteny.js +7 -9
- package/esm/LinearSyntenyDisplay/model.d.ts +48 -11
- package/esm/LinearSyntenyDisplay/model.js +39 -23
- package/esm/SyntenyFeatureDetail/SyntenyFeatureDetail.d.ts +14 -0
- package/esm/SyntenyFeatureDetail/SyntenyFeatureDetail.js +72 -0
- package/esm/SyntenyFeatureDetail/index.d.ts +2 -0
- package/esm/SyntenyFeatureDetail/index.js +27 -0
- package/esm/index.js +2 -0
- package/package.json +3 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AnyConfigurationSchemaType } from '@jbrowse/core/configuration';
|
|
2
|
+
import { Feature } from '@jbrowse/core/util';
|
|
2
3
|
/**
|
|
3
4
|
* #stateModel LGVSyntenyDisplay
|
|
4
5
|
* displays location of "synteny" feature in a plain LGV, allowing linking out
|
|
@@ -10,7 +11,14 @@ import { AnyConfigurationSchemaType } from '@jbrowse/core/configuration';
|
|
|
10
11
|
declare function stateModelFactory(schema: AnyConfigurationSchemaType): import("mobx-state-tree").IModelType<{
|
|
11
12
|
id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
12
13
|
type: import("mobx-state-tree").ISimpleType<string>;
|
|
13
|
-
rpcDriverName: import("mobx-state-tree"
|
|
14
|
+
rpcDriverName: import("mobx-state-tree" /**
|
|
15
|
+
* #stateModel LGVSyntenyDisplay
|
|
16
|
+
* displays location of "synteny" feature in a plain LGV, allowing linking out
|
|
17
|
+
* to external synteny views
|
|
18
|
+
*
|
|
19
|
+
* extends
|
|
20
|
+
* - [SharedLinearPileupDisplayMixin](../sharedlinearpileupdisplaymixin)
|
|
21
|
+
*/).IMaybe<import("mobx-state-tree").ISimpleType<string>>;
|
|
14
22
|
} & {
|
|
15
23
|
heightPreConfig: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<number>>;
|
|
16
24
|
} & {
|
|
@@ -38,7 +46,7 @@ declare function stateModelFactory(schema: AnyConfigurationSchemaType): import("
|
|
|
38
46
|
renderInProgress: AbortController | undefined;
|
|
39
47
|
filled: boolean;
|
|
40
48
|
reactElement: React.ReactElement | undefined;
|
|
41
|
-
features: Map<string,
|
|
49
|
+
features: Map<string, Feature> | undefined;
|
|
42
50
|
layout: any;
|
|
43
51
|
status: string;
|
|
44
52
|
error: unknown;
|
|
@@ -63,7 +71,7 @@ declare function stateModelFactory(schema: AnyConfigurationSchemaType): import("
|
|
|
63
71
|
setMessage(messageText: string): void;
|
|
64
72
|
setRendered(props: {
|
|
65
73
|
reactElement: React.ReactElement;
|
|
66
|
-
features: Map<string,
|
|
74
|
+
features: Map<string, Feature>;
|
|
67
75
|
layout: any;
|
|
68
76
|
maxHeightReached: boolean;
|
|
69
77
|
renderProps: any;
|
|
@@ -223,7 +231,7 @@ declare function stateModelFactory(schema: AnyConfigurationSchemaType): import("
|
|
|
223
231
|
regionCannotBeRendered(_region: import("@jbrowse/core/util").Region): import("react").JSX.Element | null;
|
|
224
232
|
} & {
|
|
225
233
|
featureIdUnderMouse: undefined | string;
|
|
226
|
-
contextMenuFeature: undefined |
|
|
234
|
+
contextMenuFeature: undefined | Feature;
|
|
227
235
|
} & {
|
|
228
236
|
readonly DisplayMessageComponent: import("react").FC<any> | undefined;
|
|
229
237
|
readonly blockType: "dynamicBlocks" | "staticBlocks";
|
|
@@ -233,19 +241,19 @@ declare function stateModelFactory(schema: AnyConfigurationSchemaType): import("
|
|
|
233
241
|
readonly TooltipComponent: import("@jbrowse/core/util").AnyReactComponentType;
|
|
234
242
|
readonly selectedFeatureId: string | undefined;
|
|
235
243
|
} & {
|
|
236
|
-
readonly features: import("@jbrowse/core/util/compositeMap").default<string,
|
|
237
|
-
readonly featureUnderMouse:
|
|
244
|
+
readonly features: import("@jbrowse/core/util/compositeMap").default<string, Feature>;
|
|
245
|
+
readonly featureUnderMouse: Feature | undefined;
|
|
238
246
|
getFeatureOverlapping(blockKey: string, x: number, y: number): string | undefined;
|
|
239
247
|
getFeatureByID(blockKey: string, id: string): [number, number, number, number] | undefined;
|
|
240
248
|
searchFeatureByID(id: string): [number, number, number, number] | undefined;
|
|
241
249
|
} & {
|
|
242
250
|
addBlock(key: string, block: import("@jbrowse/core/util/blockTypes").BaseBlock): void;
|
|
243
251
|
deleteBlock(key: string): void;
|
|
244
|
-
selectFeature(feature:
|
|
245
|
-
navToFeature(feature:
|
|
252
|
+
selectFeature(feature: Feature): void;
|
|
253
|
+
navToFeature(feature: Feature): void;
|
|
246
254
|
clearFeatureSelection(): void;
|
|
247
255
|
setFeatureIdUnderMouse(feature?: string): void;
|
|
248
|
-
setContextMenuFeature(feature?:
|
|
256
|
+
setContextMenuFeature(feature?: Feature): void;
|
|
249
257
|
} & {
|
|
250
258
|
reload(): Promise<void>;
|
|
251
259
|
} & {
|
|
@@ -257,7 +265,7 @@ declare function stateModelFactory(schema: AnyConfigurationSchemaType): import("
|
|
|
257
265
|
afterAttach(): void;
|
|
258
266
|
} & {
|
|
259
267
|
colorTagMap: import("mobx").ObservableMap<string, string>;
|
|
260
|
-
featureUnderMouseVolatile: undefined |
|
|
268
|
+
featureUnderMouseVolatile: undefined | Feature;
|
|
261
269
|
tagsReady: boolean;
|
|
262
270
|
} & {
|
|
263
271
|
readonly autorunReady: boolean;
|
|
@@ -272,9 +280,9 @@ declare function stateModelFactory(schema: AnyConfigurationSchemaType): import("
|
|
|
272
280
|
extra?: import("@jbrowse/plugin-alignments/src/shared/color").ExtraColorBy;
|
|
273
281
|
}): void;
|
|
274
282
|
updateColorTagMap(uniqueTag: string[]): void;
|
|
275
|
-
setFeatureUnderMouse(feat?:
|
|
276
|
-
selectFeature(feature:
|
|
277
|
-
copyFeatureToClipboard(feature:
|
|
283
|
+
setFeatureUnderMouse(feat?: Feature): void;
|
|
284
|
+
selectFeature(feature: Feature): void;
|
|
285
|
+
copyFeatureToClipboard(feature: Feature): void;
|
|
278
286
|
setConfig(conf: import("@jbrowse/core/configuration").AnyConfigurationModel): void;
|
|
279
287
|
setFilterBy(filter: import("@jbrowse/plugin-alignments/src/shared").IFilter): void;
|
|
280
288
|
setJexlFilters(filters: string[]): void;
|
|
@@ -293,7 +301,7 @@ declare function stateModelFactory(schema: AnyConfigurationSchemaType): import("
|
|
|
293
301
|
} & {
|
|
294
302
|
readonly maxHeight: any;
|
|
295
303
|
readonly featureHeightSetting: any;
|
|
296
|
-
readonly featureUnderMouse:
|
|
304
|
+
readonly featureUnderMouse: Feature | undefined;
|
|
297
305
|
renderReady(): boolean;
|
|
298
306
|
readonly filters: import("@jbrowse/core/pluggableElementTypes/renderers/util/serializableFilterChain").default;
|
|
299
307
|
} & {
|
|
@@ -352,6 +360,10 @@ declare function stateModelFactory(schema: AnyConfigurationSchemaType): import("
|
|
|
352
360
|
}[];
|
|
353
361
|
})[];
|
|
354
362
|
} & {
|
|
363
|
+
/**
|
|
364
|
+
* #action
|
|
365
|
+
*/
|
|
366
|
+
selectFeature(feature: Feature): void;
|
|
355
367
|
afterCreate(): void;
|
|
356
368
|
}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>;
|
|
357
369
|
export default stateModelFactory;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { lazy } from 'react';
|
|
2
|
-
import { ConfigurationReference, } from '@jbrowse/core/configuration';
|
|
3
|
-
import { getSession } from '@jbrowse/core/util';
|
|
2
|
+
import { ConfigurationReference, getConf, } from '@jbrowse/core/configuration';
|
|
3
|
+
import { getContainingTrack, getContainingView, getSession, isSessionModelWithWidgets, } from '@jbrowse/core/util';
|
|
4
4
|
import { SharedLinearPileupDisplayMixin } from '@jbrowse/plugin-alignments';
|
|
5
5
|
import { types } from 'mobx-state-tree';
|
|
6
6
|
const LaunchSyntenyViewDialog = lazy(() => import('./components/LaunchSyntenyViewDialog'));
|
|
@@ -37,12 +37,13 @@ function stateModelFactory(schema) {
|
|
|
37
37
|
...(feature
|
|
38
38
|
? [
|
|
39
39
|
{
|
|
40
|
-
label: '
|
|
40
|
+
label: 'Launch synteny view for this position',
|
|
41
41
|
onClick: () => {
|
|
42
42
|
getSession(self).queueDialog(handleClose => [
|
|
43
43
|
LaunchSyntenyViewDialog,
|
|
44
44
|
{
|
|
45
45
|
model: self,
|
|
46
|
+
trackId: getConf(getContainingTrack(self), 'trackId'),
|
|
46
47
|
handleClose,
|
|
47
48
|
feature,
|
|
48
49
|
},
|
|
@@ -73,6 +74,27 @@ function stateModelFactory(schema) {
|
|
|
73
74
|
};
|
|
74
75
|
})
|
|
75
76
|
.actions(self => ({
|
|
77
|
+
/**
|
|
78
|
+
* #action
|
|
79
|
+
*/
|
|
80
|
+
selectFeature(feature) {
|
|
81
|
+
const session = getSession(self);
|
|
82
|
+
if (isSessionModelWithWidgets(session)) {
|
|
83
|
+
const r2 = getContainingView(self);
|
|
84
|
+
let r3 = r2;
|
|
85
|
+
try {
|
|
86
|
+
r3 = getContainingView(r3);
|
|
87
|
+
}
|
|
88
|
+
catch (e) { }
|
|
89
|
+
const featureWidget = session.addWidget('SyntenyFeatureWidget', 'syntenyFeature', {
|
|
90
|
+
featureData: feature.toJSON(),
|
|
91
|
+
view: r3,
|
|
92
|
+
track: getContainingTrack(self),
|
|
93
|
+
});
|
|
94
|
+
session.showWidget(featureWidget);
|
|
95
|
+
}
|
|
96
|
+
session.setSelection(feature);
|
|
97
|
+
},
|
|
76
98
|
afterCreate() {
|
|
77
99
|
// use color by stand to help indicate inversions better on first load,
|
|
78
100
|
// otherwise use selected orientation
|
|
@@ -43,7 +43,10 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
43
43
|
message: string | undefined;
|
|
44
44
|
}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
|
|
45
45
|
onHorizontalScroll?: () => void;
|
|
46
|
-
blockState
|
|
46
|
+
blockState? /**
|
|
47
|
+
* #action
|
|
48
|
+
* controlled by a reaction
|
|
49
|
+
*/: Record<string, any>;
|
|
47
50
|
}>;
|
|
48
51
|
readonly DisplayBlurb: React.FC<{
|
|
49
52
|
model: {
|
|
@@ -55,7 +58,10 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
55
58
|
error: unknown;
|
|
56
59
|
message: string | undefined;
|
|
57
60
|
} & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
|
|
58
|
-
id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree"
|
|
61
|
+
id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree" /**
|
|
62
|
+
* #action
|
|
63
|
+
* controlled by a reaction
|
|
64
|
+
*/).ISimpleType<string>, [undefined]>;
|
|
59
65
|
type: import("mobx-state-tree").ISimpleType<string>;
|
|
60
66
|
rpcDriverName: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
|
|
61
67
|
}, {
|
|
@@ -82,6 +88,14 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
82
88
|
features: Feature[] | undefined;
|
|
83
89
|
message: string | undefined;
|
|
84
90
|
} & {
|
|
91
|
+
/**
|
|
92
|
+
* #getter
|
|
93
|
+
*/
|
|
94
|
+
readonly level: number;
|
|
95
|
+
/**
|
|
96
|
+
* #getter
|
|
97
|
+
*/
|
|
98
|
+
readonly height: number;
|
|
85
99
|
/**
|
|
86
100
|
* #getter
|
|
87
101
|
*/
|
|
@@ -126,7 +140,10 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
126
140
|
message: string | undefined;
|
|
127
141
|
}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
|
|
128
142
|
onHorizontalScroll?: () => void;
|
|
129
|
-
blockState
|
|
143
|
+
blockState? /**
|
|
144
|
+
* #action
|
|
145
|
+
* controlled by a reaction
|
|
146
|
+
*/: Record<string, any>;
|
|
130
147
|
}>;
|
|
131
148
|
readonly DisplayBlurb: React.FC<{
|
|
132
149
|
model: {
|
|
@@ -138,7 +155,10 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
138
155
|
error: unknown;
|
|
139
156
|
message: string | undefined;
|
|
140
157
|
} & import("mobx-state-tree").IStateTreeNode<import("mobx-state-tree").IModelType<{
|
|
141
|
-
id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree"
|
|
158
|
+
id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree" /**
|
|
159
|
+
* #action
|
|
160
|
+
* controlled by a reaction
|
|
161
|
+
*/).ISimpleType<string>, [undefined]>;
|
|
142
162
|
type: import("mobx-state-tree").ISimpleType<string>;
|
|
143
163
|
rpcDriverName: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
|
|
144
164
|
}, {
|
|
@@ -201,7 +221,10 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
|
|
|
201
221
|
message: string | undefined;
|
|
202
222
|
}, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
|
|
203
223
|
onHorizontalScroll?: () => void;
|
|
204
|
-
blockState
|
|
224
|
+
blockState? /**
|
|
225
|
+
* #action
|
|
226
|
+
* controlled by a reaction
|
|
227
|
+
*/: Record<string, any>;
|
|
205
228
|
}>;
|
|
206
229
|
readonly DisplayBlurb: React.FC<{
|
|
207
230
|
model: {
|
|
@@ -26,6 +26,18 @@ function stateModelFactory(configSchema) {
|
|
|
26
26
|
message: undefined,
|
|
27
27
|
}))
|
|
28
28
|
.views(self => ({
|
|
29
|
+
/**
|
|
30
|
+
* #getter
|
|
31
|
+
*/
|
|
32
|
+
get level() {
|
|
33
|
+
return getParent(self, 4).level;
|
|
34
|
+
},
|
|
35
|
+
/**
|
|
36
|
+
* #getter
|
|
37
|
+
*/
|
|
38
|
+
get height() {
|
|
39
|
+
return getParent(self, 4).height;
|
|
40
|
+
},
|
|
29
41
|
/**
|
|
30
42
|
* #getter
|
|
31
43
|
*/
|
|
@@ -131,7 +143,7 @@ function renderBlockData(self) {
|
|
|
131
143
|
// renderProps is something under our control. Compare to
|
|
132
144
|
// serverSideRenderedBlock
|
|
133
145
|
readConfObject(self.configuration);
|
|
134
|
-
const { adapterConfig } = self;
|
|
146
|
+
const { level, adapterConfig } = self;
|
|
135
147
|
const parent = getContainingView(self);
|
|
136
148
|
const sessionId = getRpcSessionId(self);
|
|
137
149
|
getSnapshot(parent);
|
|
@@ -140,8 +152,7 @@ function renderBlockData(self) {
|
|
|
140
152
|
rpcManager,
|
|
141
153
|
renderProps: {
|
|
142
154
|
...display.renderProps(),
|
|
143
|
-
|
|
144
|
-
level: getParent(self, 4).level,
|
|
155
|
+
level,
|
|
145
156
|
view: parent,
|
|
146
157
|
adapterConfig,
|
|
147
158
|
sessionId,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { reaction, autorun } from 'mobx';
|
|
2
|
+
import { addDisposer, getSnapshot } from 'mobx-state-tree';
|
|
2
3
|
import { getContainingView, getSession } from '@jbrowse/core/util';
|
|
3
4
|
import { bpToPx } from '@jbrowse/core/util/Base1DUtils';
|
|
4
5
|
import { MismatchParser } from '@jbrowse/plugin-alignments';
|
|
5
|
-
import { reaction, autorun } from 'mobx';
|
|
6
6
|
import { drawMouseoverSynteny, drawRef } from './drawSynteny';
|
|
7
7
|
export function doAfterAttach(self) {
|
|
8
8
|
addDisposer(self, autorun(() => {
|
|
@@ -48,10 +48,9 @@ export function doAfterAttach(self) {
|
|
|
48
48
|
if (!initialized) {
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
|
+
const { level } = self;
|
|
51
52
|
const { assemblyManager } = getSession(self);
|
|
52
53
|
const view = getContainingView(self);
|
|
53
|
-
// @ts-expect-error
|
|
54
|
-
const level = getParent(self, 4).level;
|
|
55
54
|
const viewSnaps = view.views.map(view => ({
|
|
56
55
|
...getSnapshot(view),
|
|
57
56
|
width: view.width,
|
|
@@ -18,6 +18,7 @@ const useStyles = makeStyles()({
|
|
|
18
18
|
position: 'relative',
|
|
19
19
|
},
|
|
20
20
|
mouseoverCanvas: {
|
|
21
|
+
imageRendering: 'pixelated',
|
|
21
22
|
position: 'absolute',
|
|
22
23
|
pointEvents: 'none',
|
|
23
24
|
},
|
|
@@ -27,6 +28,7 @@ const useStyles = makeStyles()({
|
|
|
27
28
|
});
|
|
28
29
|
const LinearSyntenyRendering = observer(function ({ model, }) {
|
|
29
30
|
const { classes } = useStyles();
|
|
31
|
+
const { mouseoverId, height } = model;
|
|
30
32
|
const xOffset = useRef(0);
|
|
31
33
|
const view = getContainingView(model);
|
|
32
34
|
const width = view.width;
|
|
@@ -39,20 +41,19 @@ const LinearSyntenyRendering = observer(function ({ model, }) {
|
|
|
39
41
|
const [mouseCurrDownX, setMouseCurrDownX] = useState();
|
|
40
42
|
const [mouseInitialDownX, setMouseInitialDownX] = useState();
|
|
41
43
|
const [currY, setCurrY] = useState();
|
|
42
|
-
const
|
|
43
|
-
const k2p = useRef();
|
|
44
|
+
const mainSyntenyCanvasRefp = useRef();
|
|
44
45
|
// these useCallbacks avoid new refs from being created on any mouseover,
|
|
45
46
|
// etc.
|
|
46
47
|
// biome-ignore lint/correctness/useExhaustiveDependencies:
|
|
47
|
-
const
|
|
48
|
+
const mouseoverDetectionCanvasRef = useCallback((ref) => {
|
|
48
49
|
model.setMouseoverCanvasRef(ref);
|
|
49
50
|
},
|
|
50
51
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
51
52
|
[model, height, width]);
|
|
52
53
|
// biome-ignore lint/correctness/useExhaustiveDependencies:
|
|
53
|
-
const
|
|
54
|
+
const mainSyntenyCanvasRef = useCallback((ref) => {
|
|
54
55
|
model.setMainCanvasRef(ref);
|
|
55
|
-
|
|
56
|
+
mainSyntenyCanvasRefp.current = ref; // this ref is additionally used in useEffect below
|
|
56
57
|
},
|
|
57
58
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
58
59
|
[model, height, width]);
|
|
@@ -75,7 +76,9 @@ const LinearSyntenyRendering = observer(function ({ model, }) {
|
|
|
75
76
|
v.setScaleFactor(1);
|
|
76
77
|
v.zoomTo(delta.current > 0
|
|
77
78
|
? v.bpPerPx * (1 + delta.current)
|
|
78
|
-
: v.bpPerPx / (1 - delta.current), event.clientX -
|
|
79
|
+
: v.bpPerPx / (1 - delta.current), event.clientX -
|
|
80
|
+
(((_a = mainSyntenyCanvasRefp.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().left) ||
|
|
81
|
+
0));
|
|
79
82
|
}
|
|
80
83
|
delta.current = 0;
|
|
81
84
|
}, 300);
|
|
@@ -98,28 +101,28 @@ const LinearSyntenyRendering = observer(function ({ model, }) {
|
|
|
98
101
|
}
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
|
-
(_a =
|
|
104
|
+
(_a = mainSyntenyCanvasRefp.current) === null || _a === void 0 ? void 0 : _a.addEventListener('wheel', onWheel);
|
|
102
105
|
return () => {
|
|
103
106
|
var _a;
|
|
104
|
-
(_a =
|
|
107
|
+
(_a = mainSyntenyCanvasRefp.current) === null || _a === void 0 ? void 0 : _a.removeEventListener('wheel', onWheel);
|
|
105
108
|
};
|
|
106
109
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
107
110
|
}, [model, height, width]);
|
|
108
111
|
// biome-ignore lint/correctness/useExhaustiveDependencies:
|
|
109
|
-
const
|
|
112
|
+
const clickMapCanvasRef = useCallback((ref) => {
|
|
110
113
|
model.setClickMapCanvasRef(ref);
|
|
111
114
|
},
|
|
112
115
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
113
116
|
[model, height, width]);
|
|
114
117
|
// biome-ignore lint/correctness/useExhaustiveDependencies:
|
|
115
|
-
const
|
|
118
|
+
const cigarClickMapCanvasRef = useCallback((ref) => {
|
|
116
119
|
model.setCigarClickMapCanvasRef(ref);
|
|
117
120
|
},
|
|
118
121
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
119
122
|
[model, height, width]);
|
|
120
123
|
return (React.createElement("div", { className: classes.rel },
|
|
121
|
-
React.createElement("canvas", { ref:
|
|
122
|
-
React.createElement("canvas", { ref:
|
|
124
|
+
React.createElement("canvas", { ref: mouseoverDetectionCanvasRef, width: width, height: height, className: classes.mouseoverCanvas }),
|
|
125
|
+
React.createElement("canvas", { ref: mainSyntenyCanvasRef, onMouseMove: event => {
|
|
123
126
|
var _a;
|
|
124
127
|
if (mouseCurrDownX !== undefined) {
|
|
125
128
|
xOffset.current += mouseCurrDownX - event.clientX;
|
|
@@ -166,7 +169,11 @@ const LinearSyntenyRendering = observer(function ({ model, }) {
|
|
|
166
169
|
const { f, cigar } = model.featPositions[id];
|
|
167
170
|
const unitMultiplier2 = Math.floor(MAX_COLOR_RANGE / cigar.length);
|
|
168
171
|
const cigarIdx = getId(r2, g2, b2, unitMultiplier2);
|
|
169
|
-
setTooltip(getTooltip(
|
|
172
|
+
setTooltip(getTooltip({
|
|
173
|
+
feature: f,
|
|
174
|
+
cigarOp: cigar[cigarIdx],
|
|
175
|
+
cigarOpLen: cigar[cigarIdx + 1],
|
|
176
|
+
}));
|
|
170
177
|
}
|
|
171
178
|
}
|
|
172
179
|
}, onMouseLeave: () => {
|
|
@@ -185,8 +192,8 @@ const LinearSyntenyRendering = observer(function ({ model, }) {
|
|
|
185
192
|
}, onContextMenu: evt => {
|
|
186
193
|
onSynContextClick(evt, model, setAnchorEl);
|
|
187
194
|
}, "data-testid": "synteny_canvas", className: classes.mainCanvas, width: width, height: height }),
|
|
188
|
-
React.createElement("canvas", { ref:
|
|
189
|
-
React.createElement("canvas", { ref:
|
|
195
|
+
React.createElement("canvas", { ref: clickMapCanvasRef, className: classes.pix, width: width, height: height }),
|
|
196
|
+
React.createElement("canvas", { ref: cigarClickMapCanvasRef, className: classes.pix, width: width, height: height }),
|
|
190
197
|
mouseoverId && tooltip && currX && currY ? (React.createElement(SyntenyTooltip, { title: tooltip })) : null,
|
|
191
198
|
anchorEl ? (React.createElement(SyntenyContextMenu, { model: model, anchorEl: anchorEl, onClose: () => {
|
|
192
199
|
setAnchorEl(undefined);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { getContainingView, getSession } from '@jbrowse/core/util';
|
|
3
3
|
import { Menu } from '@jbrowse/core/ui';
|
|
4
|
-
import { getParent } from 'mobx-state-tree';
|
|
5
4
|
export default function SyntenyContextMenu({ model, onClose, anchorEl, }) {
|
|
6
5
|
const view = getContainingView(model);
|
|
7
6
|
const { clientX, clientY, feature } = anchorEl;
|
|
@@ -30,13 +29,12 @@ export default function SyntenyContextMenu({ model, onClose, anchorEl, }) {
|
|
|
30
29
|
label: 'Center on feature',
|
|
31
30
|
onClick: () => {
|
|
32
31
|
const { f } = feature;
|
|
33
|
-
const track = getParent(model, 4);
|
|
34
32
|
const start = f.get('start');
|
|
35
33
|
const end = f.get('end');
|
|
36
34
|
const refName = f.get('refName');
|
|
37
35
|
const mate = f.get('mate');
|
|
38
|
-
const l1 = view.views[
|
|
39
|
-
const l2 = view.views[
|
|
36
|
+
const l1 = view.views[model.level];
|
|
37
|
+
const l2 = view.views[model.level + 1];
|
|
40
38
|
l1.navToLocString(`${refName}:${start}-${end}`).catch((e) => {
|
|
41
39
|
const err = `${l1.assemblyNames[0]}:${e}`;
|
|
42
40
|
console.error(err);
|
|
@@ -36,5 +36,9 @@ export declare function drawBox(ctx: CanvasRenderingContext2D, x1: number, x2: n
|
|
|
36
36
|
export declare function drawBezierBox(ctx: CanvasRenderingContext2D, x1: number, x2: number, y1: number, x3: number, x4: number, y2: number, mid: number): void;
|
|
37
37
|
export declare function onSynClick(event: React.MouseEvent, model: LinearSyntenyDisplayModel): import("../model").FeatPos | undefined;
|
|
38
38
|
export declare function onSynContextClick(event: React.MouseEvent, model: LinearSyntenyDisplayModel, setAnchorEl: (arg: ClickCoord) => void): void;
|
|
39
|
-
export declare function getTooltip(
|
|
39
|
+
export declare function getTooltip({ feature, cigarOp, cigarOpLen, }: {
|
|
40
|
+
feature: Feature;
|
|
41
|
+
cigarOp?: string;
|
|
42
|
+
cigarOpLen?: string;
|
|
43
|
+
}): string;
|
|
40
44
|
export {};
|
|
@@ -54,7 +54,6 @@ export function drawBox(ctx, x1, x2, y1, x3, x4, y2) {
|
|
|
54
54
|
ctx.lineTo(x3, y2);
|
|
55
55
|
ctx.lineTo(x4, y2);
|
|
56
56
|
ctx.closePath();
|
|
57
|
-
ctx.fill();
|
|
58
57
|
}
|
|
59
58
|
export function drawBezierBox(ctx, x1, x2, y1, x3, x4, y2, mid) {
|
|
60
59
|
const len1 = Math.abs(x1 - x2);
|
|
@@ -74,13 +73,11 @@ export function drawBezierBox(ctx, x1, x2, y1, x3, x4, y2, mid) {
|
|
|
74
73
|
ctx.lineTo(x4, y2);
|
|
75
74
|
ctx.bezierCurveTo(x4, mid, x1, mid, x1, y1);
|
|
76
75
|
ctx.closePath();
|
|
77
|
-
ctx.fill();
|
|
78
76
|
}
|
|
79
77
|
export function onSynClick(event, model) {
|
|
80
78
|
const view = getContainingView(model);
|
|
81
79
|
const track = getContainingTrack(model);
|
|
82
|
-
const ref1 = model
|
|
83
|
-
const ref2 = model.cigarClickMapCanvas;
|
|
80
|
+
const { featPositions, numFeats, clickMapCanvas: ref1, cigarClickMapCanvas: ref2, level, } = model;
|
|
84
81
|
if (!ref1 || !ref2) {
|
|
85
82
|
return;
|
|
86
83
|
}
|
|
@@ -93,18 +90,19 @@ export function onSynClick(event, model) {
|
|
|
93
90
|
const x = event.clientX - rect.left;
|
|
94
91
|
const y = event.clientY - rect.top;
|
|
95
92
|
const [r1, g1, b1] = ctx1.getImageData(x, y, 1, 1).data;
|
|
96
|
-
const unitMultiplier = Math.floor(MAX_COLOR_RANGE /
|
|
93
|
+
const unitMultiplier = Math.floor(MAX_COLOR_RANGE / numFeats);
|
|
97
94
|
const id = getId(r1, g1, b1, unitMultiplier);
|
|
98
|
-
const feat =
|
|
95
|
+
const feat = featPositions[id];
|
|
99
96
|
if (feat) {
|
|
100
97
|
const { f } = feat;
|
|
101
98
|
model.setClickId(f.id());
|
|
102
99
|
const session = getSession(model);
|
|
103
100
|
if (isSessionModelWithWidgets(session)) {
|
|
104
|
-
session.showWidget(session.addWidget('
|
|
101
|
+
session.showWidget(session.addWidget('SyntenyFeatureWidget', 'syntenyFeature', {
|
|
105
102
|
view,
|
|
106
103
|
track,
|
|
107
104
|
featureData: f.toJSON(),
|
|
105
|
+
level,
|
|
108
106
|
}));
|
|
109
107
|
}
|
|
110
108
|
}
|
|
@@ -135,9 +133,9 @@ export function onSynContextClick(event, model, setAnchorEl) {
|
|
|
135
133
|
setAnchorEl({ clientX, clientY, feature: f });
|
|
136
134
|
}
|
|
137
135
|
}
|
|
138
|
-
export function getTooltip(
|
|
136
|
+
export function getTooltip({ feature, cigarOp, cigarOpLen, }) {
|
|
139
137
|
// @ts-expect-error
|
|
140
|
-
const f1 =
|
|
138
|
+
const f1 = feature.toJSON();
|
|
141
139
|
const f2 = f1.mate;
|
|
142
140
|
const l1 = f1.end - f1.start;
|
|
143
141
|
const l2 = f2.end - f2.start;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { doesIntersect2, getContainingView } from '@jbrowse/core/util';
|
|
2
2
|
import { draw, drawMatchSimple } from './components/util';
|
|
3
|
-
import { getParent } from 'mobx-state-tree';
|
|
4
3
|
export const MAX_COLOR_RANGE = 255 * 255 * 255; // max color range
|
|
5
4
|
function makeColor(idx) {
|
|
6
5
|
const r = Math.floor(idx / (255 * 255)) % 255;
|
|
@@ -24,11 +23,9 @@ export function getId(r, g, b, unitMultiplier) {
|
|
|
24
23
|
export function drawRef(model, ctx1, ctx3) {
|
|
25
24
|
var _a;
|
|
26
25
|
const view = getContainingView(model);
|
|
27
|
-
// @ts-expect-error
|
|
28
|
-
const level = getParent(model, 4).level;
|
|
29
26
|
const drawCurves = view.drawCurves;
|
|
30
27
|
const drawCIGAR = view.drawCIGAR;
|
|
31
|
-
const { height, featPositions } = model;
|
|
28
|
+
const { level, height, featPositions } = model;
|
|
32
29
|
const width = view.width;
|
|
33
30
|
const bpPerPxs = view.views.map(v => v.bpPerPx);
|
|
34
31
|
if (ctx3) {
|
|
@@ -144,9 +141,11 @@ export function drawRef(model, ctx1, ctx3) {
|
|
|
144
141
|
colorMap[(continuingFlag && d1 > 1) || d2 > 1 ? op : 'M'];
|
|
145
142
|
continuingFlag = false;
|
|
146
143
|
draw(ctx1, px1, cx1, y1, cx2, px2, y2, mid, drawCurves);
|
|
144
|
+
ctx1.fill();
|
|
147
145
|
if (ctx3) {
|
|
148
146
|
ctx3.fillStyle = makeColor(idx);
|
|
149
147
|
draw(ctx3, px1, cx1, y1, cx2, px2, y2, mid, drawCurves);
|
|
148
|
+
ctx3.fill();
|
|
150
149
|
}
|
|
151
150
|
}
|
|
152
151
|
}
|
|
@@ -154,6 +153,7 @@ export function drawRef(model, ctx1, ctx3) {
|
|
|
154
153
|
}
|
|
155
154
|
else {
|
|
156
155
|
draw(ctx1, x11, x12, y1, x22, x21, y2, mid, drawCurves);
|
|
156
|
+
ctx1.fill();
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
}
|
|
@@ -187,7 +187,7 @@ export function drawRef(model, ctx1, ctx3) {
|
|
|
187
187
|
}
|
|
188
188
|
export function drawMouseoverSynteny(model) {
|
|
189
189
|
var _a;
|
|
190
|
-
const { clickId, mouseoverId } = model;
|
|
190
|
+
const { level, clickId, mouseoverId } = model;
|
|
191
191
|
const highResolutionScaling = 1;
|
|
192
192
|
const view = getContainingView(model);
|
|
193
193
|
const drawCurves = view.drawCurves;
|
|
@@ -195,17 +195,16 @@ export function drawMouseoverSynteny(model) {
|
|
|
195
195
|
const width = view.width;
|
|
196
196
|
const ctx = (_a = model.mouseoverCanvas) === null || _a === void 0 ? void 0 : _a.getContext('2d');
|
|
197
197
|
const offsets = view.views.map(v => v.offsetPx);
|
|
198
|
-
// @ts-expect-error
|
|
199
|
-
const level = getParent(model, 4).level;
|
|
200
198
|
if (!ctx) {
|
|
201
199
|
return;
|
|
202
200
|
}
|
|
203
201
|
ctx.resetTransform();
|
|
204
202
|
ctx.scale(highResolutionScaling, highResolutionScaling);
|
|
205
203
|
ctx.clearRect(0, 0, width, height);
|
|
204
|
+
ctx.strokeStyle = 'rgba(0, 0, 0, 0.9)';
|
|
205
|
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
|
|
206
206
|
const feature1 = model.featMap[mouseoverId || ''];
|
|
207
207
|
if (feature1) {
|
|
208
|
-
ctx.fillStyle = 'rgb(0,0,0,0.1)';
|
|
209
208
|
drawMatchSimple({
|
|
210
209
|
cb: ctx => {
|
|
211
210
|
ctx.fill();
|
|
@@ -222,7 +221,6 @@ export function drawMouseoverSynteny(model) {
|
|
|
222
221
|
}
|
|
223
222
|
const feature2 = model.featMap[clickId || ''];
|
|
224
223
|
if (feature2) {
|
|
225
|
-
ctx.strokeStyle = 'rgb(0, 0, 0, 0.9)';
|
|
226
224
|
drawMatchSimple({
|
|
227
225
|
cb: ctx => {
|
|
228
226
|
ctx.stroke();
|