@jbrowse/plugin-dotplot-view 3.6.4 → 3.7.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 (97) hide show
  1. package/dist/DiagonalizeDotplotRpc.d.ts +30 -0
  2. package/dist/DiagonalizeDotplotRpc.js +156 -0
  3. package/dist/DotplotDisplay/renderDotplotBlock.js +3 -0
  4. package/dist/DotplotDisplay/stateModelFactory.d.ts +6 -0
  5. package/dist/DotplotDisplay/stateModelFactory.js +15 -0
  6. package/dist/DotplotRenderer/DotplotRenderer.d.ts +3 -12
  7. package/dist/DotplotRenderer/clamp.d.ts +7 -0
  8. package/dist/DotplotRenderer/clamp.js +62 -0
  9. package/dist/DotplotRenderer/drawDotplot.d.ts +5 -4
  10. package/dist/DotplotRenderer/drawDotplot.js +92 -96
  11. package/dist/DotplotView/1dview.js +5 -3
  12. package/dist/DotplotView/components/ColorBySelector.d.ts +5 -0
  13. package/dist/DotplotView/components/ColorBySelector.js +79 -0
  14. package/dist/DotplotView/components/DiagonalizationProgressDialog.d.ts +6 -0
  15. package/dist/DotplotView/components/DiagonalizationProgressDialog.js +125 -0
  16. package/dist/DotplotView/components/DotplotControls.js +84 -12
  17. package/dist/DotplotView/components/DotplotTooltips.d.ts +15 -0
  18. package/dist/DotplotView/components/DotplotTooltips.js +43 -0
  19. package/dist/DotplotView/components/DotplotView.js +16 -191
  20. package/dist/DotplotView/components/DotplotWarnings.js +3 -3
  21. package/dist/DotplotView/components/ImportForm/index.js +0 -1
  22. package/dist/DotplotView/components/MinLengthSlider.d.ts +5 -0
  23. package/dist/DotplotView/components/MinLengthSlider.js +44 -0
  24. package/dist/DotplotView/components/MouseInteractionLayer.d.ts +17 -0
  25. package/dist/DotplotView/components/MouseInteractionLayer.js +18 -0
  26. package/dist/DotplotView/components/OpacitySlider.d.ts +5 -0
  27. package/dist/DotplotView/components/OpacitySlider.js +43 -0
  28. package/dist/DotplotView/components/SelectionContextMenu.d.ts +13 -0
  29. package/dist/DotplotView/components/SelectionContextMenu.js +42 -0
  30. package/dist/DotplotView/components/SliderTooltip.d.ts +2 -0
  31. package/dist/DotplotView/components/SliderTooltip.js +9 -0
  32. package/dist/DotplotView/components/hooks/useCtrlKeyTracking.d.ts +1 -0
  33. package/dist/DotplotView/components/hooks/useCtrlKeyTracking.js +24 -0
  34. package/dist/DotplotView/components/hooks/useCursorMode.d.ts +7 -0
  35. package/dist/DotplotView/components/hooks/useCursorMode.js +19 -0
  36. package/dist/DotplotView/components/hooks/useMouseCoordinates.d.ts +29 -0
  37. package/dist/DotplotView/components/hooks/useMouseCoordinates.js +52 -0
  38. package/dist/DotplotView/components/hooks/useMouseMoveHandler.d.ts +6 -0
  39. package/dist/DotplotView/components/hooks/useMouseMoveHandler.js +27 -0
  40. package/dist/DotplotView/components/hooks/useMouseUpHandler.d.ts +3 -0
  41. package/dist/DotplotView/components/hooks/useMouseUpHandler.js +31 -0
  42. package/dist/DotplotView/components/hooks/useWheelHandler.d.ts +8 -0
  43. package/dist/DotplotView/components/hooks/useWheelHandler.js +47 -0
  44. package/dist/DotplotView/components/util.js +1 -3
  45. package/dist/DotplotView/model.d.ts +5 -0
  46. package/dist/DotplotView/model.js +35 -20
  47. package/dist/ServerSideRenderedBlockContent.js +3 -20
  48. package/dist/index.js +2 -0
  49. package/esm/DiagonalizeDotplotRpc.d.ts +30 -0
  50. package/esm/DiagonalizeDotplotRpc.js +150 -0
  51. package/esm/DotplotDisplay/renderDotplotBlock.js +3 -0
  52. package/esm/DotplotDisplay/stateModelFactory.d.ts +6 -0
  53. package/esm/DotplotDisplay/stateModelFactory.js +15 -0
  54. package/esm/DotplotRenderer/DotplotRenderer.d.ts +3 -12
  55. package/esm/DotplotRenderer/clamp.d.ts +7 -0
  56. package/esm/DotplotRenderer/clamp.js +58 -0
  57. package/esm/DotplotRenderer/drawDotplot.d.ts +5 -4
  58. package/esm/DotplotRenderer/drawDotplot.js +92 -96
  59. package/esm/DotplotView/1dview.js +5 -3
  60. package/esm/DotplotView/components/ColorBySelector.d.ts +5 -0
  61. package/esm/DotplotView/components/ColorBySelector.js +74 -0
  62. package/esm/DotplotView/components/DiagonalizationProgressDialog.d.ts +6 -0
  63. package/esm/DotplotView/components/DiagonalizationProgressDialog.js +123 -0
  64. package/esm/DotplotView/components/DotplotControls.js +52 -13
  65. package/esm/DotplotView/components/DotplotTooltips.d.ts +15 -0
  66. package/esm/DotplotView/components/DotplotTooltips.js +7 -0
  67. package/esm/DotplotView/components/DotplotView.js +17 -159
  68. package/esm/DotplotView/components/DotplotWarnings.js +4 -4
  69. package/esm/DotplotView/components/ImportForm/index.js +0 -1
  70. package/esm/DotplotView/components/MinLengthSlider.d.ts +5 -0
  71. package/esm/DotplotView/components/MinLengthSlider.js +39 -0
  72. package/esm/DotplotView/components/MouseInteractionLayer.d.ts +17 -0
  73. package/esm/DotplotView/components/MouseInteractionLayer.js +12 -0
  74. package/esm/DotplotView/components/OpacitySlider.d.ts +5 -0
  75. package/esm/DotplotView/components/OpacitySlider.js +38 -0
  76. package/esm/DotplotView/components/SelectionContextMenu.d.ts +13 -0
  77. package/esm/DotplotView/components/SelectionContextMenu.js +39 -0
  78. package/esm/DotplotView/components/SliderTooltip.d.ts +2 -0
  79. package/esm/DotplotView/components/SliderTooltip.js +6 -0
  80. package/esm/DotplotView/components/hooks/useCtrlKeyTracking.d.ts +1 -0
  81. package/esm/DotplotView/components/hooks/useCtrlKeyTracking.js +21 -0
  82. package/esm/DotplotView/components/hooks/useCursorMode.d.ts +7 -0
  83. package/esm/DotplotView/components/hooks/useCursorMode.js +16 -0
  84. package/esm/DotplotView/components/hooks/useMouseCoordinates.d.ts +29 -0
  85. package/esm/DotplotView/components/hooks/useMouseCoordinates.js +49 -0
  86. package/esm/DotplotView/components/hooks/useMouseMoveHandler.d.ts +6 -0
  87. package/esm/DotplotView/components/hooks/useMouseMoveHandler.js +24 -0
  88. package/esm/DotplotView/components/hooks/useMouseUpHandler.d.ts +3 -0
  89. package/esm/DotplotView/components/hooks/useMouseUpHandler.js +28 -0
  90. package/esm/DotplotView/components/hooks/useWheelHandler.d.ts +8 -0
  91. package/esm/DotplotView/components/hooks/useWheelHandler.js +44 -0
  92. package/esm/DotplotView/components/util.js +1 -3
  93. package/esm/DotplotView/model.d.ts +5 -0
  94. package/esm/DotplotView/model.js +35 -20
  95. package/esm/ServerSideRenderedBlockContent.js +4 -21
  96. package/esm/index.js +2 -0
  97. package/package.json +4 -4
@@ -1,41 +1,62 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { lazy, useState } from 'react';
2
3
  import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
3
4
  import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons';
4
- import CropDinIcon from '@mui/icons-material/CropDin';
5
- import CropLandscapeIcon from '@mui/icons-material/CropLandscape';
5
+ import { getSession } from '@jbrowse/core/util';
6
6
  import MoreVert from '@mui/icons-material/MoreVert';
7
- import SettingsOverscanIcon from '@mui/icons-material/SettingsOverscan';
7
+ import ShuffleIcon from '@mui/icons-material/Shuffle';
8
8
  import ZoomIn from '@mui/icons-material/ZoomIn';
9
9
  import ZoomOut from '@mui/icons-material/ZoomOut';
10
10
  import { IconButton } from '@mui/material';
11
11
  import { observer } from 'mobx-react';
12
+ import ColorBySelector from './ColorBySelector';
12
13
  import { CursorMouse, CursorMove } from './CursorIcon';
14
+ import MinLengthSlider from './MinLengthSlider';
15
+ import OpacitySlider from './OpacitySlider';
16
+ const DiagonalizationProgressDialog = lazy(() => import('./DiagonalizationProgressDialog'));
13
17
  const DotplotControls = observer(function ({ model, }) {
14
- return (_jsxs("div", { children: [_jsx(IconButton, { onClick: () => {
18
+ var _a;
19
+ const [showDynamicControls, setShowDynamicControls] = useState(true);
20
+ const hasDisplays = (_a = model.tracks[0]) === null || _a === void 0 ? void 0 : _a.displays[0];
21
+ return (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '4px' }, children: [_jsx(IconButton, { onClick: () => {
15
22
  model.zoomOut();
16
23
  }, children: _jsx(ZoomOut, {}) }), _jsx(IconButton, { onClick: () => {
17
24
  model.zoomIn();
18
25
  }, children: _jsx(ZoomIn, {}) }), _jsx(IconButton, { onClick: () => model.activateTrackSelector(), title: "Open track selector", children: _jsx(TrackSelectorIcon, {}) }), _jsx(CascadingMenuButton, { menuItems: [
19
26
  {
20
27
  label: 'Square view - same bp per pixel',
21
- icon: CropDinIcon,
22
28
  onClick: () => {
23
29
  model.squareView();
24
30
  },
31
+ helpText: 'Makes both views use the same zoom level (bp per pixel), adjusting to the average of each. This ensures features are displayed at comparable scales for easier visual comparison.',
25
32
  },
26
33
  {
27
34
  label: 'Rectangular view - same total bp',
28
- icon: CropLandscapeIcon,
29
35
  onClick: () => {
30
36
  model.squareViewProportional();
31
37
  },
38
+ helpText: 'Adjusts zoom levels proportionally so both views show the same total number of base pairs. This accounts for different view widths while maintaining the same total genomic span.',
32
39
  },
33
40
  {
34
41
  label: 'Show all regions',
35
- icon: SettingsOverscanIcon,
36
42
  onClick: () => {
37
43
  model.showAllRegions();
38
44
  },
45
+ helpText: 'Zooms out to display all genome assemblies in their entirety. Useful for getting a high-level overview or resetting the view after zooming into specific regions.',
46
+ },
47
+ {
48
+ label: 'Re-order chromosomes',
49
+ icon: ShuffleIcon,
50
+ onClick: () => {
51
+ getSession(model).queueDialog(handleClose => [
52
+ DiagonalizationProgressDialog,
53
+ {
54
+ handleClose,
55
+ model,
56
+ },
57
+ ]);
58
+ },
59
+ helpText: 'Diagonalization algorithmically reorders and reorients chromosomes to minimize crossing synteny lines, creating a more diagonal pattern. This makes it easier to identify large-scale genomic rearrangements, inversions, and translocations. The process runs on the webworker for better performance.',
39
60
  },
40
61
  {
41
62
  type: 'checkbox',
@@ -44,6 +65,7 @@ const DotplotControls = observer(function ({ model, }) {
44
65
  onClick: () => {
45
66
  model.setDrawCigar(!model.drawCigar);
46
67
  },
68
+ helpText: 'Toggle detailed CIGAR string visualization showing matches, insertions, and deletions in alignments. Disable for a cleaner view that shows only broad syntenic blocks.',
47
69
  },
48
70
  {
49
71
  label: 'Show pan buttons',
@@ -52,48 +74,64 @@ const DotplotControls = observer(function ({ model, }) {
52
74
  onClick: () => {
53
75
  model.setShowPanButtons(!model.showPanButtons);
54
76
  },
77
+ helpText: 'Show or hide directional pan buttons that allow you to navigate the dotplot view by clicking arrows. Useful for precise navigation without using mouse drag.',
78
+ },
79
+ {
80
+ label: 'Show dynamic controls',
81
+ type: 'checkbox',
82
+ checked: showDynamicControls,
83
+ onClick: () => {
84
+ setShowDynamicControls(!showDynamicControls);
85
+ },
86
+ helpText: 'Toggle visibility of dynamic controls like opacity and minimum length sliders. These controls allow you to adjust dotplot visualization parameters in real-time.',
55
87
  },
56
88
  {
57
89
  label: 'Click and drag mode',
90
+ helpText: 'Configure how clicking and dragging behaves in the dotplot view. Choose between panning and region selection as the default action.',
58
91
  subMenu: [
59
92
  {
60
- label: 'Pan by default, select region when ctrl/cmd key is held',
93
+ label: 'Pan by default',
61
94
  icon: CursorMove,
62
95
  type: 'radio',
63
96
  checked: model.cursorMode === 'move',
64
97
  onClick: () => {
65
98
  model.setCursorMode('move');
66
99
  },
100
+ helpText: 'Click and drag to pan the view. Hold Ctrl/Cmd while dragging to select a region for zooming or creating a linear synteny view.',
67
101
  },
68
102
  {
69
- label: 'Select region by default, pan when ctrl/cmd key is held',
103
+ label: 'Select region by default',
70
104
  icon: CursorMouse,
71
105
  type: 'radio',
72
106
  checked: model.cursorMode === 'crosshair',
73
107
  onClick: () => {
74
108
  model.setCursorMode('crosshair');
75
109
  },
110
+ helpText: 'Click and drag to select a region for zooming or creating a linear synteny view. Hold Ctrl/Cmd while dragging to pan the view instead.',
76
111
  },
77
112
  ],
78
113
  },
79
114
  {
80
115
  label: 'Wheel scroll mode',
116
+ helpText: 'Configure how mouse wheel scrolling behaves in the dotplot view.',
81
117
  subMenu: [
82
118
  {
83
- label: 'Pans view',
119
+ label: 'Pan view',
84
120
  type: 'radio',
85
121
  checked: model.wheelMode === 'pan',
86
122
  onClick: () => {
87
123
  model.setWheelMode('pan');
88
124
  },
125
+ helpText: 'Mouse wheel scrolling will pan the view up/down. Useful for navigating through the genome without changing zoom level.',
89
126
  },
90
127
  {
91
- label: 'Zooms view',
128
+ label: 'Zoom view',
92
129
  type: 'radio',
93
130
  checked: model.wheelMode === 'zoom',
94
131
  onClick: () => {
95
132
  model.setWheelMode('zoom');
96
133
  },
134
+ helpText: 'Mouse wheel scrolling will zoom in/out of the view. Provides quick zoom control for detailed inspection of regions.',
97
135
  },
98
136
  {
99
137
  label: 'Disable',
@@ -102,9 +140,10 @@ const DotplotControls = observer(function ({ model, }) {
102
140
  onClick: () => {
103
141
  model.setWheelMode('none');
104
142
  },
143
+ helpText: 'Mouse wheel scrolling will be disabled for the dotplot view. Use this to prevent accidental zoom or pan when scrolling the page.',
105
144
  },
106
145
  ],
107
146
  },
108
- ], children: _jsx(MoreVert, {}) })] }));
147
+ ], children: _jsx(MoreVert, {}) }), _jsx(ColorBySelector, { model: model }), hasDisplays && showDynamicControls ? (_jsxs(_Fragment, { children: [_jsx(OpacitySlider, { model: model }), _jsx(MinLengthSlider, { model: model })] })) : null] }));
109
148
  });
110
149
  export default DotplotControls;
@@ -0,0 +1,15 @@
1
+ import type { DotplotViewModel } from '../model';
2
+ type Coord = [number, number] | undefined;
3
+ interface DotplotTooltipsProps {
4
+ model: DotplotViewModel;
5
+ mouseOvered: boolean;
6
+ validSelect: boolean;
7
+ mouserect: Coord;
8
+ mouserectClient: Coord;
9
+ xdistance: number;
10
+ mousedown: Coord;
11
+ mousedownClient: Coord;
12
+ ydistance: number;
13
+ }
14
+ export default function DotplotTooltips({ model, mouseOvered, validSelect, mouserect, mouserectClient, xdistance, mousedown, mousedownClient, ydistance, }: DotplotTooltipsProps): import("react/jsx-runtime").JSX.Element;
15
+ export {};
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Suspense, lazy } from 'react';
3
+ const TooltipWhereClicked = lazy(() => import('./DotplotTooltipClick'));
4
+ const TooltipWhereMouseovered = lazy(() => import('./DotplotTooltipMouseover'));
5
+ export default function DotplotTooltips({ model, mouseOvered, validSelect, mouserect, mouserectClient, xdistance, mousedown, mousedownClient, ydistance, }) {
6
+ return (_jsxs(_Fragment, { children: [mouseOvered && validSelect ? (_jsx(Suspense, { fallback: null, children: _jsx(TooltipWhereMouseovered, { model: model, mouserect: mouserect, mouserectClient: mouserectClient, xdistance: xdistance }) })) : null, validSelect ? (_jsx(Suspense, { fallback: null, children: _jsx(TooltipWhereClicked, { model: model, mousedown: mousedown, mousedownClient: mousedownClient, xdistance: xdistance, ydistance: ydistance }) })) : null] }));
7
+ }
@@ -1,16 +1,19 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Suspense, lazy, useEffect, useRef, useState } from 'react';
3
- import { LoadingEllipses, Menu, ResizeHandle } from '@jbrowse/core/ui';
4
- import { transaction } from 'mobx';
2
+ import { LoadingEllipses, ResizeHandle } from '@jbrowse/core/ui';
5
3
  import { observer } from 'mobx-react';
6
4
  import { makeStyles } from 'tss-react/mui';
7
5
  import { HorizontalAxis, VerticalAxis } from './Axes';
8
- import Grid from './Grid';
6
+ import DotplotTooltips from './DotplotTooltips';
9
7
  import Header from './Header';
10
8
  import ImportForm from './ImportForm';
11
- const TooltipWhereClicked = lazy(() => import('./DotplotTooltipClick'));
12
- const TooltipWhereMouseovered = lazy(() => import('./DotplotTooltipMouseover'));
13
- const blank = { left: 0, top: 0, width: 0, height: 0 };
9
+ import MouseInteractionLayer from './MouseInteractionLayer';
10
+ import SelectionContextMenu from './SelectionContextMenu';
11
+ import { useCtrlKeyTracking } from './hooks/useCtrlKeyTracking';
12
+ import { useCursorMode } from './hooks/useCursorMode';
13
+ import { useMouseCoordinates } from './hooks/useMouseCoordinates';
14
+ import { useMouseMoveHandler } from './hooks/useMouseMoveHandler';
15
+ import { useMouseUpHandler } from './hooks/useMouseUpHandler';
16
+ import { useWheelHandler } from './hooks/useWheelHandler';
14
17
  const useStyles = makeStyles()(theme => ({
15
18
  spacer: {
16
19
  gridColumn: '1/2',
@@ -51,9 +54,6 @@ const useStyles = makeStyles()(theme => ({
51
54
  borderTop: '1px solid #fafafa',
52
55
  },
53
56
  }));
54
- function getOffset(coord, rect) {
55
- return coord && [coord[0] - rect.left, coord[1] - rect.top];
56
- }
57
57
  const RenderedComponent = observer(({ model }) => {
58
58
  const { classes } = useStyles();
59
59
  return (_jsx("div", { className: classes.overlay, children: model.tracks.map(track => {
@@ -63,118 +63,14 @@ const RenderedComponent = observer(({ model }) => {
63
63
  }) }));
64
64
  });
65
65
  const DotplotViewInternal = observer(function ({ model, }) {
66
- var _a, _b;
67
66
  const { classes } = useStyles();
68
- const [mousecurrClient, setMouseCurrClient] = useState();
69
- const [mousedownClient, setMouseDownClient] = useState();
70
- const [mouseOvered, setMouseOvered] = useState(false);
71
- const [mouseupClient, setMouseUpClient] = useState();
72
- const ref = useRef(null);
73
- const root = useRef(null);
74
- const distanceX = useRef(0);
75
- const distanceY = useRef(0);
76
- const scheduled = useRef(false);
77
- const [ctrlKeyWasUsed, setCtrlKeyWasUsed] = useState(false);
78
- const [ctrlKeyDown, setCtrlKeyDown] = useState(false);
79
- const svg = ((_a = ref.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect()) || blank;
80
- const rootRect = ((_b = ref.current) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect()) || blank;
81
- const mousedown = getOffset(mousedownClient, svg);
82
- const mousecurr = getOffset(mousecurrClient, svg);
83
- const mouseup = getOffset(mouseupClient, svg);
84
- const mouserect = mouseup || mousecurr;
85
- const mouserectClient = mouseupClient || mousecurrClient;
86
- const xdistance = mousedown && mouserect ? mouserect[0] - mousedown[0] : 0;
87
- const ydistance = mousedown && mouserect ? mouserect[1] - mousedown[1] : 0;
88
67
  const { hview, vview, wheelMode, cursorMode } = model;
89
- const validPan = (cursorMode === 'move' && !ctrlKeyWasUsed) ||
90
- (cursorMode === 'crosshair' && ctrlKeyWasUsed);
91
- const validSelect = (cursorMode === 'move' && ctrlKeyWasUsed) ||
92
- (cursorMode === 'crosshair' && !ctrlKeyWasUsed);
93
- useEffect(() => {
94
- function onWheel(event) {
95
- event.preventDefault();
96
- distanceX.current += event.deltaX;
97
- distanceY.current -= event.deltaY;
98
- if (!scheduled.current) {
99
- scheduled.current = true;
100
- window.requestAnimationFrame(() => {
101
- transaction(() => {
102
- if (wheelMode === 'pan') {
103
- hview.scroll(distanceX.current / 3);
104
- vview.scroll(distanceY.current / 10);
105
- }
106
- else if (wheelMode === 'zoom') {
107
- if (Math.abs(distanceY.current) > Math.abs(distanceX.current) * 2 &&
108
- mousecurr) {
109
- const val = distanceY.current < 0 ? 1.1 : 0.9;
110
- hview.zoomTo(hview.bpPerPx * val, mousecurr[0]);
111
- vview.zoomTo(vview.bpPerPx * val, rootRect.height - mousecurr[1]);
112
- }
113
- }
114
- });
115
- scheduled.current = false;
116
- distanceX.current = 0;
117
- distanceY.current = 0;
118
- });
119
- }
120
- }
121
- if (ref.current) {
122
- const curr = ref.current;
123
- curr.addEventListener('wheel', onWheel);
124
- return () => {
125
- curr.removeEventListener('wheel', onWheel);
126
- };
127
- }
128
- return () => { };
129
- }, [hview, vview, wheelMode, mousecurr, rootRect.height]);
130
- useEffect(() => {
131
- function globalMouseMove(event) {
132
- setMouseCurrClient([event.clientX, event.clientY]);
133
- if (mousecurrClient && mousedownClient && validPan && !mouseupClient) {
134
- hview.scroll(-event.clientX + mousecurrClient[0]);
135
- vview.scroll(event.clientY - mousecurrClient[1]);
136
- }
137
- }
138
- window.addEventListener('mousemove', globalMouseMove);
139
- return () => {
140
- window.removeEventListener('mousemove', globalMouseMove);
141
- };
142
- }, [validPan, mousecurrClient, mousedownClient, mouseupClient, hview, vview]);
143
- useEffect(() => {
144
- function globalCtrlKeyDown(event) {
145
- if (event.metaKey || event.ctrlKey) {
146
- setCtrlKeyDown(true);
147
- }
148
- }
149
- function globalCtrlKeyUp(event) {
150
- if (!event.metaKey && !event.ctrlKey) {
151
- setCtrlKeyDown(false);
152
- }
153
- }
154
- window.addEventListener('keydown', globalCtrlKeyDown);
155
- window.addEventListener('keyup', globalCtrlKeyUp);
156
- return () => {
157
- window.removeEventListener('keydown', globalCtrlKeyDown);
158
- window.addEventListener('keyup', globalCtrlKeyUp);
159
- };
160
- }, []);
161
- useEffect(() => {
162
- function globalMouseUp(event) {
163
- if (Math.abs(xdistance) > 3 && Math.abs(ydistance) > 3 && validSelect) {
164
- setMouseUpClient([event.clientX, event.clientY]);
165
- }
166
- else {
167
- setMouseDownClient(undefined);
168
- }
169
- }
170
- if (mousedown && !mouseup) {
171
- window.addEventListener('mouseup', globalMouseUp, true);
172
- return () => {
173
- window.removeEventListener('mouseup', globalMouseUp, true);
174
- };
175
- }
176
- return () => { };
177
- }, [validSelect, mousedown, mouseup, xdistance, ydistance]);
68
+ const { mousecurrClient, mousedownClient, mouseupClient, mouseOvered, setMouseCurrClient, setMouseDownClient, setMouseUpClient, setMouseOvered, ref, root, rootRect, mousedown, mousecurr, mouseup, mouserect, mouserectClient, xdistance, ydistance, } = useMouseCoordinates();
69
+ const { ctrlKeyDown, validPan, validSelect, setCtrlKeyWasUsed, setCtrlKeyDown, } = useCursorMode(cursorMode);
70
+ useWheelHandler(ref, wheelMode, hview, vview, mousecurr, rootRect.height);
71
+ useMouseMoveHandler(mousecurrClient, mousedownClient, mouseupClient, validPan, hview, vview, setMouseCurrClient);
72
+ useCtrlKeyTracking(setCtrlKeyDown);
73
+ useMouseUpHandler(mousedown, mouseup, xdistance, ydistance, validSelect, setMouseUpClient, setMouseDownClient);
178
74
  return (_jsxs("div", { children: [_jsx(Header, { model: model, selection: !validSelect || !(mousedown && mouserect)
179
75
  ? undefined
180
76
  : {
@@ -184,45 +80,7 @@ const DotplotViewInternal = observer(function ({ model, }) {
184
80
  setMouseOvered(false);
185
81
  }, onMouseEnter: () => {
186
82
  setMouseOvered(true);
187
- }, children: [_jsxs("div", { className: classes.container, children: [_jsx(VerticalAxis, { model: model }), _jsx(HorizontalAxis, { model: model }), _jsxs("div", { ref: ref, className: classes.content, children: [mouseOvered && validSelect ? (_jsx(Suspense, { fallback: null, children: _jsx(TooltipWhereMouseovered, { model: model, mouserect: mouserect, mouserectClient: mouserectClient, xdistance: xdistance }) })) : null, validSelect ? (_jsx(Suspense, { fallback: null, children: _jsx(TooltipWhereClicked, { model: model, mousedown: mousedown, mousedownClient: mousedownClient, xdistance: xdistance, ydistance: ydistance }) })) : null, _jsx("div", { style: { cursor: ctrlKeyDown ? 'pointer' : cursorMode }, onMouseDown: event => {
188
- if (event.button === 0) {
189
- const { clientX, clientY } = event;
190
- setMouseDownClient([clientX, clientY]);
191
- setMouseCurrClient([clientX, clientY]);
192
- setCtrlKeyWasUsed(ctrlKeyDown);
193
- }
194
- }, children: _jsx(Grid, { model: model, children: validSelect && mousedown && mouserect ? (_jsx("rect", { fill: "rgba(255,0,0,0.3)", x: Math.min(mouserect[0], mousedown[0]), y: Math.min(mouserect[1], mousedown[1]), width: Math.abs(xdistance), height: Math.abs(ydistance) })) : null }) }), _jsx("div", { className: classes.spacer })] }), _jsx(RenderedComponent, { model: model }), _jsx(Menu, { open: Boolean(mouseup), onMenuItemClick: (_, callback) => {
195
- callback();
196
- setMouseUpClient(undefined);
197
- setMouseDownClient(undefined);
198
- }, onClose: () => {
199
- setMouseUpClient(undefined);
200
- setMouseDownClient(undefined);
201
- }, anchorReference: "anchorPosition", anchorPosition: mouseupClient
202
- ? {
203
- top: mouseupClient[1] + 50,
204
- left: mouseupClient[0] + 50,
205
- }
206
- : undefined, style: { zIndex: 800 }, menuItems: [
207
- {
208
- label: 'Zoom in',
209
- onClick: () => {
210
- if (mousedown && mouseup) {
211
- model.zoomInToMouseCoords(mousedown, mouseup);
212
- }
213
- setMouseOvered(false);
214
- },
215
- },
216
- {
217
- label: 'Open linear synteny view',
218
- onClick: () => {
219
- if (mousedown && mouseup) {
220
- model.onDotplotView(mousedown, mouseup);
221
- }
222
- setMouseOvered(false);
223
- },
224
- },
225
- ] })] }), _jsx(ResizeHandle, { onDrag: n => model.setHeight(model.height + n), className: classes.resizeHandle })] })] }));
83
+ }, children: [_jsxs("div", { className: classes.container, children: [_jsx(VerticalAxis, { model: model }), _jsx(HorizontalAxis, { model: model }), _jsxs("div", { ref: ref, className: classes.content, children: [_jsx(DotplotTooltips, { model: model, mouseOvered: mouseOvered, validSelect: validSelect, mouserect: mouserect, mouserectClient: mouserectClient, xdistance: xdistance, mousedown: mousedown, mousedownClient: mousedownClient, ydistance: ydistance }), _jsx(MouseInteractionLayer, { model: model, ctrlKeyDown: ctrlKeyDown, cursorMode: cursorMode, validSelect: validSelect, mousedown: mousedown, mouserect: mouserect, xdistance: xdistance, ydistance: ydistance, setMouseDownClient: setMouseDownClient, setMouseCurrClient: setMouseCurrClient, setCtrlKeyWasUsed: setCtrlKeyWasUsed }), _jsx("div", { className: classes.spacer })] }), _jsx(RenderedComponent, { model: model }), _jsx(SelectionContextMenu, { model: model, mouseup: mouseup, mouseupClient: mouseupClient, mousedown: mousedown, setMouseUpClient: setMouseUpClient, setMouseDownClient: setMouseDownClient, setMouseOvered: setMouseOvered })] }), _jsx(ResizeHandle, { onDrag: n => model.setHeight(model.height + n), className: classes.resizeHandle })] })] }));
226
84
  });
227
85
  const DotplotView = observer(function ({ model }) {
228
86
  const { initialized, loading, error } = model;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { lazy, useState } from 'react';
2
+ import { Suspense, lazy, useState } from 'react';
3
3
  import { Alert, Button } from '@mui/material';
4
4
  import { observer } from 'mobx-react';
5
5
  const WarningDialog = lazy(() => import('./WarningDialog'));
@@ -9,9 +9,9 @@ const DotplotWarnings = observer(function ({ model, }) {
9
9
  const [hide, setHide] = useState(false);
10
10
  return trackWarnings.length && !hide ? (_jsxs(Alert, { severity: "warning", children: ["Warnings during render", ' ', _jsx(Button, { onClick: () => {
11
11
  setShown(true);
12
- }, children: "More info" }), shown ? (_jsx(WarningDialog, { trackWarnings: trackWarnings, handleClose: () => {
13
- setShown(false);
14
- } })) : null, _jsx(Button, { variant: "contained", onClick: () => {
12
+ }, children: "More info" }), shown ? (_jsx(Suspense, { fallback: null, children: _jsx(WarningDialog, { trackWarnings: trackWarnings, handleClose: () => {
13
+ setShown(false);
14
+ } }) })) : null, _jsx(Button, { variant: "contained", onClick: () => {
15
15
  setHide(true);
16
16
  }, children: "Dismiss" })] })) : null;
17
17
  });
@@ -34,7 +34,6 @@ function doSubmit({ model, assembly1, assembly2, }) {
34
34
  }
35
35
  });
36
36
  }
37
- model.showAllRegions();
38
37
  model.setAssemblyNames(assembly2, assembly1);
39
38
  });
40
39
  }
@@ -0,0 +1,5 @@
1
+ import type { DotplotViewModel } from '../model';
2
+ declare const MinLengthSlider: ({ model, }: {
3
+ model: DotplotViewModel;
4
+ }) => import("react/jsx-runtime").JSX.Element;
5
+ export default MinLengthSlider;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { Slider, Typography } from '@mui/material';
4
+ import { observer } from 'mobx-react';
5
+ import { makeStyles } from 'tss-react/mui';
6
+ import SliderTooltip from './SliderTooltip';
7
+ const useStyles = makeStyles()({
8
+ container: {
9
+ display: 'flex',
10
+ alignItems: 'center',
11
+ marginLeft: 16,
12
+ marginRight: 16,
13
+ minWidth: 150,
14
+ },
15
+ });
16
+ const MinLengthSlider = observer(function ({ model, }) {
17
+ var _a, _b;
18
+ const { classes } = useStyles();
19
+ const firstDisplay = (_a = model.tracks[0]) === null || _a === void 0 ? void 0 : _a.displays[0];
20
+ const minAlignmentLength = (_b = firstDisplay === null || firstDisplay === void 0 ? void 0 : firstDisplay.minAlignmentLength) !== null && _b !== void 0 ? _b : 0;
21
+ const [minLengthValue, setMinLengthValue] = useState(Math.log2(Math.max(1, minAlignmentLength)) * 100);
22
+ useEffect(() => {
23
+ setMinLengthValue(Math.log2(Math.max(1, minAlignmentLength)) * 100);
24
+ }, [minAlignmentLength]);
25
+ return (_jsxs("span", { className: classes.container, children: [_jsx(Typography, { variant: "body2", style: { marginRight: 8 }, children: "Min length:" }), _jsx(Slider, { value: minLengthValue, onChange: (_, val) => {
26
+ setMinLengthValue(val);
27
+ }, onChangeCommitted: () => {
28
+ const newMinLength = Math.round(2 ** (minLengthValue / 100));
29
+ for (const track of model.tracks) {
30
+ for (const display of track.displays) {
31
+ ;
32
+ display.setMinAlignmentLength(newMinLength);
33
+ }
34
+ }
35
+ }, min: 0, max: Math.log2(1000000) * 100, valueLabelDisplay: "auto", valueLabelFormat: newValue => Math.round(2 ** (newValue / 100)).toLocaleString(), size: "small", style: { minWidth: 100 }, slots: {
36
+ valueLabel: SliderTooltip,
37
+ } })] }));
38
+ });
39
+ export default MinLengthSlider;
@@ -0,0 +1,17 @@
1
+ import type { DotplotViewModel } from '../model';
2
+ type Coord = [number, number] | undefined;
3
+ interface MouseInteractionLayerProps {
4
+ model: DotplotViewModel;
5
+ ctrlKeyDown: boolean;
6
+ cursorMode: string;
7
+ validSelect: boolean;
8
+ mousedown: Coord;
9
+ mouserect: Coord;
10
+ xdistance: number;
11
+ ydistance: number;
12
+ setMouseDownClient: (coord: Coord) => void;
13
+ setMouseCurrClient: (coord: Coord) => void;
14
+ setCtrlKeyWasUsed: (wasUsed: boolean) => void;
15
+ }
16
+ export default function MouseInteractionLayer({ model, ctrlKeyDown, cursorMode, validSelect, mousedown, mouserect, xdistance, ydistance, setMouseDownClient, setMouseCurrClient, setCtrlKeyWasUsed, }: MouseInteractionLayerProps): import("react/jsx-runtime").JSX.Element;
17
+ export {};
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import Grid from './Grid';
3
+ export default function MouseInteractionLayer({ model, ctrlKeyDown, cursorMode, validSelect, mousedown, mouserect, xdistance, ydistance, setMouseDownClient, setMouseCurrClient, setCtrlKeyWasUsed, }) {
4
+ return (_jsx("div", { style: { cursor: ctrlKeyDown ? 'pointer' : cursorMode }, onMouseDown: event => {
5
+ if (event.button === 0) {
6
+ const { clientX, clientY } = event;
7
+ setMouseDownClient([clientX, clientY]);
8
+ setMouseCurrClient([clientX, clientY]);
9
+ setCtrlKeyWasUsed(ctrlKeyDown);
10
+ }
11
+ }, children: _jsx(Grid, { model: model, children: validSelect && mousedown && mouserect ? (_jsx("rect", { fill: "rgba(255,0,0,0.3)", x: Math.min(mouserect[0], mousedown[0]), y: Math.min(mouserect[1], mousedown[1]), width: Math.abs(xdistance), height: Math.abs(ydistance) })) : null }) }));
12
+ }
@@ -0,0 +1,5 @@
1
+ import type { DotplotViewModel } from '../model';
2
+ declare const OpacitySlider: ({ model, }: {
3
+ model: DotplotViewModel;
4
+ }) => import("react/jsx-runtime").JSX.Element;
5
+ export default OpacitySlider;
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Slider, Typography } from '@mui/material';
3
+ import { observer } from 'mobx-react';
4
+ import { makeStyles } from 'tss-react/mui';
5
+ import SliderTooltip from './SliderTooltip';
6
+ const useStyles = makeStyles()({
7
+ container: {
8
+ display: 'flex',
9
+ alignItems: 'center',
10
+ marginLeft: 16,
11
+ marginRight: 16,
12
+ minWidth: 150,
13
+ },
14
+ });
15
+ const OpacitySlider = observer(function ({ model, }) {
16
+ var _a, _b;
17
+ const { classes } = useStyles();
18
+ const firstDisplay = (_a = model.tracks[0]) === null || _a === void 0 ? void 0 : _a.displays[0];
19
+ const alpha = (_b = firstDisplay === null || firstDisplay === void 0 ? void 0 : firstDisplay.alpha) !== null && _b !== void 0 ? _b : 1;
20
+ const exponent = 3;
21
+ const alphaToSlider = (a) => Math.pow(a, 1 / exponent);
22
+ const sliderToAlpha = (s) => Math.pow(s, exponent);
23
+ const sliderValue = alphaToSlider(alpha);
24
+ const handleAlphaChange = (_event, value) => {
25
+ const sliderVal = typeof value === 'number' ? value : value[0];
26
+ const newAlpha = sliderToAlpha(sliderVal);
27
+ for (const track of model.tracks) {
28
+ for (const display of track.displays) {
29
+ ;
30
+ display.setAlpha(newAlpha);
31
+ }
32
+ }
33
+ };
34
+ return (_jsxs("span", { className: classes.container, children: [_jsx(Typography, { variant: "body2", style: { marginRight: 8 }, children: "Opacity:" }), _jsx(Slider, { value: sliderValue, onChange: handleAlphaChange, min: 0, max: 1, step: 0.01, valueLabelDisplay: "auto", size: "small", style: { minWidth: 100 }, slots: {
35
+ valueLabel: SliderTooltip,
36
+ }, valueLabelFormat: value => sliderToAlpha(value).toFixed(3) })] }));
37
+ });
38
+ export default OpacitySlider;
@@ -0,0 +1,13 @@
1
+ import type { DotplotViewModel } from '../model';
2
+ type Coord = [number, number] | undefined;
3
+ interface SelectionContextMenuProps {
4
+ model: DotplotViewModel;
5
+ mouseup: Coord;
6
+ mouseupClient: Coord;
7
+ mousedown: Coord;
8
+ setMouseUpClient: (coord: Coord) => void;
9
+ setMouseDownClient: (coord: Coord) => void;
10
+ setMouseOvered: (isOvered: boolean) => void;
11
+ }
12
+ export default function SelectionContextMenu({ model, mouseup, mouseupClient, mousedown, setMouseUpClient, setMouseDownClient, setMouseOvered, }: SelectionContextMenuProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Menu } from '@jbrowse/core/ui';
3
+ function getSelectionMenuItems(model, mousedown, mouseup, setMouseOvered) {
4
+ return [
5
+ {
6
+ label: 'Zoom in',
7
+ onClick: () => {
8
+ if (mousedown && mouseup) {
9
+ model.zoomInToMouseCoords(mousedown, mouseup);
10
+ }
11
+ setMouseOvered(false);
12
+ },
13
+ },
14
+ {
15
+ label: 'Open linear synteny view',
16
+ onClick: () => {
17
+ if (mousedown && mouseup) {
18
+ model.onDotplotView(mousedown, mouseup);
19
+ }
20
+ setMouseOvered(false);
21
+ },
22
+ },
23
+ ];
24
+ }
25
+ export default function SelectionContextMenu({ model, mouseup, mouseupClient, mousedown, setMouseUpClient, setMouseDownClient, setMouseOvered, }) {
26
+ return (_jsx(Menu, { open: Boolean(mouseup), onMenuItemClick: (_, callback) => {
27
+ callback();
28
+ setMouseUpClient(undefined);
29
+ setMouseDownClient(undefined);
30
+ }, onClose: () => {
31
+ setMouseUpClient(undefined);
32
+ setMouseDownClient(undefined);
33
+ }, anchorReference: "anchorPosition", anchorPosition: mouseupClient
34
+ ? {
35
+ top: mouseupClient[1] + 50,
36
+ left: mouseupClient[0] + 50,
37
+ }
38
+ : undefined, style: { zIndex: 11000 }, menuItems: getSelectionMenuItems(model, mousedown, mouseup, setMouseOvered) }));
39
+ }
@@ -0,0 +1,2 @@
1
+ import type { SliderValueLabelProps } from '@mui/material';
2
+ export default function SliderTooltip(props: SliderValueLabelProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Tooltip } from '@mui/material';
3
+ export default function SliderTooltip(props) {
4
+ const { children, open, value } = props;
5
+ return (_jsx(Tooltip, { open: open, enterTouchDelay: 0, placement: "top", title: value, arrow: true, children: children }));
6
+ }
@@ -0,0 +1 @@
1
+ export declare function useCtrlKeyTracking(setCtrlKeyDown: (isDown: boolean) => void): void;