@jbrowse/plugin-arc 2.10.0 → 2.10.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.
@@ -1,14 +1,39 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const react_1 = __importDefault(require("react"));
29
+ const react_1 = __importStar(require("react"));
7
30
  const configuration_1 = require("@jbrowse/core/configuration");
8
31
  const util_1 = require("@jbrowse/core/util");
9
32
  const mobx_react_1 = require("mobx-react");
10
- const react_svg_tooltip_1 = require("react-svg-tooltip");
33
+ // locals
34
+ const ArcTooltip_1 = __importDefault(require("../ArcTooltip"));
11
35
  function Arc({ selectedFeatureId, region, bpPerPx, config, onFeatureClick, feature, }) {
36
+ const [isMouseOvered, setIsMouseOvered] = (0, react_1.useState)(false);
12
37
  const [left, right] = (0, util_1.bpSpanPx)(feature.get('start'), feature.get('end'), region, bpPerPx);
13
38
  const featureId = feature.id();
14
39
  let stroke = (0, configuration_1.readConfObject)(config, 'color', { feature });
@@ -21,7 +46,6 @@ function Arc({ selectedFeatureId, region, bpPerPx, config, onFeatureClick, featu
21
46
  const strokeWidth = (0, configuration_1.readConfObject)(config, 'thickness', { feature }) || 1;
22
47
  const height = (0, configuration_1.readConfObject)(config, 'height', { feature }) || 100;
23
48
  const ref = react_1.default.createRef();
24
- const tooltipWidth = 20 + (0, util_1.measureText)(caption === null || caption === void 0 ? void 0 : caption.toString());
25
49
  const t = 0.5;
26
50
  // formula: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves
27
51
  const textYCoord = (1 - t) * (1 - t) * (1 - t) * 0 +
@@ -29,10 +53,8 @@ function Arc({ selectedFeatureId, region, bpPerPx, config, onFeatureClick, featu
29
53
  3 * (1 - t) * (t * t) * height +
30
54
  t * t * t * 0;
31
55
  return (react_1.default.createElement("g", null,
32
- react_1.default.createElement("path", { d: `M ${left} 0 C ${left} ${height}, ${right} ${height}, ${right} 0`, stroke: stroke, strokeWidth: strokeWidth, fill: "transparent", onClick: e => onFeatureClick === null || onFeatureClick === void 0 ? void 0 : onFeatureClick(e, featureId), ref: ref, pointerEvents: "stroke" }),
33
- react_1.default.createElement(react_svg_tooltip_1.Tooltip, { triggerRef: ref },
34
- react_1.default.createElement("rect", { x: 12, y: 0, width: tooltipWidth, height: 20, rx: 5, ry: 5, fill: "black", fillOpacity: "50%" }),
35
- react_1.default.createElement("text", { x: 22, y: 14, fontSize: 10, fill: "white", textLength: tooltipWidth - 20 }, caption)),
56
+ react_1.default.createElement("path", { d: `M ${left} 0 C ${left} ${height}, ${right} ${height}, ${right} 0`, stroke: stroke, strokeWidth: strokeWidth, fill: "transparent", onClick: e => onFeatureClick === null || onFeatureClick === void 0 ? void 0 : onFeatureClick(e, featureId), onMouseOver: () => setIsMouseOvered(true), onMouseLeave: () => setIsMouseOvered(false), ref: ref, pointerEvents: "stroke" }),
57
+ isMouseOvered ? react_1.default.createElement(ArcTooltip_1.default, { contents: caption }) : null,
36
58
  react_1.default.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: 'white', strokeWidth: '0.6em' } }, label),
37
59
  react_1.default.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: textStroke } }, label)));
38
60
  }
@@ -62,6 +84,7 @@ function describeArc(x, y, radius, startAngle, endAngle) {
62
84
  ].join(' ');
63
85
  }
64
86
  function SemiCircles({ selectedFeatureId, region, bpPerPx, config, onFeatureClick, feature, }) {
87
+ const [isMouseOvered, setIsMouseOvered] = (0, react_1.useState)(false);
65
88
  const [left, right] = (0, util_1.bpSpanPx)(feature.get('start'), feature.get('end'), region, bpPerPx);
66
89
  const featureId = feature.id();
67
90
  let stroke = (0, configuration_1.readConfObject)(config, 'color', { feature });
@@ -73,13 +96,10 @@ function SemiCircles({ selectedFeatureId, region, bpPerPx, config, onFeatureClic
73
96
  const caption = (0, configuration_1.readConfObject)(config, 'caption', { feature });
74
97
  const strokeWidth = (0, configuration_1.readConfObject)(config, 'thickness', { feature }) || 1;
75
98
  const ref = react_1.default.createRef();
76
- const tooltipWidth = 20 + (0, util_1.measureText)(caption === null || caption === void 0 ? void 0 : caption.toString());
77
99
  const textYCoord = (right - left) / 2;
78
100
  return (react_1.default.createElement("g", null,
79
- react_1.default.createElement("path", { d: describeArc(left + (right - left) / 2, 0, (right - left) / 2, 90, 270), stroke: stroke, strokeWidth: strokeWidth, fill: "transparent", onClick: e => onFeatureClick === null || onFeatureClick === void 0 ? void 0 : onFeatureClick(e, featureId), ref: ref, pointerEvents: "stroke" }),
80
- react_1.default.createElement(react_svg_tooltip_1.Tooltip, { triggerRef: ref },
81
- react_1.default.createElement("rect", { x: 12, y: 0, width: tooltipWidth, height: 20, rx: 5, ry: 5, fill: "black", fillOpacity: "50%" }),
82
- react_1.default.createElement("text", { x: 22, y: 14, fontSize: 10, fill: "white", textLength: tooltipWidth - 20 }, caption)),
101
+ react_1.default.createElement("path", { d: describeArc(left + (right - left) / 2, 0, (right - left) / 2, 90, 270), stroke: stroke, strokeWidth: strokeWidth, fill: "transparent", onClick: e => onFeatureClick === null || onFeatureClick === void 0 ? void 0 : onFeatureClick(e, featureId), onMouseOver: () => setIsMouseOvered(true), onMouseLeave: () => setIsMouseOvered(false), ref: ref, pointerEvents: "stroke" }),
102
+ isMouseOvered ? react_1.default.createElement(ArcTooltip_1.default, { contents: caption }) : null,
83
103
  react_1.default.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: 'white', strokeWidth: '0.6em' } }, label),
84
104
  react_1.default.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: textStroke } }, label)));
85
105
  }
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ declare const ArcTooltip: ({ contents }: {
3
+ contents?: string | undefined;
4
+ }) => React.JSX.Element | null;
5
+ export default ArcTooltip;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const react_1 = __importDefault(require("react"));
7
+ const ui_1 = require("@jbrowse/core/ui");
8
+ const mobx_react_1 = require("mobx-react");
9
+ const material_1 = require("@mui/material");
10
+ const mui_1 = require("tss-react/mui");
11
+ const react_2 = require("@floating-ui/react");
12
+ function round(value) {
13
+ return Math.round(value * 1e5) / 1e5;
14
+ }
15
+ const useStyles = (0, mui_1.makeStyles)()(theme => ({
16
+ // these styles come from
17
+ // https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Tooltip/Tooltip.js
18
+ tooltip: {
19
+ pointerEvents: 'none',
20
+ backgroundColor: (0, material_1.alpha)(theme.palette.grey[700], 0.9),
21
+ borderRadius: theme.shape.borderRadius,
22
+ color: theme.palette.common.white,
23
+ fontFamily: theme.typography.fontFamily,
24
+ padding: '4px 8px',
25
+ fontSize: theme.typography.pxToRem(12),
26
+ lineHeight: `${round(14 / 10)}em`,
27
+ maxWidth: 300,
28
+ wordWrap: 'break-word',
29
+ },
30
+ }));
31
+ const TooltipContents = react_1.default.forwardRef(function TooltipContents2({ message }, ref) {
32
+ return (react_1.default.createElement("div", { ref: ref }, react_1.default.isValidElement(message) ? (message) : message ? (react_1.default.createElement(ui_1.SanitizedHTML, { html: String(message) })) : null));
33
+ });
34
+ const ArcTooltip = (0, mobx_react_1.observer)(function ({ contents }) {
35
+ var _a, _b;
36
+ const { theme, classes } = useStyles();
37
+ const { refs, floatingStyles, context } = (0, react_2.useFloating)({
38
+ placement: 'right',
39
+ });
40
+ const clientPoint = (0, react_2.useClientPoint)(context);
41
+ const { getFloatingProps } = (0, react_2.useInteractions)([clientPoint]);
42
+ const popperTheme = (_a = theme === null || theme === void 0 ? void 0 : theme.components) === null || _a === void 0 ? void 0 : _a.MuiPopper;
43
+ return contents ? (react_1.default.createElement(material_1.Portal, { container: (_b = popperTheme === null || popperTheme === void 0 ? void 0 : popperTheme.defaultProps) === null || _b === void 0 ? void 0 : _b.container },
44
+ react_1.default.createElement("div", { className: classes.tooltip, ref: refs.setFloating, style: {
45
+ ...floatingStyles,
46
+ zIndex: 100000,
47
+ pointerEvents: 'none',
48
+ }, ...getFloatingProps() },
49
+ react_1.default.createElement(TooltipContents, { message: contents })))) : null;
50
+ });
51
+ exports.default = ArcTooltip;
@@ -375,8 +375,8 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
375
375
  };
376
376
  }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, "displayId">>;
377
377
  }>> & import("mobx-state-tree/dist/internal").NonEmptyObject & import("mobx-state-tree")._NotCustomized, {
378
- type: string;
379
378
  id: string;
379
+ type: string;
380
380
  configuration: import("mobx-state-tree").ModelSnapshotType<Record<string, any>>;
381
381
  rpcDriverName: string | undefined;
382
382
  heightPreConfig: number | undefined;
@@ -22,79 +22,21 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
25
28
  Object.defineProperty(exports, "__esModule", { value: true });
26
29
  const react_1 = __importStar(require("react"));
27
30
  const mobx_react_1 = require("mobx-react");
28
31
  const util_1 = require("@jbrowse/core/util");
29
32
  const configuration_1 = require("@jbrowse/core/configuration");
30
- const vcf_1 = require("@gmod/vcf");
31
- const react_svg_tooltip_1 = require("react-svg-tooltip");
32
- function f(feature, alt) {
33
- var _a, _b, _c, _d, _e;
34
- const bnd = alt ? (0, vcf_1.parseBreakend)(alt) : undefined;
35
- let start = feature.get('start');
36
- let end = feature.get('end');
37
- const strand = feature.get('strand');
38
- const mate = feature.get('mate');
39
- const refName = feature.get('refName');
40
- let mateRefName;
41
- let mateEnd = 0;
42
- let mateStart = 0;
43
- // one sided bracket used, because there could be <INS:ME> and we just check
44
- // startswith below
45
- const symbolicAlleles = ['<TRA', '<DEL', '<INV', '<INS', '<DUP', '<CNV'];
46
- if (symbolicAlleles.some(a => alt === null || alt === void 0 ? void 0 : alt.startsWith(a))) {
47
- // END is defined to be a single value, not an array. CHR2 not defined in
48
- // VCF spec, but should be similar
49
- const e = ((_b = (_a = feature.get('INFO')) === null || _a === void 0 ? void 0 : _a.END) === null || _b === void 0 ? void 0 : _b[0]) || feature.get('end');
50
- mateEnd = e;
51
- mateStart = e - 1;
52
- mateRefName = (_e = (_d = (_c = feature.get('INFO')) === null || _c === void 0 ? void 0 : _c.CHR2) === null || _d === void 0 ? void 0 : _d[0]) !== null && _e !== void 0 ? _e : refName;
53
- // re-adjust the arc to be from start to end of feature by re-assigning end
54
- // to the 'mate'
55
- start = feature.get('start');
56
- end = feature.get('start') + 1;
57
- }
58
- else if (bnd === null || bnd === void 0 ? void 0 : bnd.MatePosition) {
59
- const matePosition = bnd.MatePosition.split(':');
60
- mateEnd = +matePosition[1];
61
- mateStart = +matePosition[1] - 1;
62
- mateRefName = matePosition[0];
63
- }
64
- return {
65
- k1: { refName, start, end, strand },
66
- k2: mate !== null && mate !== void 0 ? mate : { refName: mateRefName, end: mateEnd, start: mateStart },
67
- };
68
- }
69
- function makeSummary(feature, alt) {
70
- var _a;
71
- const { k1, k2 } = f(feature, alt);
72
- return [
73
- feature.get('name'),
74
- feature.get('id'),
75
- (0, util_1.assembleLocString)(k1),
76
- (0, util_1.assembleLocString)(k2),
77
- (_a = feature.get('INFO')) === null || _a === void 0 ? void 0 : _a.SVTYPE,
78
- alt,
79
- ]
80
- .filter(f => !!f)
81
- .join(' - ');
82
- }
83
- // conditionally rendered tooltip only on mouseover, speeds up
84
- const SvgTooltip = react_1.default.forwardRef(function SvgTooltip2({ feature, alt }, ref) {
85
- const caption = makeSummary(feature, alt);
86
- const tooltipWidth = 20 + (0, util_1.measureText)(caption);
87
- return ref !== null ? (
88
- // @ts-expect-error
89
- react_1.default.createElement(react_svg_tooltip_1.Tooltip, { triggerRef: ref },
90
- react_1.default.createElement("rect", { x: 12, y: 0, width: tooltipWidth, height: 20, rx: 5, ry: 5, fill: "black", fillOpacity: "50%" }),
91
- react_1.default.createElement("text", { x: 22, y: 14, fontSize: 10, fill: "white", textLength: tooltipWidth - 20 }, caption))) : null;
92
- });
33
+ const ArcTooltip_1 = __importDefault(require("../../ArcTooltip"));
34
+ const util_2 = require("./util");
93
35
  const Arc = (0, mobx_react_1.observer)(function ({ model, feature, alt, assembly, view, }) {
94
36
  var _a, _b;
95
37
  const [mouseOvered, setMouseOvered] = (0, react_1.useState)(false);
96
38
  const { height } = model;
97
- const { k1, k2 } = f(feature, alt);
39
+ const { k1, k2 } = (0, util_2.makeFeaturePair)(feature, alt);
98
40
  const ref = (0, react_1.useRef)(null);
99
41
  const c = (0, configuration_1.getConf)(model, 'color', { feature, alt });
100
42
  const ra1 = assembly.getCanonicalRefName(k1.refName) || k1.refName;
@@ -111,9 +53,13 @@ const Arc = (0, mobx_react_1.observer)(function ({ model, feature, alt, assembly
111
53
  const p2 = r2 - view.offsetPx;
112
54
  const left = p1;
113
55
  const right = p2;
56
+ const col = mouseOvered ? 'black' : c;
57
+ const sw = 3;
114
58
  return absrad > 1 ? (react_1.default.createElement(react_1.default.Fragment, null,
115
- react_1.default.createElement("path", { d: `M ${left} 0 C ${left} ${destY}, ${right} ${destY}, ${right} 0`, ref: ref, stroke: mouseOvered ? 'black' : c, strokeWidth: 3, onMouseOut: () => setMouseOvered(false), onMouseOver: () => setMouseOvered(true), onClick: () => model.selectFeature(feature), fill: "none", pointerEvents: "stroke" }),
116
- mouseOvered ? (react_1.default.createElement(SvgTooltip, { feature: feature, alt: alt, ref: ref })) : null)) : null;
59
+ react_1.default.createElement("path", { d: `M ${left} 0 C ${left} ${destY}, ${right} ${destY}, ${right} 0`, ref: ref, stroke: col, strokeWidth: sw, onMouseOut: () => setMouseOvered(false), onMouseOver: () => setMouseOvered(true), onClick: () => model.selectFeature(feature), fill: "none", pointerEvents: "stroke" }),
60
+ k1.mateDirection !== undefined ? (react_1.default.createElement("line", { stroke: col, strokeWidth: sw, onMouseOut: () => setMouseOvered(false), onMouseOver: () => setMouseOvered(true), onClick: () => model.selectFeature(feature), x1: left, x2: left + k1.mateDirection * 20, y1: 1.5, y2: 1.5 })) : null,
61
+ k2.mateDirection !== undefined ? (react_1.default.createElement("line", { stroke: col, strokeWidth: sw, onMouseOut: () => setMouseOvered(false), onMouseOver: () => setMouseOvered(true), onClick: () => model.selectFeature(feature), x1: right, x2: right + k2.mateDirection * 20, y1: 1.5, y2: 1.5 })) : null,
62
+ mouseOvered ? (react_1.default.createElement(ArcTooltip_1.default, { contents: (0, util_2.makeSummary)(feature, alt) })) : null)) : null;
117
63
  }
118
64
  return null;
119
65
  });
@@ -0,0 +1,17 @@
1
+ import { Feature } from '@jbrowse/core/util';
2
+ export declare function makeFeaturePair(feature: Feature, alt?: string): {
3
+ k1: {
4
+ refName: string;
5
+ start: number;
6
+ end: number;
7
+ strand: any;
8
+ mateDirection: number;
9
+ };
10
+ k2: {
11
+ refName: string;
12
+ start: number;
13
+ end: number;
14
+ mateDirection?: number | undefined;
15
+ };
16
+ };
17
+ export declare function makeSummary(feature: Feature, alt?: string): string;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeSummary = exports.makeFeaturePair = void 0;
4
+ const vcf_1 = require("@gmod/vcf");
5
+ const util_1 = require("@jbrowse/core/util");
6
+ function makeFeaturePair(feature, alt) {
7
+ var _a, _b, _c, _d;
8
+ const bnd = alt ? (0, vcf_1.parseBreakend)(alt) : undefined;
9
+ let start = feature.get('start');
10
+ let end = feature.get('end');
11
+ const strand = feature.get('strand');
12
+ const mate = feature.get('mate');
13
+ const refName = feature.get('refName');
14
+ let mateRefName;
15
+ let mateEnd = 0;
16
+ let mateStart = 0;
17
+ let joinDirection = 0;
18
+ let mateDirection = 0;
19
+ // one sided bracket used, because there could be <INS:ME> and we just check
20
+ // startswith below
21
+ const symbolicAlleles = ['<TRA', '<DEL', '<INV', '<INS', '<DUP', '<CNV'];
22
+ if (symbolicAlleles.some(a => alt === null || alt === void 0 ? void 0 : alt.startsWith(a))) {
23
+ // END is defined to be a single value, not an array. CHR2 not defined in
24
+ // VCF spec, but should be similar
25
+ const info = feature.get('INFO');
26
+ const e = (_b = (_a = info === null || info === void 0 ? void 0 : info.END) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : end;
27
+ mateRefName = (_d = (_c = info === null || info === void 0 ? void 0 : info.CHR2) === null || _c === void 0 ? void 0 : _c[0]) !== null && _d !== void 0 ? _d : refName;
28
+ mateEnd = e;
29
+ mateStart = e - 1;
30
+ // re-adjust the arc to be from start to end of feature by re-assigning end
31
+ // to the 'mate'
32
+ start = start;
33
+ end = start + 1;
34
+ }
35
+ else if (bnd === null || bnd === void 0 ? void 0 : bnd.MatePosition) {
36
+ const matePosition = bnd.MatePosition.split(':');
37
+ mateDirection = bnd.MateDirection === 'left' ? 1 : -1;
38
+ joinDirection = bnd.Join === 'left' ? -1 : 1;
39
+ mateEnd = +matePosition[1];
40
+ mateStart = +matePosition[1] - 1;
41
+ mateRefName = matePosition[0];
42
+ }
43
+ return {
44
+ k1: {
45
+ refName,
46
+ start,
47
+ end,
48
+ strand,
49
+ mateDirection,
50
+ },
51
+ k2: mate !== null && mate !== void 0 ? mate : {
52
+ refName: mateRefName,
53
+ end: mateEnd,
54
+ start: mateStart,
55
+ mateDirection: joinDirection,
56
+ },
57
+ };
58
+ }
59
+ exports.makeFeaturePair = makeFeaturePair;
60
+ function makeSummary(feature, alt) {
61
+ var _a;
62
+ const { k1, k2 } = makeFeaturePair(feature, alt);
63
+ return [
64
+ feature.get('name'),
65
+ feature.get('id'),
66
+ (0, util_1.assembleLocString)(k1),
67
+ (0, util_1.assembleLocString)(k2),
68
+ (_a = feature.get('INFO')) === null || _a === void 0 ? void 0 : _a.SVTYPE,
69
+ alt,
70
+ ]
71
+ .filter(f => !!f)
72
+ .join(' - ');
73
+ }
74
+ exports.makeSummary = makeSummary;
@@ -1,9 +1,11 @@
1
- import React from 'react';
1
+ import React, { useState } from 'react';
2
2
  import { readConfObject, } from '@jbrowse/core/configuration';
3
- import { bpSpanPx, measureText } from '@jbrowse/core/util';
3
+ import { bpSpanPx } from '@jbrowse/core/util';
4
4
  import { observer } from 'mobx-react';
5
- import { Tooltip } from 'react-svg-tooltip';
5
+ // locals
6
+ import ArcTooltip from '../ArcTooltip';
6
7
  function Arc({ selectedFeatureId, region, bpPerPx, config, onFeatureClick, feature, }) {
8
+ const [isMouseOvered, setIsMouseOvered] = useState(false);
7
9
  const [left, right] = bpSpanPx(feature.get('start'), feature.get('end'), region, bpPerPx);
8
10
  const featureId = feature.id();
9
11
  let stroke = readConfObject(config, 'color', { feature });
@@ -16,7 +18,6 @@ function Arc({ selectedFeatureId, region, bpPerPx, config, onFeatureClick, featu
16
18
  const strokeWidth = readConfObject(config, 'thickness', { feature }) || 1;
17
19
  const height = readConfObject(config, 'height', { feature }) || 100;
18
20
  const ref = React.createRef();
19
- const tooltipWidth = 20 + measureText(caption === null || caption === void 0 ? void 0 : caption.toString());
20
21
  const t = 0.5;
21
22
  // formula: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves
22
23
  const textYCoord = (1 - t) * (1 - t) * (1 - t) * 0 +
@@ -24,10 +25,8 @@ function Arc({ selectedFeatureId, region, bpPerPx, config, onFeatureClick, featu
24
25
  3 * (1 - t) * (t * t) * height +
25
26
  t * t * t * 0;
26
27
  return (React.createElement("g", null,
27
- React.createElement("path", { d: `M ${left} 0 C ${left} ${height}, ${right} ${height}, ${right} 0`, stroke: stroke, strokeWidth: strokeWidth, fill: "transparent", onClick: e => onFeatureClick === null || onFeatureClick === void 0 ? void 0 : onFeatureClick(e, featureId), ref: ref, pointerEvents: "stroke" }),
28
- React.createElement(Tooltip, { triggerRef: ref },
29
- React.createElement("rect", { x: 12, y: 0, width: tooltipWidth, height: 20, rx: 5, ry: 5, fill: "black", fillOpacity: "50%" }),
30
- React.createElement("text", { x: 22, y: 14, fontSize: 10, fill: "white", textLength: tooltipWidth - 20 }, caption)),
28
+ React.createElement("path", { d: `M ${left} 0 C ${left} ${height}, ${right} ${height}, ${right} 0`, stroke: stroke, strokeWidth: strokeWidth, fill: "transparent", onClick: e => onFeatureClick === null || onFeatureClick === void 0 ? void 0 : onFeatureClick(e, featureId), onMouseOver: () => setIsMouseOvered(true), onMouseLeave: () => setIsMouseOvered(false), ref: ref, pointerEvents: "stroke" }),
29
+ isMouseOvered ? React.createElement(ArcTooltip, { contents: caption }) : null,
31
30
  React.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: 'white', strokeWidth: '0.6em' } }, label),
32
31
  React.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: textStroke } }, label)));
33
32
  }
@@ -57,6 +56,7 @@ function describeArc(x, y, radius, startAngle, endAngle) {
57
56
  ].join(' ');
58
57
  }
59
58
  function SemiCircles({ selectedFeatureId, region, bpPerPx, config, onFeatureClick, feature, }) {
59
+ const [isMouseOvered, setIsMouseOvered] = useState(false);
60
60
  const [left, right] = bpSpanPx(feature.get('start'), feature.get('end'), region, bpPerPx);
61
61
  const featureId = feature.id();
62
62
  let stroke = readConfObject(config, 'color', { feature });
@@ -68,13 +68,10 @@ function SemiCircles({ selectedFeatureId, region, bpPerPx, config, onFeatureClic
68
68
  const caption = readConfObject(config, 'caption', { feature });
69
69
  const strokeWidth = readConfObject(config, 'thickness', { feature }) || 1;
70
70
  const ref = React.createRef();
71
- const tooltipWidth = 20 + measureText(caption === null || caption === void 0 ? void 0 : caption.toString());
72
71
  const textYCoord = (right - left) / 2;
73
72
  return (React.createElement("g", null,
74
- React.createElement("path", { d: describeArc(left + (right - left) / 2, 0, (right - left) / 2, 90, 270), stroke: stroke, strokeWidth: strokeWidth, fill: "transparent", onClick: e => onFeatureClick === null || onFeatureClick === void 0 ? void 0 : onFeatureClick(e, featureId), ref: ref, pointerEvents: "stroke" }),
75
- React.createElement(Tooltip, { triggerRef: ref },
76
- React.createElement("rect", { x: 12, y: 0, width: tooltipWidth, height: 20, rx: 5, ry: 5, fill: "black", fillOpacity: "50%" }),
77
- React.createElement("text", { x: 22, y: 14, fontSize: 10, fill: "white", textLength: tooltipWidth - 20 }, caption)),
73
+ React.createElement("path", { d: describeArc(left + (right - left) / 2, 0, (right - left) / 2, 90, 270), stroke: stroke, strokeWidth: strokeWidth, fill: "transparent", onClick: e => onFeatureClick === null || onFeatureClick === void 0 ? void 0 : onFeatureClick(e, featureId), onMouseOver: () => setIsMouseOvered(true), onMouseLeave: () => setIsMouseOvered(false), ref: ref, pointerEvents: "stroke" }),
74
+ isMouseOvered ? React.createElement(ArcTooltip, { contents: caption }) : null,
78
75
  React.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: 'white', strokeWidth: '0.6em' } }, label),
79
76
  React.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: textStroke } }, label)));
80
77
  }
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ declare const ArcTooltip: ({ contents }: {
3
+ contents?: string | undefined;
4
+ }) => React.JSX.Element | null;
5
+ export default ArcTooltip;
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { SanitizedHTML } from '@jbrowse/core/ui';
3
+ import { observer } from 'mobx-react';
4
+ import { Portal, alpha } from '@mui/material';
5
+ import { makeStyles } from 'tss-react/mui';
6
+ import { useClientPoint, useFloating, useInteractions, } from '@floating-ui/react';
7
+ function round(value) {
8
+ return Math.round(value * 1e5) / 1e5;
9
+ }
10
+ const useStyles = makeStyles()(theme => ({
11
+ // these styles come from
12
+ // https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Tooltip/Tooltip.js
13
+ tooltip: {
14
+ pointerEvents: 'none',
15
+ backgroundColor: alpha(theme.palette.grey[700], 0.9),
16
+ borderRadius: theme.shape.borderRadius,
17
+ color: theme.palette.common.white,
18
+ fontFamily: theme.typography.fontFamily,
19
+ padding: '4px 8px',
20
+ fontSize: theme.typography.pxToRem(12),
21
+ lineHeight: `${round(14 / 10)}em`,
22
+ maxWidth: 300,
23
+ wordWrap: 'break-word',
24
+ },
25
+ }));
26
+ const TooltipContents = React.forwardRef(function TooltipContents2({ message }, ref) {
27
+ return (React.createElement("div", { ref: ref }, React.isValidElement(message) ? (message) : message ? (React.createElement(SanitizedHTML, { html: String(message) })) : null));
28
+ });
29
+ const ArcTooltip = observer(function ({ contents }) {
30
+ var _a, _b;
31
+ const { theme, classes } = useStyles();
32
+ const { refs, floatingStyles, context } = useFloating({
33
+ placement: 'right',
34
+ });
35
+ const clientPoint = useClientPoint(context);
36
+ const { getFloatingProps } = useInteractions([clientPoint]);
37
+ const popperTheme = (_a = theme === null || theme === void 0 ? void 0 : theme.components) === null || _a === void 0 ? void 0 : _a.MuiPopper;
38
+ return contents ? (React.createElement(Portal, { container: (_b = popperTheme === null || popperTheme === void 0 ? void 0 : popperTheme.defaultProps) === null || _b === void 0 ? void 0 : _b.container },
39
+ React.createElement("div", { className: classes.tooltip, ref: refs.setFloating, style: {
40
+ ...floatingStyles,
41
+ zIndex: 100000,
42
+ pointerEvents: 'none',
43
+ }, ...getFloatingProps() },
44
+ React.createElement(TooltipContents, { message: contents })))) : null;
45
+ });
46
+ export default ArcTooltip;
@@ -375,8 +375,8 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
375
375
  };
376
376
  }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, "displayId">>;
377
377
  }>> & import("mobx-state-tree/dist/internal").NonEmptyObject & import("mobx-state-tree")._NotCustomized, {
378
- type: string;
379
378
  id: string;
379
+ type: string;
380
380
  configuration: import("mobx-state-tree").ModelSnapshotType<Record<string, any>>;
381
381
  rpcDriverName: string | undefined;
382
382
  heightPreConfig: number | undefined;
@@ -1,75 +1,14 @@
1
1
  import React, { useRef, useState } from 'react';
2
2
  import { observer } from 'mobx-react';
3
- import { assembleLocString, getContainingView, getSession, measureText, } from '@jbrowse/core/util';
3
+ import { getContainingView, getSession, } from '@jbrowse/core/util';
4
4
  import { getConf } from '@jbrowse/core/configuration';
5
- import { parseBreakend } from '@gmod/vcf';
6
- import { Tooltip } from 'react-svg-tooltip';
7
- function f(feature, alt) {
8
- var _a, _b, _c, _d, _e;
9
- const bnd = alt ? parseBreakend(alt) : undefined;
10
- let start = feature.get('start');
11
- let end = feature.get('end');
12
- const strand = feature.get('strand');
13
- const mate = feature.get('mate');
14
- const refName = feature.get('refName');
15
- let mateRefName;
16
- let mateEnd = 0;
17
- let mateStart = 0;
18
- // one sided bracket used, because there could be <INS:ME> and we just check
19
- // startswith below
20
- const symbolicAlleles = ['<TRA', '<DEL', '<INV', '<INS', '<DUP', '<CNV'];
21
- if (symbolicAlleles.some(a => alt === null || alt === void 0 ? void 0 : alt.startsWith(a))) {
22
- // END is defined to be a single value, not an array. CHR2 not defined in
23
- // VCF spec, but should be similar
24
- const e = ((_b = (_a = feature.get('INFO')) === null || _a === void 0 ? void 0 : _a.END) === null || _b === void 0 ? void 0 : _b[0]) || feature.get('end');
25
- mateEnd = e;
26
- mateStart = e - 1;
27
- mateRefName = (_e = (_d = (_c = feature.get('INFO')) === null || _c === void 0 ? void 0 : _c.CHR2) === null || _d === void 0 ? void 0 : _d[0]) !== null && _e !== void 0 ? _e : refName;
28
- // re-adjust the arc to be from start to end of feature by re-assigning end
29
- // to the 'mate'
30
- start = feature.get('start');
31
- end = feature.get('start') + 1;
32
- }
33
- else if (bnd === null || bnd === void 0 ? void 0 : bnd.MatePosition) {
34
- const matePosition = bnd.MatePosition.split(':');
35
- mateEnd = +matePosition[1];
36
- mateStart = +matePosition[1] - 1;
37
- mateRefName = matePosition[0];
38
- }
39
- return {
40
- k1: { refName, start, end, strand },
41
- k2: mate !== null && mate !== void 0 ? mate : { refName: mateRefName, end: mateEnd, start: mateStart },
42
- };
43
- }
44
- function makeSummary(feature, alt) {
45
- var _a;
46
- const { k1, k2 } = f(feature, alt);
47
- return [
48
- feature.get('name'),
49
- feature.get('id'),
50
- assembleLocString(k1),
51
- assembleLocString(k2),
52
- (_a = feature.get('INFO')) === null || _a === void 0 ? void 0 : _a.SVTYPE,
53
- alt,
54
- ]
55
- .filter(f => !!f)
56
- .join(' - ');
57
- }
58
- // conditionally rendered tooltip only on mouseover, speeds up
59
- const SvgTooltip = React.forwardRef(function SvgTooltip2({ feature, alt }, ref) {
60
- const caption = makeSummary(feature, alt);
61
- const tooltipWidth = 20 + measureText(caption);
62
- return ref !== null ? (
63
- // @ts-expect-error
64
- React.createElement(Tooltip, { triggerRef: ref },
65
- React.createElement("rect", { x: 12, y: 0, width: tooltipWidth, height: 20, rx: 5, ry: 5, fill: "black", fillOpacity: "50%" }),
66
- React.createElement("text", { x: 22, y: 14, fontSize: 10, fill: "white", textLength: tooltipWidth - 20 }, caption))) : null;
67
- });
5
+ import ArcTooltip from '../../ArcTooltip';
6
+ import { makeFeaturePair, makeSummary } from './util';
68
7
  const Arc = observer(function ({ model, feature, alt, assembly, view, }) {
69
8
  var _a, _b;
70
9
  const [mouseOvered, setMouseOvered] = useState(false);
71
10
  const { height } = model;
72
- const { k1, k2 } = f(feature, alt);
11
+ const { k1, k2 } = makeFeaturePair(feature, alt);
73
12
  const ref = useRef(null);
74
13
  const c = getConf(model, 'color', { feature, alt });
75
14
  const ra1 = assembly.getCanonicalRefName(k1.refName) || k1.refName;
@@ -86,9 +25,13 @@ const Arc = observer(function ({ model, feature, alt, assembly, view, }) {
86
25
  const p2 = r2 - view.offsetPx;
87
26
  const left = p1;
88
27
  const right = p2;
28
+ const col = mouseOvered ? 'black' : c;
29
+ const sw = 3;
89
30
  return absrad > 1 ? (React.createElement(React.Fragment, null,
90
- React.createElement("path", { d: `M ${left} 0 C ${left} ${destY}, ${right} ${destY}, ${right} 0`, ref: ref, stroke: mouseOvered ? 'black' : c, strokeWidth: 3, onMouseOut: () => setMouseOvered(false), onMouseOver: () => setMouseOvered(true), onClick: () => model.selectFeature(feature), fill: "none", pointerEvents: "stroke" }),
91
- mouseOvered ? (React.createElement(SvgTooltip, { feature: feature, alt: alt, ref: ref })) : null)) : null;
31
+ React.createElement("path", { d: `M ${left} 0 C ${left} ${destY}, ${right} ${destY}, ${right} 0`, ref: ref, stroke: col, strokeWidth: sw, onMouseOut: () => setMouseOvered(false), onMouseOver: () => setMouseOvered(true), onClick: () => model.selectFeature(feature), fill: "none", pointerEvents: "stroke" }),
32
+ k1.mateDirection !== undefined ? (React.createElement("line", { stroke: col, strokeWidth: sw, onMouseOut: () => setMouseOvered(false), onMouseOver: () => setMouseOvered(true), onClick: () => model.selectFeature(feature), x1: left, x2: left + k1.mateDirection * 20, y1: 1.5, y2: 1.5 })) : null,
33
+ k2.mateDirection !== undefined ? (React.createElement("line", { stroke: col, strokeWidth: sw, onMouseOut: () => setMouseOvered(false), onMouseOver: () => setMouseOvered(true), onClick: () => model.selectFeature(feature), x1: right, x2: right + k2.mateDirection * 20, y1: 1.5, y2: 1.5 })) : null,
34
+ mouseOvered ? (React.createElement(ArcTooltip, { contents: makeSummary(feature, alt) })) : null)) : null;
92
35
  }
93
36
  return null;
94
37
  });
@@ -0,0 +1,17 @@
1
+ import { Feature } from '@jbrowse/core/util';
2
+ export declare function makeFeaturePair(feature: Feature, alt?: string): {
3
+ k1: {
4
+ refName: string;
5
+ start: number;
6
+ end: number;
7
+ strand: any;
8
+ mateDirection: number;
9
+ };
10
+ k2: {
11
+ refName: string;
12
+ start: number;
13
+ end: number;
14
+ mateDirection?: number | undefined;
15
+ };
16
+ };
17
+ export declare function makeSummary(feature: Feature, alt?: string): string;
@@ -0,0 +1,69 @@
1
+ import { parseBreakend } from '@gmod/vcf';
2
+ import { assembleLocString } from '@jbrowse/core/util';
3
+ export function makeFeaturePair(feature, alt) {
4
+ var _a, _b, _c, _d;
5
+ const bnd = alt ? parseBreakend(alt) : undefined;
6
+ let start = feature.get('start');
7
+ let end = feature.get('end');
8
+ const strand = feature.get('strand');
9
+ const mate = feature.get('mate');
10
+ const refName = feature.get('refName');
11
+ let mateRefName;
12
+ let mateEnd = 0;
13
+ let mateStart = 0;
14
+ let joinDirection = 0;
15
+ let mateDirection = 0;
16
+ // one sided bracket used, because there could be <INS:ME> and we just check
17
+ // startswith below
18
+ const symbolicAlleles = ['<TRA', '<DEL', '<INV', '<INS', '<DUP', '<CNV'];
19
+ if (symbolicAlleles.some(a => alt === null || alt === void 0 ? void 0 : alt.startsWith(a))) {
20
+ // END is defined to be a single value, not an array. CHR2 not defined in
21
+ // VCF spec, but should be similar
22
+ const info = feature.get('INFO');
23
+ const e = (_b = (_a = info === null || info === void 0 ? void 0 : info.END) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : end;
24
+ mateRefName = (_d = (_c = info === null || info === void 0 ? void 0 : info.CHR2) === null || _c === void 0 ? void 0 : _c[0]) !== null && _d !== void 0 ? _d : refName;
25
+ mateEnd = e;
26
+ mateStart = e - 1;
27
+ // re-adjust the arc to be from start to end of feature by re-assigning end
28
+ // to the 'mate'
29
+ start = start;
30
+ end = start + 1;
31
+ }
32
+ else if (bnd === null || bnd === void 0 ? void 0 : bnd.MatePosition) {
33
+ const matePosition = bnd.MatePosition.split(':');
34
+ mateDirection = bnd.MateDirection === 'left' ? 1 : -1;
35
+ joinDirection = bnd.Join === 'left' ? -1 : 1;
36
+ mateEnd = +matePosition[1];
37
+ mateStart = +matePosition[1] - 1;
38
+ mateRefName = matePosition[0];
39
+ }
40
+ return {
41
+ k1: {
42
+ refName,
43
+ start,
44
+ end,
45
+ strand,
46
+ mateDirection,
47
+ },
48
+ k2: mate !== null && mate !== void 0 ? mate : {
49
+ refName: mateRefName,
50
+ end: mateEnd,
51
+ start: mateStart,
52
+ mateDirection: joinDirection,
53
+ },
54
+ };
55
+ }
56
+ export function makeSummary(feature, alt) {
57
+ var _a;
58
+ const { k1, k2 } = makeFeaturePair(feature, alt);
59
+ return [
60
+ feature.get('name'),
61
+ feature.get('id'),
62
+ assembleLocString(k1),
63
+ assembleLocString(k2),
64
+ (_a = feature.get('INFO')) === null || _a === void 0 ? void 0 : _a.SVTYPE,
65
+ alt,
66
+ ]
67
+ .filter(f => !!f)
68
+ .join(' - ');
69
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-arc",
3
- "version": "2.10.0",
3
+ "version": "2.10.1",
4
4
  "description": "JBrowse 2 arc adapters, tracks, etc.",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -35,9 +35,6 @@
35
35
  "build:commonjs": "tsc --build tsconfig.build.commonjs.json",
36
36
  "clean": "rimraf dist esm *.tsbuildinfo"
37
37
  },
38
- "dependencies": {
39
- "react-svg-tooltip": "^0.0.11"
40
- },
41
38
  "peerDependencies": {
42
39
  "@jbrowse/core": "^2.0.0",
43
40
  "@jbrowse/plugin-linear-genome-view": "^2.0.0",
@@ -56,5 +53,5 @@
56
53
  "publishConfig": {
57
54
  "access": "public"
58
55
  },
59
- "gitHead": "223d8bfb68fd1bacaf22852639ad5920f1b7f43b"
56
+ "gitHead": "442b5f87efddfdf4ccf520b4d9dd01ddd370cb07"
60
57
  }