@jbrowse/plugin-linear-comparative-view 2.15.4 → 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.
Files changed (121) hide show
  1. package/dist/LGVSyntenyDisplay/components/LaunchSyntenyViewDialog.d.ts +2 -1
  2. package/dist/LGVSyntenyDisplay/components/LaunchSyntenyViewDialog.js +4 -2
  3. package/dist/LGVSyntenyDisplay/components/util.d.ts +4 -1
  4. package/dist/LGVSyntenyDisplay/components/util.js +7 -15
  5. package/dist/LGVSyntenyDisplay/model.d.ts +26 -14
  6. package/dist/LGVSyntenyDisplay/model.js +23 -1
  7. package/dist/LaunchLinearSyntenyView.js +41 -11
  8. package/dist/LinearComparativeDisplay/stateModelFactory.d.ts +42 -14
  9. package/dist/LinearComparativeDisplay/stateModelFactory.js +20 -10
  10. package/dist/LinearComparativeView/components/Header.d.ts +2 -3
  11. package/dist/LinearComparativeView/components/Header.js +72 -62
  12. package/dist/LinearComparativeView/components/HeaderSearchBoxes.d.ts +6 -0
  13. package/dist/LinearComparativeView/components/HeaderSearchBoxes.js +34 -0
  14. package/dist/LinearComparativeView/components/LinearComparativeRenderArea.d.ts +6 -0
  15. package/dist/LinearComparativeView/components/LinearComparativeRenderArea.js +61 -0
  16. package/dist/LinearComparativeView/components/LinearComparativeView.d.ts +2 -4
  17. package/dist/LinearComparativeView/components/LinearComparativeView.js +3 -67
  18. package/dist/LinearComparativeView/components/Rubberband.js +1 -1
  19. package/dist/LinearComparativeView/index.js +3 -0
  20. package/dist/LinearComparativeView/model.d.ts +265 -12
  21. package/dist/LinearComparativeView/model.js +45 -75
  22. package/dist/LinearSyntenyDisplay/afterAttach.js +5 -4
  23. package/dist/LinearSyntenyDisplay/components/LinearSyntenyRendering.js +47 -32
  24. package/dist/LinearSyntenyDisplay/components/SyntenyContextMenu.js +10 -6
  25. package/dist/LinearSyntenyDisplay/components/util.d.ts +7 -2
  26. package/dist/LinearSyntenyDisplay/components/util.js +12 -14
  27. package/dist/LinearSyntenyDisplay/drawSynteny.d.ts +1 -1
  28. package/dist/LinearSyntenyDisplay/drawSynteny.js +29 -25
  29. package/dist/LinearSyntenyDisplay/index.js +1 -1
  30. package/dist/LinearSyntenyDisplay/model.d.ts +48 -10
  31. package/dist/LinearSyntenyDisplay/model.js +38 -15
  32. package/dist/LinearSyntenyView/components/ImportForm/{ImportCustomTrack.d.ts → AddCustomTrack.d.ts} +2 -3
  33. package/dist/LinearSyntenyView/components/ImportForm/{ImportCustomTrack.js → AddCustomTrack.js} +3 -3
  34. package/dist/LinearSyntenyView/components/ImportForm/LinearSyntenyImportForm.js +195 -0
  35. package/dist/LinearSyntenyView/components/ImportForm/Spacer.d.ts +2 -0
  36. package/dist/LinearSyntenyView/components/ImportForm/Spacer.js +10 -0
  37. package/dist/LinearSyntenyView/components/ImportForm/TrackSelector.d.ts +10 -0
  38. package/dist/LinearSyntenyView/components/ImportForm/{ImportSyntenyTrackSelector.js → TrackSelector.js} +15 -20
  39. package/dist/LinearSyntenyView/components/ImportForm/TrackSelectorUtil.d.ts +14 -0
  40. package/dist/LinearSyntenyView/components/ImportForm/TrackSelectorUtil.js +52 -0
  41. package/dist/LinearSyntenyView/components/LinearSyntenyView.js +3 -3
  42. package/dist/LinearSyntenyView/index.js +1 -1
  43. package/dist/LinearSyntenyView/model.d.ts +267 -9
  44. package/dist/LinearSyntenyView/model.js +2 -2
  45. package/dist/LinearSyntenyView/svgcomponents/SVGLinearGenomeView.d.ts +12 -0
  46. package/dist/LinearSyntenyView/svgcomponents/SVGLinearGenomeView.js +19 -0
  47. package/dist/LinearSyntenyView/svgcomponents/SVGLinearSyntenyView.d.ts +1 -3
  48. package/dist/LinearSyntenyView/svgcomponents/SVGLinearSyntenyView.js +36 -27
  49. package/dist/LinearSyntenyViewHelper/index.d.ts +2 -0
  50. package/dist/LinearSyntenyViewHelper/index.js +25 -0
  51. package/dist/LinearSyntenyViewHelper/stateModelFactory.d.ts +30 -0
  52. package/dist/LinearSyntenyViewHelper/stateModelFactory.js +105 -0
  53. package/dist/SyntenyFeatureDetail/SyntenyFeatureDetail.d.ts +14 -0
  54. package/dist/SyntenyFeatureDetail/SyntenyFeatureDetail.js +100 -0
  55. package/dist/SyntenyFeatureDetail/index.d.ts +2 -0
  56. package/dist/SyntenyFeatureDetail/index.js +56 -0
  57. package/dist/index.js +4 -0
  58. package/esm/LGVSyntenyDisplay/components/LaunchSyntenyViewDialog.d.ts +2 -1
  59. package/esm/LGVSyntenyDisplay/components/LaunchSyntenyViewDialog.js +4 -2
  60. package/esm/LGVSyntenyDisplay/components/util.d.ts +4 -1
  61. package/esm/LGVSyntenyDisplay/components/util.js +8 -16
  62. package/esm/LGVSyntenyDisplay/model.d.ts +26 -14
  63. package/esm/LGVSyntenyDisplay/model.js +25 -3
  64. package/esm/LaunchLinearSyntenyView.js +41 -11
  65. package/esm/LinearComparativeDisplay/stateModelFactory.d.ts +42 -14
  66. package/esm/LinearComparativeDisplay/stateModelFactory.js +21 -11
  67. package/esm/LinearComparativeView/components/Header.d.ts +2 -3
  68. package/esm/LinearComparativeView/components/Header.js +73 -63
  69. package/esm/LinearComparativeView/components/HeaderSearchBoxes.d.ts +6 -0
  70. package/esm/LinearComparativeView/components/HeaderSearchBoxes.js +29 -0
  71. package/esm/LinearComparativeView/components/LinearComparativeRenderArea.d.ts +6 -0
  72. package/esm/LinearComparativeView/components/LinearComparativeRenderArea.js +56 -0
  73. package/esm/LinearComparativeView/components/LinearComparativeView.d.ts +2 -4
  74. package/esm/LinearComparativeView/components/LinearComparativeView.js +3 -67
  75. package/esm/LinearComparativeView/components/Rubberband.js +1 -1
  76. package/esm/LinearComparativeView/index.js +3 -0
  77. package/esm/LinearComparativeView/model.d.ts +265 -12
  78. package/esm/LinearComparativeView/model.js +47 -77
  79. package/esm/LinearSyntenyDisplay/afterAttach.js +5 -4
  80. package/esm/LinearSyntenyDisplay/components/LinearSyntenyRendering.js +48 -33
  81. package/esm/LinearSyntenyDisplay/components/SyntenyContextMenu.js +10 -6
  82. package/esm/LinearSyntenyDisplay/components/util.d.ts +7 -2
  83. package/esm/LinearSyntenyDisplay/components/util.js +12 -14
  84. package/esm/LinearSyntenyDisplay/drawSynteny.d.ts +1 -1
  85. package/esm/LinearSyntenyDisplay/drawSynteny.js +29 -25
  86. package/esm/LinearSyntenyDisplay/index.js +1 -1
  87. package/esm/LinearSyntenyDisplay/model.d.ts +48 -10
  88. package/esm/LinearSyntenyDisplay/model.js +38 -15
  89. package/esm/LinearSyntenyView/components/ImportForm/{ImportCustomTrack.d.ts → AddCustomTrack.d.ts} +2 -3
  90. package/esm/LinearSyntenyView/components/ImportForm/{ImportCustomTrack.js → AddCustomTrack.js} +3 -3
  91. package/esm/LinearSyntenyView/components/ImportForm/LinearSyntenyImportForm.js +167 -0
  92. package/esm/LinearSyntenyView/components/ImportForm/Spacer.d.ts +2 -0
  93. package/esm/LinearSyntenyView/components/ImportForm/Spacer.js +4 -0
  94. package/esm/LinearSyntenyView/components/ImportForm/TrackSelector.d.ts +10 -0
  95. package/esm/LinearSyntenyView/components/ImportForm/{ImportSyntenyTrackSelector.js → TrackSelector.js} +15 -20
  96. package/esm/LinearSyntenyView/components/ImportForm/TrackSelectorUtil.d.ts +14 -0
  97. package/esm/LinearSyntenyView/components/ImportForm/TrackSelectorUtil.js +23 -0
  98. package/esm/LinearSyntenyView/components/LinearSyntenyView.js +3 -3
  99. package/esm/LinearSyntenyView/index.js +1 -1
  100. package/esm/LinearSyntenyView/model.d.ts +267 -9
  101. package/esm/LinearSyntenyView/model.js +2 -2
  102. package/esm/LinearSyntenyView/svgcomponents/SVGLinearGenomeView.d.ts +12 -0
  103. package/esm/LinearSyntenyView/svgcomponents/SVGLinearGenomeView.js +13 -0
  104. package/esm/LinearSyntenyView/svgcomponents/SVGLinearSyntenyView.d.ts +1 -3
  105. package/esm/LinearSyntenyView/svgcomponents/SVGLinearSyntenyView.js +38 -29
  106. package/esm/LinearSyntenyViewHelper/index.d.ts +2 -0
  107. package/esm/LinearSyntenyViewHelper/index.js +19 -0
  108. package/esm/LinearSyntenyViewHelper/stateModelFactory.d.ts +30 -0
  109. package/esm/LinearSyntenyViewHelper/stateModelFactory.js +102 -0
  110. package/esm/SyntenyFeatureDetail/SyntenyFeatureDetail.d.ts +14 -0
  111. package/esm/SyntenyFeatureDetail/SyntenyFeatureDetail.js +72 -0
  112. package/esm/SyntenyFeatureDetail/index.d.ts +2 -0
  113. package/esm/SyntenyFeatureDetail/index.js +27 -0
  114. package/esm/index.js +4 -0
  115. package/package.json +3 -3
  116. package/dist/LinearSyntenyView/components/ImportForm/ImportSyntenyTrackSelector.d.ts +0 -9
  117. package/dist/LinearSyntenyView/components/ImportForm/index.js +0 -138
  118. package/esm/LinearSyntenyView/components/ImportForm/ImportSyntenyTrackSelector.d.ts +0 -9
  119. package/esm/LinearSyntenyView/components/ImportForm/index.js +0 -110
  120. /package/dist/LinearSyntenyView/components/ImportForm/{index.d.ts → LinearSyntenyImportForm.d.ts} +0 -0
  121. /package/esm/LinearSyntenyView/components/ImportForm/{index.d.ts → LinearSyntenyImportForm.d.ts} +0 -0
@@ -1,4 +1,4 @@
1
- import React, { useState, useCallback, useRef, lazy } from 'react';
1
+ import React, { useState, useCallback, useRef, lazy, useEffect } from 'react';
2
2
  import { observer } from 'mobx-react';
3
3
  import { getContainingView } from '@jbrowse/core/util';
4
4
  import { transaction } from 'mobx';
@@ -17,21 +17,23 @@ const useStyles = makeStyles()({
17
17
  rel: {
18
18
  position: 'relative',
19
19
  },
20
- abs: {
20
+ mouseoverCanvas: {
21
+ imageRendering: 'pixelated',
21
22
  position: 'absolute',
22
- },
23
- none: {
24
23
  pointEvents: 'none',
25
24
  },
25
+ mainCanvas: {
26
+ position: 'absolute',
27
+ },
26
28
  });
27
29
  const LinearSyntenyRendering = observer(function ({ model, }) {
28
- const { classes, cx } = useStyles();
30
+ const { classes } = useStyles();
31
+ const { mouseoverId, height } = model;
29
32
  const xOffset = useRef(0);
30
- const currScrollFrame = useRef();
31
33
  const view = getContainingView(model);
32
- const height = view.middleComparativeHeight;
33
34
  const width = view.width;
34
35
  const delta = useRef(0);
36
+ const scheduled = useRef(false);
35
37
  const timeout = useRef();
36
38
  const [anchorEl, setAnchorEl] = useState();
37
39
  const [tooltip, setTooltip] = useState('');
@@ -39,18 +41,25 @@ 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 { mouseoverId } = model;
44
+ const mainSyntenyCanvasRefp = useRef();
43
45
  // these useCallbacks avoid new refs from being created on any mouseover,
44
46
  // etc.
45
47
  // biome-ignore lint/correctness/useExhaustiveDependencies:
46
- const k1 = useCallback((ref) => {
48
+ const mouseoverDetectionCanvasRef = useCallback((ref) => {
47
49
  model.setMouseoverCanvasRef(ref);
48
50
  },
49
51
  // eslint-disable-next-line react-hooks/exhaustive-deps
50
52
  [model, height, width]);
51
53
  // biome-ignore lint/correctness/useExhaustiveDependencies:
52
- const k2 = useCallback((ref) => {
54
+ const mainSyntenyCanvasRef = useCallback((ref) => {
53
55
  model.setMainCanvasRef(ref);
56
+ mainSyntenyCanvasRefp.current = ref; // this ref is additionally used in useEffect below
57
+ },
58
+ // eslint-disable-next-line react-hooks/exhaustive-deps
59
+ [model, height, width]);
60
+ // biome-ignore lint/correctness/useExhaustiveDependencies:
61
+ useEffect(() => {
62
+ var _a;
54
63
  function onWheel(event) {
55
64
  event.preventDefault();
56
65
  if (event.ctrlKey) {
@@ -62,11 +71,14 @@ const LinearSyntenyRendering = observer(function ({ model, }) {
62
71
  clearTimeout(timeout.current);
63
72
  }
64
73
  timeout.current = setTimeout(() => {
74
+ var _a;
65
75
  for (const v of view.views) {
66
76
  v.setScaleFactor(1);
67
77
  v.zoomTo(delta.current > 0
68
78
  ? v.bpPerPx * (1 + delta.current)
69
- : v.bpPerPx / (1 - delta.current), event.clientX - ((ref === null || ref === void 0 ? void 0 : ref.getBoundingClientRect().left) || 0));
79
+ : v.bpPerPx / (1 - delta.current), event.clientX -
80
+ (((_a = mainSyntenyCanvasRefp.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().left) ||
81
+ 0));
70
82
  }
71
83
  delta.current = 0;
72
84
  }, 300);
@@ -75,56 +87,55 @@ const LinearSyntenyRendering = observer(function ({ model, }) {
75
87
  if (Math.abs(event.deltaY) < Math.abs(event.deltaX)) {
76
88
  xOffset.current += event.deltaX / 2;
77
89
  }
78
- if (currScrollFrame.current === undefined) {
79
- currScrollFrame.current = requestAnimationFrame(() => {
90
+ if (!scheduled.current) {
91
+ scheduled.current = true;
92
+ window.requestAnimationFrame(() => {
80
93
  transaction(() => {
81
94
  for (const v of view.views) {
82
95
  v.horizontalScroll(xOffset.current);
83
96
  }
84
97
  xOffset.current = 0;
85
- currScrollFrame.current = undefined;
98
+ scheduled.current = false;
86
99
  });
87
100
  });
88
101
  }
89
102
  }
90
103
  }
91
- ref === null || ref === void 0 ? void 0 : ref.addEventListener('wheel', onWheel);
92
- // this is a react 19-ism to have a cleanup in the ref callback
93
- // https://react.dev/blog/2024/04/25/react-19#cleanup-functions-for-refs
94
- // note: it warns in earlier versions of react
104
+ (_a = mainSyntenyCanvasRefp.current) === null || _a === void 0 ? void 0 : _a.addEventListener('wheel', onWheel);
95
105
  return () => {
96
- ref === null || ref === void 0 ? void 0 : ref.removeEventListener('wheel', onWheel);
106
+ var _a;
107
+ (_a = mainSyntenyCanvasRefp.current) === null || _a === void 0 ? void 0 : _a.removeEventListener('wheel', onWheel);
97
108
  };
98
- },
99
- // eslint-disable-next-line react-hooks/exhaustive-deps
100
- [model, height, width]);
109
+ // eslint-disable-next-line react-hooks/exhaustive-deps
110
+ }, [model, height, width]);
101
111
  // biome-ignore lint/correctness/useExhaustiveDependencies:
102
- const k3 = useCallback((ref) => {
112
+ const clickMapCanvasRef = useCallback((ref) => {
103
113
  model.setClickMapCanvasRef(ref);
104
114
  },
105
115
  // eslint-disable-next-line react-hooks/exhaustive-deps
106
116
  [model, height, width]);
107
117
  // biome-ignore lint/correctness/useExhaustiveDependencies:
108
- const k4 = useCallback((ref) => {
118
+ const cigarClickMapCanvasRef = useCallback((ref) => {
109
119
  model.setCigarClickMapCanvasRef(ref);
110
120
  },
111
121
  // eslint-disable-next-line react-hooks/exhaustive-deps
112
122
  [model, height, width]);
113
123
  return (React.createElement("div", { className: classes.rel },
114
- React.createElement("canvas", { ref: k1, width: width, height: height, className: cx(classes.abs, classes.none) }),
115
- React.createElement("canvas", { ref: k2, onMouseMove: event => {
124
+ React.createElement("canvas", { ref: mouseoverDetectionCanvasRef, width: width, height: height, className: classes.mouseoverCanvas }),
125
+ React.createElement("canvas", { ref: mainSyntenyCanvasRef, onMouseMove: event => {
116
126
  var _a;
117
127
  if (mouseCurrDownX !== undefined) {
118
128
  xOffset.current += mouseCurrDownX - event.clientX;
119
129
  setMouseCurrDownX(event.clientX);
120
- if (currScrollFrame.current === undefined) {
121
- currScrollFrame.current = requestAnimationFrame(() => {
130
+ if (!scheduled.current) {
131
+ scheduled.current = true;
132
+ window.requestAnimationFrame(() => {
122
133
  transaction(() => {
123
134
  for (const v of view.views) {
124
135
  v.horizontalScroll(xOffset.current);
125
136
  }
126
137
  xOffset.current = 0;
127
- currScrollFrame.current = undefined;
138
+ scheduled.current = false;
128
139
  });
129
140
  });
130
141
  }
@@ -158,7 +169,11 @@ const LinearSyntenyRendering = observer(function ({ model, }) {
158
169
  const { f, cigar } = model.featPositions[id];
159
170
  const unitMultiplier2 = Math.floor(MAX_COLOR_RANGE / cigar.length);
160
171
  const cigarIdx = getId(r2, g2, b2, unitMultiplier2);
161
- setTooltip(getTooltip(f, cigar[cigarIdx], cigar[cigarIdx + 1]));
172
+ setTooltip(getTooltip({
173
+ feature: f,
174
+ cigarOp: cigar[cigarIdx],
175
+ cigarOpLen: cigar[cigarIdx + 1],
176
+ }));
162
177
  }
163
178
  }
164
179
  }, onMouseLeave: () => {
@@ -176,9 +191,9 @@ const LinearSyntenyRendering = observer(function ({ model, }) {
176
191
  }
177
192
  }, onContextMenu: evt => {
178
193
  onSynContextClick(evt, model, setAnchorEl);
179
- }, "data-testid": "synteny_canvas", className: classes.abs, width: width, height: height }),
180
- React.createElement("canvas", { ref: k3, className: classes.pix, width: width, height: height }),
181
- React.createElement("canvas", { ref: k4, className: classes.pix, width: width, height: height }),
194
+ }, "data-testid": "synteny_canvas", className: classes.mainCanvas, width: width, height: height }),
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 }),
182
197
  mouseoverId && tooltip && currX && currY ? (React.createElement(SyntenyTooltip, { title: tooltip })) : null,
183
198
  anchorEl ? (React.createElement(SyntenyContextMenu, { model: model, anchorEl: anchorEl, onClose: () => {
184
199
  setAnchorEl(undefined);
@@ -33,13 +33,17 @@ export default function SyntenyContextMenu({ model, onClose, anchorEl, }) {
33
33
  const end = f.get('end');
34
34
  const refName = f.get('refName');
35
35
  const mate = f.get('mate');
36
- view.views[0].navToLocString(`${refName}:${start}-${end}`).catch((e) => {
37
- console.error(e);
38
- getSession(model).notifyError(`${e}`, e);
36
+ const l1 = view.views[model.level];
37
+ const l2 = view.views[model.level + 1];
38
+ l1.navToLocString(`${refName}:${start}-${end}`).catch((e) => {
39
+ const err = `${l1.assemblyNames[0]}:${e}`;
40
+ console.error(err);
41
+ getSession(model).notifyError(err, e);
39
42
  });
40
- view.views[1].navToLocString(`${mate.refName}:${mate.start}-${mate.end}`).catch((e) => {
41
- console.error(e);
42
- getSession(model).notifyError(`${e}`, e);
43
+ l2.navToLocString(`${mate.refName}:${mate.start}-${mate.end}`).catch((e) => {
44
+ const err = `${l2.assemblyNames[0]}:${e}`;
45
+ console.error(err);
46
+ getSession(model).notifyError(err, e);
43
47
  });
44
48
  },
45
49
  },
@@ -19,10 +19,11 @@ interface FeatPos {
19
19
  f: Feature;
20
20
  cigar: string[];
21
21
  }
22
- export declare function drawMatchSimple({ feature, ctx, offsets, cb, height, drawCurves, oobLimit, viewWidth, hideTiny, }: {
22
+ export declare function drawMatchSimple({ feature, ctx, offsets, level, cb, height, drawCurves, oobLimit, viewWidth, hideTiny, }: {
23
23
  feature: FeatPos;
24
24
  ctx: CanvasRenderingContext2D;
25
25
  offsets: number[];
26
+ level: number;
26
27
  oobLimit: number;
27
28
  viewWidth: number;
28
29
  cb: (ctx: CanvasRenderingContext2D) => void;
@@ -35,5 +36,9 @@ export declare function drawBox(ctx: CanvasRenderingContext2D, x1: number, x2: n
35
36
  export declare function drawBezierBox(ctx: CanvasRenderingContext2D, x1: number, x2: number, y1: number, x3: number, x4: number, y2: number, mid: number): void;
36
37
  export declare function onSynClick(event: React.MouseEvent, model: LinearSyntenyDisplayModel): import("../model").FeatPos | undefined;
37
38
  export declare function onSynContextClick(event: React.MouseEvent, model: LinearSyntenyDisplayModel, setAnchorEl: (arg: ClickCoord) => void): void;
38
- export declare function getTooltip(f: Feature, cigarOp?: string, cigarOpLen?: string): string;
39
+ export declare function getTooltip({ feature, cigarOp, cigarOpLen, }: {
40
+ feature: Feature;
41
+ cigarOp?: string;
42
+ cigarOpLen?: string;
43
+ }): string;
39
44
  export {};
@@ -1,12 +1,12 @@
1
1
  import { assembleLocString, doesIntersect2, getSession, isSessionModelWithWidgets, getContainingTrack, getContainingView, } from '@jbrowse/core/util';
2
2
  // locals
3
3
  import { getId, MAX_COLOR_RANGE } from '../drawSynteny';
4
- export function drawMatchSimple({ feature, ctx, offsets, cb, height, drawCurves, oobLimit, viewWidth, hideTiny, }) {
4
+ export function drawMatchSimple({ feature, ctx, offsets, level, cb, height, drawCurves, oobLimit, viewWidth, hideTiny, }) {
5
5
  const { p11, p12, p21, p22 } = feature;
6
- const x11 = p11.offsetPx - offsets[0];
7
- const x12 = p12.offsetPx - offsets[0];
8
- const x21 = p21.offsetPx - offsets[1];
9
- const x22 = p22.offsetPx - offsets[1];
6
+ const x11 = p11.offsetPx - offsets[level];
7
+ const x12 = p12.offsetPx - offsets[level];
8
+ const x21 = p21.offsetPx - offsets[level + 1];
9
+ const x22 = p22.offsetPx - offsets[level + 1];
10
10
  const l1 = Math.abs(x12 - x11);
11
11
  const l2 = Math.abs(x22 - x21);
12
12
  const y1 = 0;
@@ -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.clickMapCanvas;
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 / model.numFeats);
93
+ const unitMultiplier = Math.floor(MAX_COLOR_RANGE / numFeats);
97
94
  const id = getId(r1, g1, b1, unitMultiplier);
98
- const feat = model.featPositions[id];
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('BaseFeatureWidget', 'baseFeature', {
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(f, cigarOp, cigarOpLen) {
136
+ export function getTooltip({ feature, cigarOp, cigarOpLen, }) {
139
137
  // @ts-expect-error
140
- const f1 = f.toJSON();
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,5 +1,5 @@
1
1
  import { LinearSyntenyDisplayModel } from './model';
2
2
  export declare const MAX_COLOR_RANGE: number;
3
3
  export declare function getId(r: number, g: number, b: number, unitMultiplier: number): number;
4
- export declare function drawRef(model: LinearSyntenyDisplayModel, ctx1: CanvasRenderingContext2D, ctx3?: CanvasRenderingContext2D): undefined;
4
+ export declare function drawRef(model: LinearSyntenyDisplayModel, ctx1: CanvasRenderingContext2D, ctx3?: CanvasRenderingContext2D): void;
5
5
  export declare function drawMouseoverSynteny(model: LinearSyntenyDisplayModel): void;
@@ -25,25 +25,24 @@ export function drawRef(model, ctx1, ctx3) {
25
25
  const view = getContainingView(model);
26
26
  const drawCurves = view.drawCurves;
27
27
  const drawCIGAR = view.drawCIGAR;
28
- const height = view.middleComparativeHeight;
28
+ const { level, height, featPositions } = model;
29
29
  const width = view.width;
30
30
  const bpPerPxs = view.views.map(v => v.bpPerPx);
31
31
  if (ctx3) {
32
32
  ctx3.imageSmoothingEnabled = false;
33
33
  }
34
34
  ctx1.beginPath();
35
- const featPos = model.featPositions;
36
35
  const offsets = view.views.map(v => v.offsetPx);
37
- const unitMultiplier = Math.floor(MAX_COLOR_RANGE / featPos.length);
36
+ const unitMultiplier = Math.floor(MAX_COLOR_RANGE / featPositions.length);
38
37
  // this loop is optimized to draw many thin lines with a single ctx.stroke
39
38
  // call, a separate loop below draws larger boxes
40
39
  ctx1.fillStyle = colorMap.M;
41
40
  ctx1.strokeStyle = colorMap.M;
42
- for (const { p11, p12, p21, p22 } of featPos) {
43
- const x11 = p11.offsetPx - offsets[0];
44
- const x12 = p12.offsetPx - offsets[0];
45
- const x21 = p21.offsetPx - offsets[1];
46
- const x22 = p22.offsetPx - offsets[1];
41
+ for (const { p11, p12, p21, p22 } of featPositions) {
42
+ const x11 = p11.offsetPx - offsets[level];
43
+ const x12 = p12.offsetPx - offsets[level];
44
+ const x21 = p21.offsetPx - offsets[level + 1];
45
+ const x22 = p22.offsetPx - offsets[level + 1];
47
46
  const l1 = Math.abs(x12 - x11);
48
47
  const l2 = Math.abs(x22 - x21);
49
48
  const y1 = 0;
@@ -69,11 +68,11 @@ export function drawRef(model, ctx1, ctx3) {
69
68
  // ctx.stroke once is much more efficient than calling stroke() many times
70
69
  ctx1.fillStyle = colorMap.M;
71
70
  ctx1.strokeStyle = colorMap.M;
72
- for (const { p11, p12, p21, p22, f, cigar } of featPos) {
73
- const x11 = p11.offsetPx - offsets[0];
74
- const x12 = p12.offsetPx - offsets[0];
75
- const x21 = p21.offsetPx - offsets[1];
76
- const x22 = p22.offsetPx - offsets[1];
71
+ for (const { p11, p12, p21, p22, f, cigar } of featPositions) {
72
+ const x11 = p11.offsetPx - offsets[level];
73
+ const x12 = p12.offsetPx - offsets[level];
74
+ const x21 = p21.offsetPx - offsets[level + 1];
75
+ const x22 = p22.offsetPx - offsets[level + 1];
77
76
  const l1 = Math.abs(x12 - x11);
78
77
  const l2 = Math.abs(x22 - x21);
79
78
  const minX = Math.min(x21, x22);
@@ -108,8 +107,8 @@ export function drawRef(model, ctx1, ctx3) {
108
107
  px1 = cx1;
109
108
  px2 = cx2;
110
109
  }
111
- const d1 = len / bpPerPxs[0];
112
- const d2 = len / bpPerPxs[1];
110
+ const d1 = len / bpPerPxs[level];
111
+ const d2 = len / bpPerPxs[level + 1];
113
112
  if (op === 'M' || op === '=' || op === 'X') {
114
113
  cx1 += d1 * rev1;
115
114
  cx2 += d2 * rev2;
@@ -135,16 +134,18 @@ export function drawRef(model, ctx1, ctx3) {
135
134
  continuingFlag = true;
136
135
  }
137
136
  else {
138
- // allow rendering the dominant color when using continuing flag
139
- // if the last element of continuing was a large feature, else
140
- // just use match
137
+ // allow rendering the dominant color when using continuing
138
+ // flag if the last element of continuing was a large
139
+ // feature, else just use match
141
140
  ctx1.fillStyle =
142
141
  colorMap[(continuingFlag && d1 > 1) || d2 > 1 ? op : 'M'];
143
142
  continuingFlag = false;
144
143
  draw(ctx1, px1, cx1, y1, cx2, px2, y2, mid, drawCurves);
144
+ ctx1.fill();
145
145
  if (ctx3) {
146
146
  ctx3.fillStyle = makeColor(idx);
147
147
  draw(ctx3, px1, cx1, y1, cx2, px2, y2, mid, drawCurves);
148
+ ctx3.fill();
148
149
  }
149
150
  }
150
151
  }
@@ -152,6 +153,7 @@ export function drawRef(model, ctx1, ctx3) {
152
153
  }
153
154
  else {
154
155
  draw(ctx1, x11, x12, y1, x22, x21, y2, mid, drawCurves);
156
+ ctx1.fill();
155
157
  }
156
158
  }
157
159
  }
@@ -162,8 +164,8 @@ export function drawRef(model, ctx1, ctx3) {
162
164
  }
163
165
  ctx2.imageSmoothingEnabled = false;
164
166
  ctx2.clearRect(0, 0, width, height);
165
- for (let i = 0; i < featPos.length; i++) {
166
- const feature = featPos[i];
167
+ for (let i = 0; i < featPositions.length; i++) {
168
+ const feature = featPositions[i];
167
169
  const idx = i * unitMultiplier + 1;
168
170
  ctx2.fillStyle = makeColor(idx);
169
171
  // too many click map false positives with colored stroked lines
@@ -174,6 +176,7 @@ export function drawRef(model, ctx1, ctx3) {
174
176
  feature,
175
177
  ctx: ctx2,
176
178
  drawCurves,
179
+ level,
177
180
  offsets,
178
181
  oobLimit,
179
182
  viewWidth: view.width,
@@ -181,15 +184,14 @@ export function drawRef(model, ctx1, ctx3) {
181
184
  height,
182
185
  });
183
186
  }
184
- return undefined;
185
187
  }
186
188
  export function drawMouseoverSynteny(model) {
187
189
  var _a;
188
- const { clickId, mouseoverId } = model;
190
+ const { level, clickId, mouseoverId } = model;
189
191
  const highResolutionScaling = 1;
190
192
  const view = getContainingView(model);
191
193
  const drawCurves = view.drawCurves;
192
- const height = view.middleComparativeHeight;
194
+ const height = model.height;
193
195
  const width = view.width;
194
196
  const ctx = (_a = model.mouseoverCanvas) === null || _a === void 0 ? void 0 : _a.getContext('2d');
195
197
  const offsets = view.views.map(v => v.offsetPx);
@@ -199,14 +201,16 @@ export function drawMouseoverSynteny(model) {
199
201
  ctx.resetTransform();
200
202
  ctx.scale(highResolutionScaling, highResolutionScaling);
201
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)';
202
206
  const feature1 = model.featMap[mouseoverId || ''];
203
207
  if (feature1) {
204
- ctx.fillStyle = 'rgb(0,0,0,0.1)';
205
208
  drawMatchSimple({
206
209
  cb: ctx => {
207
210
  ctx.fill();
208
211
  },
209
212
  feature: feature1,
213
+ level,
210
214
  ctx,
211
215
  oobLimit,
212
216
  viewWidth: view.width,
@@ -217,13 +221,13 @@ export function drawMouseoverSynteny(model) {
217
221
  }
218
222
  const feature2 = model.featMap[clickId || ''];
219
223
  if (feature2) {
220
- ctx.strokeStyle = 'rgb(0, 0, 0, 0.9)';
221
224
  drawMatchSimple({
222
225
  cb: ctx => {
223
226
  ctx.stroke();
224
227
  },
225
228
  feature: feature2,
226
229
  ctx,
230
+ level,
227
231
  oobLimit,
228
232
  viewWidth: view.width,
229
233
  drawCurves,
@@ -11,7 +11,7 @@ export default function LinearSyntenyDisplayF(pluginManager) {
11
11
  configSchema,
12
12
  stateModel: stateModelFactory(configSchema),
13
13
  trackType: 'SyntenyTrack',
14
- viewType: 'LinearSyntenyView',
14
+ viewType: 'LinearSyntenyViewHelper',
15
15
  ReactComponent: lazy(() => import('./components/Component')),
16
16
  });
17
17
  });
@@ -24,7 +24,6 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
24
24
  } & {
25
25
  type: import("mobx-state-tree").ISimpleType<"LinearComparativeDisplay">;
26
26
  configuration: AnyConfigurationSchemaType;
27
- height: import("mobx-state-tree").IType<number | undefined, number, number>;
28
27
  } & {
29
28
  /**
30
29
  * #property
@@ -77,9 +76,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
77
76
  rendererTypeName: string;
78
77
  error: unknown;
79
78
  message: string | undefined;
80
- }, import("mobx-state-tree" /**
81
- * #action
82
- */)._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
79
+ }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
83
80
  }> | null;
84
81
  readonly adapterConfig: any;
85
82
  readonly parentTrack: any;
@@ -99,6 +96,8 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
99
96
  features: Feature[] | undefined;
100
97
  message: string | undefined;
101
98
  } & {
99
+ readonly level: number;
100
+ readonly height: number;
102
101
  renderProps(): {
103
102
  rpcDriverName: string | undefined;
104
103
  displayModel: {
@@ -114,9 +113,13 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
114
113
  setSubschema(slotName: string, data: Record<string, unknown>): Record<string, unknown> | ({
115
114
  [x: string]: any;
116
115
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & any & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>);
117
- } & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>);
116
+ } & import("mobx-state-tree" /**
117
+ * #volatile
118
+ * canvas for drawing mouseover shading this is separate from the other
119
+ * code for speed: don't have to redraw entire canvas to do a feature's
120
+ * mouseover shading
121
+ */).IStateTreeNode<AnyConfigurationSchemaType>);
118
122
  } & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>;
119
- height: number;
120
123
  } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
121
124
  rendererTypeName: string;
122
125
  error: unknown;
@@ -160,9 +163,7 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
160
163
  rendererTypeName: string;
161
164
  error: unknown;
162
165
  message: string | undefined;
163
- }, import("mobx-state-tree" /**
164
- * #action
165
- */)._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
166
+ }, import("mobx-state-tree")._NotCustomized, import("mobx-state-tree")._NotCustomized>>;
166
167
  }> | null;
167
168
  readonly adapterConfig: any;
168
169
  readonly parentTrack: any;
@@ -188,7 +189,6 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
188
189
  } & {
189
190
  type: import("mobx-state-tree").ISimpleType<"LinearComparativeDisplay">;
190
191
  configuration: AnyConfigurationSchemaType;
191
- height: import("mobx-state-tree").IType<number | undefined, number, number>;
192
192
  }, {
193
193
  rendererTypeName: string;
194
194
  error: unknown;
@@ -264,13 +264,51 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
264
264
  } & {
265
265
  afterAttach(): void;
266
266
  } & {
267
+ /**
268
+ * #volatile
269
+ * canvas used for drawing visible screen
270
+ */
267
271
  mainCanvas: HTMLCanvasElement | null;
272
+ /**
273
+ * #volatile
274
+ * canvas used for drawing click map with feature ids this renders a
275
+ * unique color per alignment, so that it can be re-traced after a
276
+ * feature click with getImageData at that pixel
277
+ */
268
278
  clickMapCanvas: HTMLCanvasElement | null;
279
+ /**
280
+ * #volatile
281
+ * canvas used for drawing click map with cigar data this can show if you
282
+ * are mousing over a insertion/deletion. it is similar in purpose to the
283
+ * clickMapRef but was not feasible to pack this into the clickMapRef
284
+ */
269
285
  cigarClickMapCanvas: HTMLCanvasElement | null;
286
+ /**
287
+ * #volatile
288
+ * canvas for drawing mouseover shading this is separate from the other
289
+ * code for speed: don't have to redraw entire canvas to do a feature's
290
+ * mouseover shading
291
+ */
270
292
  mouseoverCanvas: HTMLCanvasElement | null;
293
+ /**
294
+ * #volatile
295
+ * assigned by reaction
296
+ */
271
297
  featPositions: FeatPos[];
298
+ /**
299
+ * #volatile
300
+ * currently mouse'd over feature
301
+ */
272
302
  mouseoverId: string | undefined;
303
+ /**
304
+ * #volatile
305
+ * currently click'd over feature
306
+ */
273
307
  clickId: string | undefined;
308
+ /**
309
+ * #volatile
310
+ * currently mouseover'd CIGAR subfeature
311
+ */
274
312
  cigarMouseoverId: number;
275
313
  } & {
276
314
  /**
@@ -20,28 +20,51 @@ function stateModelFactory(configSchema) {
20
20
  configuration: ConfigurationReference(configSchema),
21
21
  }))
22
22
  .volatile(() => ({
23
- // canvas used for drawing visible screen
23
+ /**
24
+ * #volatile
25
+ * canvas used for drawing visible screen
26
+ */
24
27
  mainCanvas: null,
25
- // canvas used for drawing click map with feature ids
26
- // this renders a unique color per alignment, so that it can be re-traced
27
- // after a feature click with getImageData at that pixel
28
+ /**
29
+ * #volatile
30
+ * canvas used for drawing click map with feature ids this renders a
31
+ * unique color per alignment, so that it can be re-traced after a
32
+ * feature click with getImageData at that pixel
33
+ */
28
34
  clickMapCanvas: null,
29
- // canvas used for drawing click map with cigar data
30
- // this can show if you are mousing over a insertion/deletion. it is similar
31
- // in purpose to the clickMapRef but was not feasible to pack this into the
32
- // clickMapRef
35
+ /**
36
+ * #volatile
37
+ * canvas used for drawing click map with cigar data this can show if you
38
+ * are mousing over a insertion/deletion. it is similar in purpose to the
39
+ * clickMapRef but was not feasible to pack this into the clickMapRef
40
+ */
33
41
  cigarClickMapCanvas: null,
34
- // canvas for drawing mouseover shading
35
- // this is separate from the other code for speed: don't have to redraw
36
- // entire canvas to do a feature's mouseover shading
42
+ /**
43
+ * #volatile
44
+ * canvas for drawing mouseover shading this is separate from the other
45
+ * code for speed: don't have to redraw entire canvas to do a feature's
46
+ * mouseover shading
47
+ */
37
48
  mouseoverCanvas: null,
38
- // assigned by reaction
49
+ /**
50
+ * #volatile
51
+ * assigned by reaction
52
+ */
39
53
  featPositions: [],
40
- // currently mouse'd over feature
54
+ /**
55
+ * #volatile
56
+ * currently mouse'd over feature
57
+ */
41
58
  mouseoverId: undefined,
42
- // currently click'd over feature
59
+ /**
60
+ * #volatile
61
+ * currently click'd over feature
62
+ */
43
63
  clickId: undefined,
44
- // currently mouseover'd CIGAR subfeature
64
+ /**
65
+ * #volatile
66
+ * currently mouseover'd CIGAR subfeature
67
+ */
45
68
  cigarMouseoverId: -1,
46
69
  }))
47
70
  .actions(self => ({