@jbrowse/plugin-breakpoint-split-view 3.6.5 → 4.0.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 (99) hide show
  1. package/esm/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.js +1 -1
  2. package/esm/BreakpointAlignmentsFeatureDetail/index.js +2 -2
  3. package/esm/BreakpointGetFeatures/BreakpointGetFeatures.d.ts +51 -0
  4. package/esm/BreakpointGetFeatures/BreakpointGetFeatures.js +57 -0
  5. package/esm/BreakpointGetFeatures/index.d.ts +2 -0
  6. package/esm/BreakpointGetFeatures/index.js +4 -0
  7. package/esm/BreakpointSplitView/BreakpointSplitView.js +33 -47
  8. package/esm/BreakpointSplitView/components/AlignmentConnections.d.ts +2 -7
  9. package/esm/BreakpointSplitView/components/AlignmentConnections.js +20 -25
  10. package/esm/BreakpointSplitView/components/Breakends.d.ts +2 -7
  11. package/esm/BreakpointSplitView/components/Breakends.js +28 -59
  12. package/esm/BreakpointSplitView/components/BreakpointSplitView.d.ts +1 -1
  13. package/esm/BreakpointSplitView/components/BreakpointSplitView.js +19 -5
  14. package/esm/BreakpointSplitView/components/BreakpointSplitViewOverlay.d.ts +1 -1
  15. package/esm/BreakpointSplitView/components/BreakpointSplitViewOverlay.js +3 -3
  16. package/esm/BreakpointSplitView/components/ExportSvgDialog.d.ts +1 -1
  17. package/esm/BreakpointSplitView/components/ExportSvgDialog.js +5 -1
  18. package/esm/BreakpointSplitView/components/Header.d.ts +5 -0
  19. package/esm/BreakpointSplitView/components/Header.js +47 -0
  20. package/esm/BreakpointSplitView/components/HeaderSearchBoxes.d.ts +5 -0
  21. package/esm/BreakpointSplitView/components/HeaderSearchBoxes.js +22 -0
  22. package/esm/BreakpointSplitView/components/Overlay.d.ts +1 -1
  23. package/esm/BreakpointSplitView/components/Overlay.js +8 -11
  24. package/esm/BreakpointSplitView/components/PairedFeatures.d.ts +2 -7
  25. package/esm/BreakpointSplitView/components/PairedFeatures.js +22 -47
  26. package/esm/BreakpointSplitView/components/Rubberband.d.ts +6 -0
  27. package/esm/BreakpointSplitView/components/Rubberband.js +27 -0
  28. package/esm/BreakpointSplitView/components/RubberbandSpan.d.ts +15 -0
  29. package/esm/BreakpointSplitView/components/RubberbandSpan.js +30 -0
  30. package/esm/BreakpointSplitView/components/RubberbandTooltip.d.ts +5 -0
  31. package/esm/BreakpointSplitView/components/RubberbandTooltip.js +17 -0
  32. package/esm/BreakpointSplitView/components/Translocations.d.ts +2 -7
  33. package/esm/BreakpointSplitView/components/Translocations.js +23 -58
  34. package/esm/BreakpointSplitView/components/VerticalGuide.d.ts +6 -0
  35. package/esm/BreakpointSplitView/components/VerticalGuide.js +24 -0
  36. package/esm/BreakpointSplitView/components/overlayUtils.d.ts +24 -0
  37. package/esm/BreakpointSplitView/components/overlayUtils.js +47 -0
  38. package/esm/BreakpointSplitView/components/rubberbandUtil.d.ts +4 -0
  39. package/esm/BreakpointSplitView/components/rubberbandUtil.js +3 -0
  40. package/esm/BreakpointSplitView/components/useRangeSelect.d.ts +59 -0
  41. package/esm/BreakpointSplitView/components/useRangeSelect.js +121 -0
  42. package/esm/BreakpointSplitView/components/util.js +1 -2
  43. package/esm/BreakpointSplitView/getClip.js +4 -4
  44. package/esm/BreakpointSplitView/index.js +2 -2
  45. package/esm/BreakpointSplitView/model.d.ts +403 -116
  46. package/esm/BreakpointSplitView/model.js +117 -41
  47. package/esm/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.d.ts +2 -2
  48. package/esm/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js +8 -8
  49. package/esm/BreakpointSplitView/types.d.ts +8 -0
  50. package/esm/BreakpointSplitView/util.d.ts +1 -1
  51. package/esm/BreakpointSplitView/util.js +8 -17
  52. package/esm/LaunchBreakpointSplitView/index.d.ts +2 -0
  53. package/esm/LaunchBreakpointSplitView/index.js +12 -0
  54. package/esm/index.js +7 -6
  55. package/package.json +28 -34
  56. package/dist/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.d.ts +0 -10
  57. package/dist/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.js +0 -12
  58. package/dist/BreakpointAlignmentsFeatureDetail/index.d.ts +0 -2
  59. package/dist/BreakpointAlignmentsFeatureDetail/index.js +0 -67
  60. package/dist/BreakpointSplitView/BreakpointSplitView.d.ts +0 -34
  61. package/dist/BreakpointSplitView/BreakpointSplitView.js +0 -84
  62. package/dist/BreakpointSplitView/components/AlignmentConnections.d.ts +0 -8
  63. package/dist/BreakpointSplitView/components/AlignmentConnections.js +0 -133
  64. package/dist/BreakpointSplitView/components/Breakends.d.ts +0 -8
  65. package/dist/BreakpointSplitView/components/Breakends.js +0 -95
  66. package/dist/BreakpointSplitView/components/BreakpointSplitView.d.ts +0 -5
  67. package/dist/BreakpointSplitView/components/BreakpointSplitView.js +0 -45
  68. package/dist/BreakpointSplitView/components/BreakpointSplitViewOverlay.d.ts +0 -5
  69. package/dist/BreakpointSplitView/components/BreakpointSplitViewOverlay.js +0 -33
  70. package/dist/BreakpointSplitView/components/ExportSvgDialog.d.ts +0 -7
  71. package/dist/BreakpointSplitView/components/ExportSvgDialog.js +0 -57
  72. package/dist/BreakpointSplitView/components/Overlay.d.ts +0 -8
  73. package/dist/BreakpointSplitView/components/Overlay.js +0 -27
  74. package/dist/BreakpointSplitView/components/PairedFeatures.d.ts +0 -8
  75. package/dist/BreakpointSplitView/components/PairedFeatures.js +0 -75
  76. package/dist/BreakpointSplitView/components/Translocations.d.ts +0 -8
  77. package/dist/BreakpointSplitView/components/Translocations.js +0 -99
  78. package/dist/BreakpointSplitView/components/getOrientationColor.d.ts +0 -41
  79. package/dist/BreakpointSplitView/components/getOrientationColor.js +0 -103
  80. package/dist/BreakpointSplitView/components/util.d.ts +0 -8
  81. package/dist/BreakpointSplitView/components/util.js +0 -142
  82. package/dist/BreakpointSplitView/getClip.d.ts +0 -1
  83. package/dist/BreakpointSplitView/getClip.js +0 -10
  84. package/dist/BreakpointSplitView/index.d.ts +0 -2
  85. package/dist/BreakpointSplitView/index.js +0 -52
  86. package/dist/BreakpointSplitView/model.d.ts +0 -342
  87. package/dist/BreakpointSplitView/model.js +0 -244
  88. package/dist/BreakpointSplitView/svgcomponents/SVGBackground.d.ts +0 -5
  89. package/dist/BreakpointSplitView/svgcomponents/SVGBackground.js +0 -10
  90. package/dist/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.d.ts +0 -5
  91. package/dist/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js +0 -41
  92. package/dist/BreakpointSplitView/svgcomponents/util.d.ts +0 -4
  93. package/dist/BreakpointSplitView/svgcomponents/util.js +0 -18
  94. package/dist/BreakpointSplitView/types.d.ts +0 -22
  95. package/dist/BreakpointSplitView/types.js +0 -2
  96. package/dist/BreakpointSplitView/util.d.ts +0 -28
  97. package/dist/BreakpointSplitView/util.js +0 -67
  98. package/dist/index.d.ts +0 -7
  99. package/dist/index.js +0 -20
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
4
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
5
+ import MoreVertIcon from '@mui/icons-material/MoreVert';
6
+ import SearchIcon from '@mui/icons-material/Search';
7
+ import { FormGroup } from '@mui/material';
8
+ import { observer } from 'mobx-react';
9
+ import HeaderSearchBoxes from "./HeaderSearchBoxes.js";
10
+ const useStyles = makeStyles()({
11
+ inline: {
12
+ display: 'inline-flex',
13
+ },
14
+ });
15
+ const Header = observer(function Header({ model, }) {
16
+ const { classes } = useStyles();
17
+ const { views } = model;
18
+ const [showSearchBoxes, setShowSearchBoxes] = useState(views.length <= 3);
19
+ const [sideBySide, setSideBySide] = useState(views.length <= 3);
20
+ return (_jsxs(FormGroup, { row: true, children: [_jsx(CascadingMenuButton, { menuItems: () => model.menuItems(), children: _jsx(MoreVertIcon, {}) }), _jsx(CascadingMenuButton, { menuItems: [
21
+ {
22
+ label: 'Show search boxes',
23
+ type: 'checkbox',
24
+ checked: showSearchBoxes,
25
+ onClick: () => {
26
+ setShowSearchBoxes(!showSearchBoxes);
27
+ },
28
+ },
29
+ {
30
+ label: 'Orientation - Side-by-side',
31
+ type: 'radio',
32
+ checked: sideBySide,
33
+ onClick: () => {
34
+ setSideBySide(!sideBySide);
35
+ },
36
+ },
37
+ {
38
+ label: 'Orientation - Vertical',
39
+ type: 'radio',
40
+ checked: !sideBySide,
41
+ onClick: () => {
42
+ setSideBySide(!sideBySide);
43
+ },
44
+ },
45
+ ], children: _jsx(SearchIcon, {}) }), showSearchBoxes ? (_jsx("span", { className: sideBySide ? classes.inline : undefined, children: views.map(view => (_jsx(HeaderSearchBoxes, { view: view }, view.id))) })) : null] }));
46
+ });
47
+ export default Header;
@@ -0,0 +1,5 @@
1
+ import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
2
+ declare const HeaderSearchBoxes: ({ view, }: {
3
+ view: LinearGenomeViewModel;
4
+ }) => import("react/jsx-runtime").JSX.Element;
5
+ export default HeaderSearchBoxes;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { getBpDisplayStr } from '@jbrowse/core/util';
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
4
+ import { SearchBox } from '@jbrowse/plugin-linear-genome-view';
5
+ import { Typography } from '@mui/material';
6
+ import { observer } from 'mobx-react';
7
+ const useStyles = makeStyles()(() => ({
8
+ bp: {
9
+ display: 'flex',
10
+ alignItems: 'center',
11
+ marginLeft: 10,
12
+ },
13
+ searchBox: {
14
+ display: 'flex',
15
+ },
16
+ }));
17
+ const HeaderSearchBoxes = observer(function HeaderSearchBoxes({ view, }) {
18
+ const { classes } = useStyles();
19
+ const { assemblyDisplayNames, coarseTotalBp } = view;
20
+ return (_jsxs("span", { className: classes.searchBox, children: [_jsx(SearchBox, { model: view, showHelp: false }), _jsxs(Typography, { variant: "body2", color: "textSecondary", className: classes.bp, children: [assemblyDisplayNames.join(','), " ", getBpDisplayStr(coarseTotalBp)] })] }));
21
+ });
22
+ export default HeaderSearchBoxes;
@@ -1,4 +1,4 @@
1
- import type { BreakpointViewModel } from '../model';
1
+ import type { BreakpointViewModel } from '../model.ts';
2
2
  declare const Overlay: (props: {
3
3
  parentRef: React.RefObject<SVGSVGElement | null>;
4
4
  model: BreakpointViewModel;
@@ -1,22 +1,19 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { observer } from 'mobx-react';
3
- import AlignmentConnections from './AlignmentConnections';
4
- import Breakends from './Breakends';
5
- import PairedFeatures from './PairedFeatures';
6
- import Translocations from './Translocations';
7
- const Overlay = observer(function (props) {
8
- var _a;
3
+ import AlignmentConnections from "./AlignmentConnections.js";
4
+ import Breakends from "./Breakends.js";
5
+ import PairedFeatures from "./PairedFeatures.js";
6
+ import Translocations from "./Translocations.js";
7
+ const Overlay = observer(function Overlay(props) {
9
8
  const { model, trackId } = props;
10
9
  const tracks = model.getMatchedTracks(trackId);
11
- const type = (_a = tracks[0]) === null || _a === void 0 ? void 0 : _a.type;
10
+ const type = tracks[0]?.type;
12
11
  if (type === 'AlignmentsTrack') {
13
12
  return _jsx(AlignmentConnections, { ...props });
14
13
  }
15
- else if (type === 'VariantTrack') {
14
+ if (type === 'VariantTrack') {
16
15
  return model.hasTranslocations(trackId) ? (_jsx(Translocations, { ...props })) : model.hasPairedFeatures(trackId) ? (_jsx(PairedFeatures, { ...props })) : (_jsx(Breakends, { ...props }));
17
16
  }
18
- else {
19
- return null;
20
- }
17
+ return null;
21
18
  });
22
19
  export default Overlay;
@@ -1,8 +1,3 @@
1
- import type { BreakpointViewModel } from '../model';
2
- declare const PairedFeatures: ({ model, trackId, parentRef: ref, getTrackYPosOverride, }: {
3
- model: BreakpointViewModel;
4
- trackId: string;
5
- parentRef: React.RefObject<SVGSVGElement | null>;
6
- getTrackYPosOverride?: (trackId: string, level: number) => number;
7
- }) => import("react/jsx-runtime").JSX.Element | null;
1
+ import type { OverlayProps } from './overlayUtils.tsx';
2
+ declare const PairedFeatures: ({ model, trackId, parentRef, getTrackYPosOverride, }: OverlayProps) => import("react/jsx-runtime").JSX.Element | null;
8
3
  export default PairedFeatures;
@@ -1,71 +1,46 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useMemo, useState } from 'react';
3
3
  import { getSession } from '@jbrowse/core/util';
4
+ import { getSnapshot } from '@jbrowse/mobx-state-tree';
4
5
  import { observer } from 'mobx-react';
5
- import { getSnapshot } from 'mobx-state-tree';
6
- import { getMatchedPairedFeatures } from './util';
7
- import { getPxFromCoordinate, useNextFrame, yPos } from '../util';
8
- const [LEFT] = [0, 1, 2, 3];
9
- const PairedFeatures = observer(function ({ model, trackId, parentRef: ref, getTrackYPosOverride, }) {
6
+ import { LEFT, buildSimplePath, createMouseHandlers, getCanonicalRefs, getTestId, getYOffset, } from "./overlayUtils.js";
7
+ import { getMatchedPairedFeatures } from "./util.js";
8
+ import { getPxFromCoordinate, useNextFrame, yPos } from "../util.js";
9
+ const PairedFeatures = observer(function PairedFeatures({ model, trackId, parentRef, getTrackYPosOverride, }) {
10
10
  const { interactiveOverlay, views } = model;
11
11
  const session = getSession(model);
12
12
  const { assemblyManager } = session;
13
- const totalFeatures = model.getTrackFeatures(trackId);
14
- const layoutMatches = useMemo(() => model.getMatchedFeaturesInLayout(trackId, getMatchedPairedFeatures(totalFeatures)), [totalFeatures, trackId, model]);
15
- const [mouseoverElt, setMouseoverElt] = useState();
16
13
  const snap = getSnapshot(model);
14
+ const v0 = views[0];
15
+ const assembly = v0 ? assemblyManager.get(v0.assemblyNames[0]) : undefined;
17
16
  useNextFrame(snap);
18
- const assembly = assemblyManager.get(views[0].assemblyNames[0]);
17
+ const totalFeatures = model.getTrackFeatures(trackId);
18
+ const layoutMatches = useMemo(() => {
19
+ const matchedFeatures = getMatchedPairedFeatures(totalFeatures);
20
+ return model.getMatchedFeaturesInLayout(trackId, matchedFeatures);
21
+ }, [totalFeatures, trackId, model]);
22
+ const [mouseoverElt, setMouseoverElt] = useState();
23
+ const yOffset = getYOffset(parentRef);
24
+ const tracks = views.map(v => v.getTrack(trackId));
19
25
  if (!assembly) {
20
26
  return null;
21
27
  }
22
- let yoff = 0;
23
- if (ref.current) {
24
- const rect = ref.current.getBoundingClientRect();
25
- yoff = rect.top;
26
- }
27
- return (_jsx("g", { stroke: "green", strokeWidth: 5, fill: "none", "data-testid": layoutMatches.length ? `${trackId}-loaded` : trackId, children: layoutMatches.map(chunk => {
28
+ return (_jsx("g", { stroke: "green", strokeWidth: 5, fill: "none", "data-testid": getTestId(trackId, layoutMatches.length > 0), children: layoutMatches.map(chunk => {
28
29
  const ret = [];
29
30
  for (let i = 0; i < chunk.length - 1; i += 1) {
30
31
  const { layout: c1, feature: f1, level: level1 } = chunk[i];
31
32
  const { layout: c2, feature: f2, level: level2 } = chunk[i + 1];
32
33
  const id = f1.id();
33
- if (!c1 || !c2) {
34
- return null;
35
- }
36
- const f1origref = f1.get('refName');
37
- const f2origref = f2.get('refName');
38
- const f1ref = assembly.getCanonicalRefName(f1origref);
39
- const f2ref = assembly.getCanonicalRefName(f2origref);
40
- if (!f1ref || !f2ref) {
41
- throw new Error(`unable to find ref for ${f1ref || f2ref}`);
42
- }
34
+ const { f1ref, f2ref } = getCanonicalRefs(assembly, f1.get('refName'), f2.get('refName'));
43
35
  const x1 = getPxFromCoordinate(views[level1], f1ref, c1[LEFT]);
44
36
  const x2 = getPxFromCoordinate(views[level2], f2ref, c2[LEFT]);
45
- const tracks = views.map(v => v.getTrack(trackId));
46
37
  const y1 = yPos(trackId, level1, views, tracks, c1, getTrackYPosOverride) -
47
- yoff;
38
+ yOffset;
48
39
  const y2 = yPos(trackId, level2, views, tracks, c2, getTrackYPosOverride) -
49
- yoff;
50
- const path = [
51
- 'M',
52
- x1,
53
- y1,
54
- 'L',
55
- x2,
56
- y2,
57
- ].join(' ');
58
- ret.push(_jsx("path", { d: path, "data-testid": "r2", pointerEvents: interactiveOverlay ? 'auto' : undefined, strokeWidth: id === mouseoverElt ? 10 : 5, onClick: () => {
59
- var _a, _b, _c;
60
- const featureWidget = (_a = session.addWidget) === null || _a === void 0 ? void 0 : _a.call(session, 'VariantFeatureWidget', 'variantFeature', {
61
- featureData: (_b = totalFeatures.get(id)) === null || _b === void 0 ? void 0 : _b.toJSON(),
62
- });
63
- (_c = session.showWidget) === null || _c === void 0 ? void 0 : _c.call(session, featureWidget);
64
- }, onMouseOver: () => {
65
- setMouseoverElt(id);
66
- }, onMouseOut: () => {
67
- setMouseoverElt(undefined);
68
- } }, JSON.stringify(path)));
40
+ yOffset;
41
+ const path = buildSimplePath(x1, y1, x2, y2);
42
+ const mouseHandlers = createMouseHandlers(id, setMouseoverElt, session, 'VariantFeatureWidget', 'variantFeature', totalFeatures.get(id)?.toJSON());
43
+ ret.push(_jsx("path", { d: path, "data-testid": "r2", pointerEvents: interactiveOverlay ? 'auto' : undefined, strokeWidth: id === mouseoverElt ? 10 : 5, ...mouseHandlers }, JSON.stringify(path)));
69
44
  }
70
45
  return ret;
71
46
  }) }));
@@ -0,0 +1,6 @@
1
+ import type { BreakpointViewModel } from '../model.ts';
2
+ declare const Rubberband: ({ model, ControlComponent, }: {
3
+ model: BreakpointViewModel;
4
+ ControlComponent?: React.ReactElement;
5
+ }) => import("react/jsx-runtime").JSX.Element;
6
+ export default Rubberband;
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef } from 'react';
3
+ import { Menu } from '@jbrowse/core/ui';
4
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
5
+ import { observer } from 'mobx-react';
6
+ import RubberbandSpan from "./RubberbandSpan.js";
7
+ import VerticalGuide from "./VerticalGuide.js";
8
+ import { useRangeSelect } from "./useRangeSelect.js";
9
+ const useStyles = makeStyles()({
10
+ rubberbandControl: {
11
+ cursor: 'crosshair',
12
+ width: '100%',
13
+ minHeight: 8,
14
+ position: 'relative',
15
+ zIndex: 900,
16
+ },
17
+ });
18
+ const Rubberband = observer(function Rubberband({ model, ControlComponent = _jsx("div", {}), }) {
19
+ const ref = useRef(null);
20
+ const { classes } = useStyles();
21
+ const { guideX, rubberbandOn, leftBpOffset, rightBpOffset, numOfBpSelected, width, left, anchorPosition, open, handleMenuItemClick, handleClose, mouseMove, mouseDown, mouseOut, } = useRangeSelect(ref, model);
22
+ return (_jsxs(_Fragment, { children: [guideX !== undefined ? (_jsx(VerticalGuide, { model: model, coordX: guideX })) : rubberbandOn ? (_jsx(RubberbandSpan, { leftBpOffset: leftBpOffset, rightBpOffset: rightBpOffset, numOfBpSelected: numOfBpSelected, width: width, left: left })) : null, anchorPosition ? (_jsx(Menu, { anchorReference: "anchorPosition", anchorPosition: {
23
+ left: anchorPosition.clientX,
24
+ top: anchorPosition.clientY,
25
+ }, onMenuItemClick: handleMenuItemClick, open: open, onClose: handleClose, menuItems: model.rubberBandMenuItems() })) : null, _jsx("div", { "data-testid": "rubberband_controls", className: classes.rubberbandControl, ref: ref, onMouseDown: mouseDown, onMouseMove: mouseMove, onMouseOut: mouseOut, children: ControlComponent })] }));
26
+ });
27
+ export default Rubberband;
@@ -0,0 +1,15 @@
1
+ interface Offset {
2
+ coord: number;
3
+ refName?: string;
4
+ oob?: boolean;
5
+ }
6
+ export default function RubberbandSpan({ leftBpOffset, rightBpOffset, numOfBpSelected, left, width, top, sticky, }: {
7
+ leftBpOffset: Offset[];
8
+ rightBpOffset: Offset[];
9
+ numOfBpSelected?: number[];
10
+ left: number;
11
+ width: number;
12
+ top?: number;
13
+ sticky?: boolean;
14
+ }): import("react/jsx-runtime").JSX.Element;
15
+ export {};
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { getBpDisplayStr, stringify } from '@jbrowse/core/util';
4
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
5
+ import { Typography, alpha } from '@mui/material';
6
+ import RubberbandTooltip from "./RubberbandTooltip.js";
7
+ const useStyles = makeStyles()(theme => {
8
+ const { tertiary } = theme.palette;
9
+ const background = alpha(tertiary.light, 0.7);
10
+ return {
11
+ rubberband: {
12
+ height: '100%',
13
+ background,
14
+ position: 'absolute',
15
+ zIndex: 830,
16
+ textAlign: 'center',
17
+ },
18
+ rubberbandText: {
19
+ color: theme.palette.tertiary.contrastText,
20
+ },
21
+ };
22
+ });
23
+ export default function RubberbandSpan({ leftBpOffset, rightBpOffset, numOfBpSelected, left, width, top = 0, sticky = false, }) {
24
+ const { classes } = useStyles();
25
+ const [anchorEl, setAnchorEl] = useState(null);
26
+ return (_jsxs(_Fragment, { children: [anchorEl ? (_jsxs(_Fragment, { children: [_jsx(RubberbandTooltip, { side: "left", anchorEl: anchorEl, text: leftBpOffset.map((l, idx) => (_jsx("div", { children: stringify(l, true) }, `${JSON.stringify(l)}-left-${idx}`))) }), _jsx(RubberbandTooltip, { side: "right", anchorEl: anchorEl, text: rightBpOffset.map((l, idx) => (_jsx("div", { children: stringify(l, true) }, `${JSON.stringify(l)}-right-${idx}`))) })] })) : null, _jsx("div", { className: classes.rubberband, style: { left, width }, children: numOfBpSelected ? (_jsx(Typography, { ref: setAnchorEl, variant: "h6", className: classes.rubberbandText, style: {
27
+ top,
28
+ position: sticky ? 'sticky' : undefined,
29
+ }, children: numOfBpSelected.map((n, i) => (_jsx("div", { children: getBpDisplayStr(n) }, `bpSelectedRow-${i}`))) })) : null })] }));
30
+ }
@@ -0,0 +1,5 @@
1
+ export default function RubberbandTooltip({ anchorEl, side, text, }: {
2
+ anchorEl: HTMLSpanElement;
3
+ side: string;
4
+ text: React.ReactNode;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Tooltip } from '@mui/material';
3
+ export default function RubberbandTooltip({ anchorEl, side, text, }) {
4
+ return (_jsx(Tooltip, { title: text, open: true, placement: side === 'left' ? 'left-start' : 'right-start', slotProps: {
5
+ popper: {
6
+ anchorEl,
7
+ modifiers: [
8
+ {
9
+ name: 'offset',
10
+ options: {
11
+ offset: [-30, -10],
12
+ },
13
+ },
14
+ ],
15
+ },
16
+ }, children: _jsx("span", {}) }));
17
+ }
@@ -1,8 +1,3 @@
1
- import type { BreakpointViewModel } from '../model';
2
- declare const Translocations: ({ model, trackId, parentRef: ref, getTrackYPosOverride, }: {
3
- model: BreakpointViewModel;
4
- trackId: string;
5
- parentRef: React.RefObject<SVGSVGElement | null>;
6
- getTrackYPosOverride?: (trackId: string, level: number) => number;
7
- }) => import("react/jsx-runtime").JSX.Element | null;
1
+ import type { OverlayProps } from './overlayUtils.tsx';
2
+ declare const Translocations: ({ model, trackId, parentRef, getTrackYPosOverride, }: OverlayProps) => import("react/jsx-runtime").JSX.Element | null;
8
3
  export default Translocations;
@@ -1,57 +1,43 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useMemo, useState } from 'react';
3
3
  import { getSession } from '@jbrowse/core/util';
4
+ import { getSnapshot } from '@jbrowse/mobx-state-tree';
4
5
  import { observer } from 'mobx-react';
5
- import { getSnapshot } from 'mobx-state-tree';
6
- import { getMatchedTranslocationFeatures } from './util';
7
- import { getPxFromCoordinate, useNextFrame, yPos } from '../util';
8
- const [LEFT] = [0, 1, 2, 3];
9
- function str(s) {
10
- if (s === '+') {
11
- return 1;
12
- }
13
- else if (s === '-') {
14
- return -1;
15
- }
16
- else {
17
- return 0;
18
- }
19
- }
20
- const Translocations = observer(function ({ model, trackId, parentRef: ref, getTrackYPosOverride, }) {
6
+ import { LEFT, buildBreakpointPath, createMouseHandlers, getTestId, getYOffset, strandToSign, } from "./overlayUtils.js";
7
+ import { getMatchedTranslocationFeatures } from "./util.js";
8
+ import { getPxFromCoordinate, useNextFrame, yPos } from "../util.js";
9
+ const Translocations = observer(function Translocations({ model, trackId, parentRef, getTrackYPosOverride, }) {
21
10
  const { interactiveOverlay, views } = model;
22
11
  const session = getSession(model);
23
12
  const { assemblyManager } = session;
24
- const totalFeatures = model.getTrackFeatures(trackId);
25
- const layoutMatches = useMemo(() => model.getMatchedFeaturesInLayout(trackId, getMatchedTranslocationFeatures(totalFeatures)), [totalFeatures, trackId, model]);
26
- const [mouseoverElt, setMouseoverElt] = useState();
27
13
  const snap = getSnapshot(model);
14
+ const v0 = views[0];
15
+ const assembly = v0 ? assemblyManager.get(v0.assemblyNames[0]) : undefined;
28
16
  useNextFrame(snap);
29
- const assembly = assemblyManager.get(views[0].assemblyNames[0]);
17
+ const totalFeatures = model.getTrackFeatures(trackId);
18
+ const layoutMatches = useMemo(() => {
19
+ const matchedFeatures = getMatchedTranslocationFeatures(totalFeatures);
20
+ return model.getMatchedFeaturesInLayout(trackId, matchedFeatures);
21
+ }, [totalFeatures, trackId, model]);
22
+ const [mouseoverElt, setMouseoverElt] = useState();
23
+ const yOffset = getYOffset(parentRef);
24
+ const tracks = views.map(v => v.getTrack(trackId));
30
25
  if (!assembly) {
31
26
  return null;
32
27
  }
33
- let yOffset = 0;
34
- if (ref.current) {
35
- const rect = ref.current.getBoundingClientRect();
36
- yOffset = rect.top;
37
- }
38
28
  if (views.length < 2) {
39
29
  return null;
40
30
  }
41
- return (_jsx("g", { fill: "none", stroke: "green", strokeWidth: 5, "data-testid": layoutMatches.length ? `${trackId}-loaded` : trackId, children: layoutMatches.map(chunk => {
42
- var _a, _b;
31
+ return (_jsx("g", { fill: "none", stroke: "green", strokeWidth: 5, "data-testid": getTestId(trackId, layoutMatches.length > 0), children: layoutMatches.map(chunk => {
43
32
  const ret = [];
44
33
  for (const { layout: c1, feature: f1, level: level1 } of chunk) {
45
34
  const level2 = level1 === 0 ? 1 : 0;
46
35
  const id = f1.id();
47
- if (!c1) {
48
- return null;
49
- }
50
36
  const info = f1.get('INFO');
51
37
  const chr2 = info.CHR2[0];
52
38
  const end2 = info.END[0];
53
- const res = (_b = (_a = info.STRANDS) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.split('');
54
- const [myDirection, mateDirection] = res !== null && res !== void 0 ? res : ['.', '.'];
39
+ const res = info.STRANDS?.[0]?.split('');
40
+ const [myDirection, mateDirection] = res ?? ['.', '.'];
55
41
  const r = getPxFromCoordinate(views[level2], chr2, end2);
56
42
  if (r) {
57
43
  const c2 = [r, 0, r + 1, 0];
@@ -59,36 +45,15 @@ const Translocations = observer(function ({ model, trackId, parentRef: ref, getT
59
45
  const x2 = r;
60
46
  const reversed1 = views[level1].pxToBp(x1).reversed;
61
47
  const reversed2 = views[level2].pxToBp(x2).reversed;
62
- const tracks = views.map(v => v.getTrack(trackId));
63
48
  const y1 = yPos(trackId, level1, views, tracks, c1, getTrackYPosOverride) -
64
49
  yOffset;
65
50
  const y2 = yPos(trackId, level2, views, tracks, c2, getTrackYPosOverride) -
66
51
  yOffset;
67
- const path = [
68
- 'M',
69
- x1 - 20 * str(myDirection) * (reversed1 ? -1 : 1),
70
- y1,
71
- 'L',
72
- x1,
73
- y1,
74
- 'L',
75
- x2,
76
- y2,
77
- 'L',
78
- x2 - 20 * str(mateDirection) * (reversed2 ? -1 : 1),
79
- y2,
80
- ].join(' ');
81
- ret.push(_jsx("path", { d: path, pointerEvents: interactiveOverlay ? 'auto' : undefined, strokeWidth: id === mouseoverElt ? 10 : 5, onClick: () => {
82
- var _a, _b;
83
- const featureWidget = (_a = session.addWidget) === null || _a === void 0 ? void 0 : _a.call(session, 'VariantFeatureWidget', 'variantFeature', {
84
- featureData: (totalFeatures.get(id) || { toJSON: () => { } }).toJSON(),
85
- });
86
- (_b = session.showWidget) === null || _b === void 0 ? void 0 : _b.call(session, featureWidget);
87
- }, onMouseOver: () => {
88
- setMouseoverElt(id);
89
- }, onMouseOut: () => {
90
- setMouseoverElt(undefined);
91
- } }, JSON.stringify(path)));
52
+ const x1Tick = x1 - 20 * strandToSign(myDirection) * (reversed1 ? -1 : 1);
53
+ const x2Tick = x2 - 20 * strandToSign(mateDirection) * (reversed2 ? -1 : 1);
54
+ const path = buildBreakpointPath(x1, y1, x2, y2, x1Tick, x2Tick);
55
+ const mouseHandlers = createMouseHandlers(id, setMouseoverElt, session, 'VariantFeatureWidget', 'variantFeature', totalFeatures.get(id)?.toJSON());
56
+ ret.push(_jsx("path", { d: path, pointerEvents: interactiveOverlay ? 'auto' : undefined, strokeWidth: id === mouseoverElt ? 10 : 5, ...mouseHandlers }, JSON.stringify(path)));
92
57
  }
93
58
  }
94
59
  return ret;
@@ -0,0 +1,6 @@
1
+ import type { BreakpointViewModel } from '../model.ts';
2
+ declare const VerticalGuide: ({ model, coordX, }: {
3
+ model: BreakpointViewModel;
4
+ coordX: number;
5
+ }) => import("react/jsx-runtime").JSX.Element;
6
+ export default VerticalGuide;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { stringify } from '@jbrowse/core/util';
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
4
+ import { Tooltip } from '@mui/material';
5
+ import { observer } from 'mobx-react';
6
+ const useStyles = makeStyles()({
7
+ guide: {
8
+ pointerEvents: 'none',
9
+ height: '100%',
10
+ width: 1,
11
+ position: 'absolute',
12
+ background: 'red',
13
+ zIndex: 1001,
14
+ },
15
+ });
16
+ const VerticalGuide = observer(function VerticalGuide({ model, coordX, }) {
17
+ const { classes } = useStyles();
18
+ return (_jsx(Tooltip, { open: true, placement: "top", title: model.views
19
+ .map(view => view.pxToBp(coordX))
20
+ .map((elt, idx) => (_jsx("div", { children: stringify(elt, true) }, [JSON.stringify(elt), idx].join('-')))), arrow: true, children: _jsx("div", { className: classes.guide, style: {
21
+ left: coordX,
22
+ } }) }));
23
+ });
24
+ export default VerticalGuide;
@@ -0,0 +1,24 @@
1
+ import type { BreakpointViewModel } from '../model.ts';
2
+ import type { Assembly } from '@jbrowse/core/assemblyManager/assembly';
3
+ import type { getSession } from '@jbrowse/core/util';
4
+ export declare const LEFT: 0, RIGHT: 2;
5
+ export interface OverlayProps {
6
+ model: BreakpointViewModel;
7
+ trackId: string;
8
+ parentRef: React.RefObject<SVGSVGElement | null>;
9
+ getTrackYPosOverride?: (trackId: string, level: number) => number;
10
+ }
11
+ export declare function getYOffset(parentRef: React.RefObject<SVGSVGElement | null>): number;
12
+ export declare function createMouseHandlers(id: string, setMouseoverElt: (id: string | undefined) => void, session: ReturnType<typeof getSession>, widgetType: string, widgetId: string, featureData: unknown): {
13
+ onClick: () => void;
14
+ onMouseOver: () => void;
15
+ onMouseOut: () => void;
16
+ };
17
+ export declare function getTestId(trackId: string, hasMatches: boolean): string;
18
+ export declare function getCanonicalRefs(assembly: Assembly, f1RefName: string, f2RefName: string): {
19
+ f1ref: string;
20
+ f2ref: string;
21
+ };
22
+ export declare function strandToSign(s: string): 0 | 1 | -1;
23
+ export declare function buildSimplePath(x1: number, y1: number, x2: number, y2: number): string;
24
+ export declare function buildBreakpointPath(x1: number, y1: number, x2: number, y2: number, x1Tick: number, x2Tick: number): string;
@@ -0,0 +1,47 @@
1
+ export const [LEFT, , RIGHT] = [0, 1, 2, 3];
2
+ export function getYOffset(parentRef) {
3
+ return parentRef.current?.getBoundingClientRect().top ?? 0;
4
+ }
5
+ export function createMouseHandlers(id, setMouseoverElt, session, widgetType, widgetId, featureData) {
6
+ return {
7
+ onClick: () => {
8
+ const featureWidget = session.addWidget?.(widgetType, widgetId, {
9
+ featureData,
10
+ });
11
+ session.showWidget?.(featureWidget);
12
+ },
13
+ onMouseOver: () => {
14
+ setMouseoverElt(id);
15
+ },
16
+ onMouseOut: () => {
17
+ setMouseoverElt(undefined);
18
+ },
19
+ };
20
+ }
21
+ export function getTestId(trackId, hasMatches) {
22
+ return hasMatches ? `${trackId}-loaded` : trackId;
23
+ }
24
+ export function getCanonicalRefs(assembly, f1RefName, f2RefName) {
25
+ const f1ref = assembly.getCanonicalRefName(f1RefName);
26
+ const f2ref = assembly.getCanonicalRefName(f2RefName);
27
+ if (!f1ref || !f2ref) {
28
+ throw new Error(`unable to find ref for ${f1ref || f2ref}`);
29
+ }
30
+ return { f1ref, f2ref };
31
+ }
32
+ export function strandToSign(s) {
33
+ return s === '+' ? 1 : s === '-' ? -1 : 0;
34
+ }
35
+ const FLAT_ARC_HEIGHT = 30;
36
+ function arcOrLine(x1, y1, x2, y2) {
37
+ const midX = (x1 + x2) / 2;
38
+ return y1 === y2
39
+ ? `Q ${midX} ${y1 - FLAT_ARC_HEIGHT} ${x2} ${y2}`
40
+ : `L ${x2} ${y2}`;
41
+ }
42
+ export function buildSimplePath(x1, y1, x2, y2) {
43
+ return `M ${x1} ${y1} ${arcOrLine(x1, y1, x2, y2)}`;
44
+ }
45
+ export function buildBreakpointPath(x1, y1, x2, y2, x1Tick, x2Tick) {
46
+ return `M ${x1Tick} ${y1} L ${x1} ${y1} ${arcOrLine(x1, y1, x2, y2)} L ${x2Tick} ${y2}`;
47
+ }
@@ -0,0 +1,4 @@
1
+ export declare function getRelativeX(event: {
2
+ clientX: number;
3
+ target: EventTarget | null;
4
+ }, element: HTMLElement | null): number;
@@ -0,0 +1,3 @@
1
+ export function getRelativeX(event, element) {
2
+ return event.clientX - (element?.getBoundingClientRect().left || 0);
3
+ }
@@ -0,0 +1,59 @@
1
+ import type React from 'react';
2
+ import type { BreakpointViewModel } from '../model.ts';
3
+ interface AnchorPosition {
4
+ offsetX: number;
5
+ clientX: number;
6
+ clientY: number;
7
+ }
8
+ export declare function useRangeSelect(ref: React.RefObject<HTMLDivElement | null>, model: BreakpointViewModel): {
9
+ open: boolean;
10
+ guideX: number | undefined;
11
+ mouseDown: (event: React.MouseEvent<HTMLDivElement>) => void;
12
+ mouseMove: (event: React.MouseEvent<HTMLDivElement>) => void;
13
+ mouseOut: () => void;
14
+ handleMenuItemClick: (_: unknown, callback: () => void) => void;
15
+ rubberbandOn?: undefined;
16
+ handleClose?: undefined;
17
+ leftBpOffset?: undefined;
18
+ rightBpOffset?: undefined;
19
+ anchorPosition?: undefined;
20
+ numOfBpSelected?: undefined;
21
+ width?: undefined;
22
+ left?: undefined;
23
+ } | {
24
+ open: boolean;
25
+ rubberbandOn: boolean;
26
+ mouseDown: (event: React.MouseEvent<HTMLDivElement>) => void;
27
+ mouseMove: (event: React.MouseEvent<HTMLDivElement>) => void;
28
+ mouseOut: () => void;
29
+ handleClose: () => void;
30
+ handleMenuItemClick: (_: unknown, callback: () => void) => void;
31
+ leftBpOffset: {
32
+ coord: number;
33
+ index: number;
34
+ refName: string;
35
+ oob: boolean;
36
+ assemblyName: string;
37
+ offset: number;
38
+ start: number;
39
+ end: number;
40
+ reversed?: boolean;
41
+ }[];
42
+ rightBpOffset: {
43
+ coord: number;
44
+ index: number;
45
+ refName: string;
46
+ oob: boolean;
47
+ assemblyName: string;
48
+ offset: number;
49
+ start: number;
50
+ end: number;
51
+ reversed?: boolean;
52
+ }[];
53
+ anchorPosition: AnchorPosition | undefined;
54
+ numOfBpSelected: number[];
55
+ width: number;
56
+ left: number;
57
+ guideX?: undefined;
58
+ };
59
+ export {};