@jbrowse/plugin-arc 2.7.0 → 2.7.2

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,3 +1,15 @@
1
1
  import React from 'react';
2
- declare const ArcRendering: (props: any) => React.JSX.Element;
2
+ import { AnyConfigurationModel } from '@jbrowse/core/configuration';
3
+ import { Feature, Region } from '@jbrowse/core/util';
4
+ declare const ArcRendering: ({ features, config, regions, bpPerPx, height, exportSVG, displayModel: { selectedFeatureId }, }: {
5
+ features: Map<string, Feature>;
6
+ config: AnyConfigurationModel;
7
+ regions: Region[];
8
+ bpPerPx: number;
9
+ height: number;
10
+ displayModel: {
11
+ selectedFeatureId: string;
12
+ };
13
+ exportSVG: boolean;
14
+ }) => React.JSX.Element;
3
15
  export default ArcRendering;
@@ -8,54 +8,88 @@ const configuration_1 = require("@jbrowse/core/configuration");
8
8
  const util_1 = require("@jbrowse/core/util");
9
9
  const mobx_react_1 = require("mobx-react");
10
10
  const react_svg_tooltip_1 = require("react-svg-tooltip");
11
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
- const ArcRendering = (0, mobx_react_1.observer)(function (props) {
13
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
- const onClick = (event, id) => {
15
- const { onFeatureClick: handler } = props;
16
- if (!handler) {
17
- return undefined;
18
- }
19
- return handler(event, id);
11
+ function Arc({ selectedFeatureId, region, bpPerPx, config, onFeatureClick, feature, }) {
12
+ const [left, right] = (0, util_1.bpSpanPx)(feature.get('start'), feature.get('end'), region, bpPerPx);
13
+ const featureId = feature.id();
14
+ let stroke = (0, configuration_1.readConfObject)(config, 'color', { feature });
15
+ let textStroke = 'black';
16
+ if (selectedFeatureId && String(selectedFeatureId) === String(feature.id())) {
17
+ stroke = textStroke = 'red';
18
+ }
19
+ const label = (0, configuration_1.readConfObject)(config, 'label', { feature });
20
+ const caption = (0, configuration_1.readConfObject)(config, 'caption', { feature });
21
+ const strokeWidth = (0, configuration_1.readConfObject)(config, 'thickness', { feature }) || 1;
22
+ const height = (0, configuration_1.readConfObject)(config, 'height', { feature }) || 100;
23
+ const ref = react_1.default.createRef();
24
+ const tooltipWidth = 20 + (0, util_1.measureText)(caption === null || caption === void 0 ? void 0 : caption.toString());
25
+ const t = 0.5;
26
+ // formula: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves
27
+ const textYCoord = (1 - t) * (1 - t) * (1 - t) * 0 +
28
+ 3 * ((1 - t) * (1 - t)) * (t * height) +
29
+ 3 * (1 - t) * (t * t) * height +
30
+ t * t * t * 0;
31
+ 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)),
36
+ react_1.default.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: 'white', strokeWidth: '0.6em' } }, label),
37
+ react_1.default.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: textStroke } }, label)));
38
+ }
39
+ function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
40
+ const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180;
41
+ return {
42
+ x: centerX + radius * Math.cos(angleInRadians),
43
+ y: centerY + radius * Math.sin(angleInRadians),
20
44
  };
21
- const { features, config, regions, blockKey, bpPerPx, displayModel: { selectedFeatureId }, } = props;
22
- const [region] = regions;
23
- const arcsRendered = [];
24
- for (const feature of features.values()) {
25
- const [left, right] = (0, util_1.bpSpanPx)(feature.get('start'), feature.get('end'), region, bpPerPx);
26
- const featureId = feature.id();
27
- const id = blockKey + '-' + featureId;
28
- let stroke = (0, configuration_1.readConfObject)(config, 'color', { feature });
29
- let textStroke = 'black';
30
- if (selectedFeatureId &&
31
- String(selectedFeatureId) === String(feature.id())) {
32
- stroke = textStroke = 'red';
33
- }
34
- const label = (0, configuration_1.readConfObject)(config, 'label', { feature });
35
- const caption = (0, configuration_1.readConfObject)(config, 'caption', { feature });
36
- const strokeWidth = (0, configuration_1.readConfObject)(config, 'thickness', { feature }) || 1;
37
- const height = (0, configuration_1.readConfObject)(config, 'height', { feature }) || 100;
38
- const ref = react_1.default.createRef();
39
- const tooltipWidth = 20 + (0, util_1.measureText)(caption === null || caption === void 0 ? void 0 : caption.toString());
40
- const t = 0.5;
41
- // formula: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves
42
- const textYCoord = (1 - t) * (1 - t) * (1 - t) * 0 +
43
- 3 * ((1 - t) * (1 - t)) * (t * height) +
44
- 3 * (1 - t) * (t * t) * height +
45
- t * t * t * 0;
46
- arcsRendered.push(react_1.default.createElement("g", { key: id, onClick: e => onClick(e, featureId) },
47
- react_1.default.createElement("path", { id: id, d: `M ${left} 0 C ${left} ${height}, ${right} ${height}, ${right} 0`, stroke: stroke, strokeWidth: strokeWidth, fill: "transparent", onClick: e => onClick(e, featureId), ref: ref, pointerEvents: "stroke" }),
48
- react_1.default.createElement(react_svg_tooltip_1.Tooltip, { triggerRef: ref },
49
- react_1.default.createElement("rect", { x: 12, y: 0, width: tooltipWidth, height: 20, rx: 5, ry: 5, fill: "black", fillOpacity: "50%" }),
50
- react_1.default.createElement("text", { x: 22, y: 14, fontSize: 10, fill: "white", textLength: tooltipWidth - 20 }, caption)),
51
- react_1.default.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: 'white', strokeWidth: '0.6em' } }, label),
52
- react_1.default.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: textStroke } }, label)));
45
+ }
46
+ function describeArc(x, y, radius, startAngle, endAngle) {
47
+ const start = polarToCartesian(x, y, radius, endAngle);
48
+ const end = polarToCartesian(x, y, radius, startAngle);
49
+ const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
50
+ return [
51
+ 'M',
52
+ start.x,
53
+ start.y,
54
+ 'A',
55
+ radius,
56
+ radius,
57
+ 0,
58
+ largeArcFlag,
59
+ 0,
60
+ end.x,
61
+ end.y,
62
+ ].join(' ');
63
+ }
64
+ function SemiCircles({ selectedFeatureId, region, bpPerPx, config, onFeatureClick, feature, }) {
65
+ const [left, right] = (0, util_1.bpSpanPx)(feature.get('start'), feature.get('end'), region, bpPerPx);
66
+ const featureId = feature.id();
67
+ let stroke = (0, configuration_1.readConfObject)(config, 'color', { feature });
68
+ let textStroke = 'black';
69
+ if (selectedFeatureId && String(selectedFeatureId) === String(feature.id())) {
70
+ stroke = textStroke = 'red';
53
71
  }
72
+ const label = (0, configuration_1.readConfObject)(config, 'label', { feature });
73
+ const caption = (0, configuration_1.readConfObject)(config, 'caption', { feature });
74
+ const strokeWidth = (0, configuration_1.readConfObject)(config, 'thickness', { feature }) || 1;
75
+ const ref = react_1.default.createRef();
76
+ const tooltipWidth = 20 + (0, util_1.measureText)(caption === null || caption === void 0 ? void 0 : caption.toString());
77
+ const textYCoord = (right - left) / 2;
78
+ 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)),
83
+ react_1.default.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: 'white', strokeWidth: '0.6em' } }, label),
84
+ react_1.default.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: textStroke } }, label)));
85
+ }
86
+ const ArcRendering = (0, mobx_react_1.observer)(function ({ features, config, regions, bpPerPx, height, exportSVG, displayModel: { selectedFeatureId }, }) {
87
+ const [region] = regions;
54
88
  const width = (region.end - region.start) / bpPerPx;
55
- const height = 500;
56
- return (react_1.default.createElement("svg", { className: "ArcRendering", width: width, height: height, style: {
57
- outline: 'none',
58
- position: 'relative',
59
- } }, arcsRendered));
89
+ const semicircles = (0, configuration_1.readConfObject)(config, 'displayMode') === 'semicircles';
90
+ return (react_1.default.createElement(Wrapper, { exportSVG: exportSVG, width: width, height: height }, [...features.values()].map(f => semicircles ? (react_1.default.createElement(SemiCircles, { key: f.id(), config: config, region: region, bpPerPx: bpPerPx, selectedFeatureId: selectedFeatureId, feature: f })) : (react_1.default.createElement(Arc, { key: f.id(), config: config, region: region, bpPerPx: bpPerPx, selectedFeatureId: selectedFeatureId, feature: f })))));
60
91
  });
92
+ function Wrapper({ exportSVG, width, height, children, }) {
93
+ return exportSVG ? (react_1.default.createElement(react_1.default.Fragment, null, children)) : (react_1.default.createElement("svg", { width: width, height: height }, children));
94
+ }
61
95
  exports.default = ArcRendering;
@@ -44,5 +44,14 @@ declare const ArcRenderer: import("@jbrowse/core/configuration/configurationSche
44
44
  defaultValue: string;
45
45
  contextVariable: string[];
46
46
  };
47
+ /**
48
+ * #slot
49
+ */
50
+ displayMode: {
51
+ type: string;
52
+ defaultValue: string;
53
+ model: import("mobx-state-tree").ISimpleType<string>;
54
+ description: string;
55
+ };
47
56
  }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
48
57
  export default ArcRenderer;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const configuration_1 = require("@jbrowse/core/configuration");
4
+ const mobx_state_tree_1 = require("mobx-state-tree");
4
5
  /**
5
6
  * #config ArcRenderer
6
7
  */
@@ -51,5 +52,14 @@ const ArcRenderer = (0, configuration_1.ConfigurationSchema)('ArcRenderer', {
51
52
  defaultValue: `jexl:get(feature,'name')`,
52
53
  contextVariable: ['feature'],
53
54
  },
55
+ /**
56
+ * #slot
57
+ */
58
+ displayMode: {
59
+ type: 'enum',
60
+ defaultValue: 'arcs',
61
+ model: mobx_state_tree_1.types.enumeration('DisplayMode', ['arcs', 'semicircles']),
62
+ description: 'render semi-circles instead of arcs',
63
+ },
54
64
  }, { explicitlyTyped: true });
55
65
  exports.default = ArcRenderer;
@@ -1,5 +1,9 @@
1
1
  /// <reference types="react" />
2
2
  import { AnyConfigurationSchemaType } from '@jbrowse/core/configuration';
3
+ /**
4
+ * #stateModel LinearArcDisplay
5
+ * extends BaseLinearDisplay
6
+ */
3
7
  export declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): import("mobx-state-tree").IModelType<{
4
8
  id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
5
9
  type: import("mobx-state-tree").ISimpleType<string>;
@@ -59,7 +63,9 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
59
63
  configuration: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
60
64
  maxFeatureScreenDensity: {
61
65
  type: string;
62
- description: string;
66
+ description: string; /**
67
+ * #property
68
+ */
63
69
  defaultValue: number;
64
70
  };
65
71
  fetchSizeLimit: {
@@ -73,21 +79,36 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
73
79
  description: string;
74
80
  };
75
81
  mouseover: {
76
- type: string;
82
+ type: string; /**
83
+ * #getter
84
+ */
77
85
  description: string;
78
86
  defaultValue: string;
79
87
  contextVariable: string[];
80
88
  };
81
89
  }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, "displayId">>;
82
90
  } & {
91
+ /**
92
+ * #property
93
+ */
83
94
  type: import("mobx-state-tree").ISimpleType<"LinearArcDisplay">;
95
+ /**
96
+ * #property
97
+ */
84
98
  configuration: AnyConfigurationSchemaType;
99
+ /**
100
+ * #property
101
+ */
102
+ displayMode: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
85
103
  }, {
86
104
  rendererTypeName: string;
87
105
  error: unknown;
88
106
  message: string | undefined;
89
107
  } & {
90
108
  readonly RenderingComponent: import("react").FC<{
109
+ /**
110
+ * #property
111
+ */
91
112
  model: {
92
113
  id: string;
93
114
  type: string;
@@ -143,7 +164,9 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
143
164
  } & {
144
165
  scrollTop: number;
145
166
  } & {
146
- readonly height: number;
167
+ readonly height: number; /**
168
+ * #property
169
+ */
147
170
  } & {
148
171
  setScrollTop(scrollTop: number): void;
149
172
  setHeight(displayHeight: number): number;
@@ -157,7 +180,9 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
157
180
  readonly currentFeatureScreenDensity: number;
158
181
  readonly maxFeatureScreenDensity: any;
159
182
  readonly featureDensityStatsReady: boolean;
160
- readonly maxAllowableBytes: number;
183
+ readonly maxAllowableBytes: number; /**
184
+ * #method
185
+ */
161
186
  } & {
162
187
  afterAttach(): void;
163
188
  } & {
@@ -207,10 +232,55 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
207
232
  renderSvg(opts: import("@jbrowse/plugin-linear-genome-view").ExportSvgDisplayOptions): Promise<import("react").JSX.Element>;
208
233
  afterAttach(): void;
209
234
  } & {
235
+ /**
236
+ * #getter
237
+ */
210
238
  readonly blockType: string;
239
+ /**
240
+ * #getter
241
+ */
211
242
  readonly renderDelay: number;
212
- renderProps(): any;
243
+ /**
244
+ * #getter
245
+ */
213
246
  readonly rendererTypeName: any;
247
+ } & {
248
+ /**
249
+ * #getter
250
+ */
251
+ readonly displayModeSetting: any;
252
+ } & {
253
+ /**
254
+ * #getter
255
+ */
256
+ readonly rendererConfig: {
257
+ [x: string]: any;
258
+ } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
259
+ setSubschema(slotName: string, data: unknown): any;
260
+ } & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>;
261
+ } & {
262
+ /**
263
+ * #method
264
+ */
265
+ renderProps(): any;
266
+ } & {
267
+ /**
268
+ * #action
269
+ */
270
+ setDisplayMode(flag: string): void;
271
+ } & {
272
+ /**
273
+ * #method
274
+ */
275
+ trackMenuItems(): (import("@jbrowse/core/ui").MenuDivider | import("@jbrowse/core/ui").MenuSubHeader | import("@jbrowse/core/ui").NormalMenuItem | import("@jbrowse/core/ui").CheckboxMenuItem | import("@jbrowse/core/ui").RadioMenuItem | import("@jbrowse/core/ui").SubMenuItem | {
276
+ label: string;
277
+ subMenu: {
278
+ type: string;
279
+ label: string;
280
+ onClick: () => void;
281
+ checked: boolean;
282
+ }[];
283
+ })[];
214
284
  }, {
215
285
  type: string;
216
286
  } & Partial<import("mobx-state-tree/dist/internal").ExtractCFromProps<{
@@ -272,7 +342,9 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
272
342
  configuration: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
273
343
  maxFeatureScreenDensity: {
274
344
  type: string;
275
- description: string;
345
+ description: string; /**
346
+ * #property
347
+ */
276
348
  defaultValue: number;
277
349
  };
278
350
  fetchSizeLimit: {
@@ -286,7 +358,9 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
286
358
  description: string;
287
359
  };
288
360
  mouseover: {
289
- type: string;
361
+ type: string; /**
362
+ * #getter
363
+ */
290
364
  description: string;
291
365
  defaultValue: string;
292
366
  contextVariable: string[];
@@ -4,30 +4,120 @@ exports.stateModelFactory = void 0;
4
4
  const configuration_1 = require("@jbrowse/core/configuration");
5
5
  const mobx_state_tree_1 = require("mobx-state-tree");
6
6
  const plugin_linear_genome_view_1 = require("@jbrowse/plugin-linear-genome-view");
7
+ const util_1 = require("@jbrowse/core/util");
8
+ /**
9
+ * #stateModel LinearArcDisplay
10
+ * extends BaseLinearDisplay
11
+ */
7
12
  function stateModelFactory(configSchema) {
8
13
  return mobx_state_tree_1.types
9
14
  .compose('LinearArcDisplay', plugin_linear_genome_view_1.BaseLinearDisplay, mobx_state_tree_1.types.model({
15
+ /**
16
+ * #property
17
+ */
10
18
  type: mobx_state_tree_1.types.literal('LinearArcDisplay'),
19
+ /**
20
+ * #property
21
+ */
11
22
  configuration: (0, configuration_1.ConfigurationReference)(configSchema),
23
+ /**
24
+ * #property
25
+ */
26
+ displayMode: mobx_state_tree_1.types.maybe(mobx_state_tree_1.types.string),
27
+ }))
28
+ .views(self => ({
29
+ /**
30
+ * #getter
31
+ */
32
+ get blockType() {
33
+ return 'staticBlocks';
34
+ },
35
+ /**
36
+ * #getter
37
+ */
38
+ get renderDelay() {
39
+ return 500;
40
+ },
41
+ /**
42
+ * #getter
43
+ */
44
+ get rendererTypeName() {
45
+ return self.configuration.renderer.type;
46
+ },
47
+ }))
48
+ .views(self => ({
49
+ /**
50
+ * #getter
51
+ */
52
+ get displayModeSetting() {
53
+ var _a;
54
+ return (_a = self.displayMode) !== null && _a !== void 0 ? _a : (0, configuration_1.getConf)(self, ['renderer', 'displayMode']);
55
+ },
56
+ }))
57
+ .views(self => ({
58
+ /**
59
+ * #getter
60
+ */
61
+ get rendererConfig() {
62
+ const configBlob = (0, configuration_1.getConf)(self, ['renderer']) || {};
63
+ const config = configBlob;
64
+ return self.rendererType.configSchema.create({
65
+ ...config,
66
+ displayMode: self.displayModeSetting,
67
+ }, (0, util_1.getEnv)(self));
68
+ },
12
69
  }))
13
70
  .views(self => {
14
71
  const { renderProps: superRenderProps } = self;
15
72
  return {
16
- get blockType() {
17
- return 'staticBlocks';
18
- },
19
- get renderDelay() {
20
- return 500;
21
- },
73
+ /**
74
+ * #method
75
+ */
22
76
  renderProps() {
23
77
  return {
24
78
  ...superRenderProps(),
25
79
  rpcDriverName: self.rpcDriverName,
26
- config: self.configuration.renderer,
80
+ config: self.rendererConfig,
81
+ height: self.height,
27
82
  };
28
83
  },
29
- get rendererTypeName() {
30
- return self.configuration.renderer.type;
84
+ };
85
+ })
86
+ .actions(self => ({
87
+ /**
88
+ * #action
89
+ */
90
+ setDisplayMode(flag) {
91
+ self.displayMode = flag;
92
+ },
93
+ }))
94
+ .views(self => {
95
+ const superMenuItems = self.trackMenuItems;
96
+ return {
97
+ /**
98
+ * #method
99
+ */
100
+ trackMenuItems() {
101
+ return [
102
+ ...superMenuItems(),
103
+ {
104
+ label: 'Display mode',
105
+ subMenu: [
106
+ {
107
+ type: 'radio',
108
+ label: 'Arcs',
109
+ onClick: () => self.setDisplayMode('arcs'),
110
+ checked: self.displayMode === 'arcs',
111
+ },
112
+ {
113
+ type: 'radio',
114
+ label: 'Semi-circles',
115
+ onClick: () => self.setDisplayMode('semicircles'),
116
+ checked: self.displayMode === 'semicircles',
117
+ },
118
+ ],
119
+ },
120
+ ];
31
121
  },
32
122
  };
33
123
  });
@@ -1,3 +1,15 @@
1
1
  import React from 'react';
2
- declare const ArcRendering: (props: any) => React.JSX.Element;
2
+ import { AnyConfigurationModel } from '@jbrowse/core/configuration';
3
+ import { Feature, Region } from '@jbrowse/core/util';
4
+ declare const ArcRendering: ({ features, config, regions, bpPerPx, height, exportSVG, displayModel: { selectedFeatureId }, }: {
5
+ features: Map<string, Feature>;
6
+ config: AnyConfigurationModel;
7
+ regions: Region[];
8
+ bpPerPx: number;
9
+ height: number;
10
+ displayModel: {
11
+ selectedFeatureId: string;
12
+ };
13
+ exportSVG: boolean;
14
+ }) => React.JSX.Element;
3
15
  export default ArcRendering;
@@ -1,56 +1,90 @@
1
1
  import React from 'react';
2
- import { readConfObject } from '@jbrowse/core/configuration';
2
+ import { readConfObject, } from '@jbrowse/core/configuration';
3
3
  import { bpSpanPx, measureText } from '@jbrowse/core/util';
4
4
  import { observer } from 'mobx-react';
5
5
  import { Tooltip } from 'react-svg-tooltip';
6
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
- const ArcRendering = observer(function (props) {
8
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
- const onClick = (event, id) => {
10
- const { onFeatureClick: handler } = props;
11
- if (!handler) {
12
- return undefined;
13
- }
14
- return handler(event, id);
6
+ function Arc({ selectedFeatureId, region, bpPerPx, config, onFeatureClick, feature, }) {
7
+ const [left, right] = bpSpanPx(feature.get('start'), feature.get('end'), region, bpPerPx);
8
+ const featureId = feature.id();
9
+ let stroke = readConfObject(config, 'color', { feature });
10
+ let textStroke = 'black';
11
+ if (selectedFeatureId && String(selectedFeatureId) === String(feature.id())) {
12
+ stroke = textStroke = 'red';
13
+ }
14
+ const label = readConfObject(config, 'label', { feature });
15
+ const caption = readConfObject(config, 'caption', { feature });
16
+ const strokeWidth = readConfObject(config, 'thickness', { feature }) || 1;
17
+ const height = readConfObject(config, 'height', { feature }) || 100;
18
+ const ref = React.createRef();
19
+ const tooltipWidth = 20 + measureText(caption === null || caption === void 0 ? void 0 : caption.toString());
20
+ const t = 0.5;
21
+ // formula: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves
22
+ const textYCoord = (1 - t) * (1 - t) * (1 - t) * 0 +
23
+ 3 * ((1 - t) * (1 - t)) * (t * height) +
24
+ 3 * (1 - t) * (t * t) * height +
25
+ t * t * t * 0;
26
+ 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)),
31
+ React.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: 'white', strokeWidth: '0.6em' } }, label),
32
+ React.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: textStroke } }, label)));
33
+ }
34
+ function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
35
+ const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180;
36
+ return {
37
+ x: centerX + radius * Math.cos(angleInRadians),
38
+ y: centerY + radius * Math.sin(angleInRadians),
15
39
  };
16
- const { features, config, regions, blockKey, bpPerPx, displayModel: { selectedFeatureId }, } = props;
17
- const [region] = regions;
18
- const arcsRendered = [];
19
- for (const feature of features.values()) {
20
- const [left, right] = bpSpanPx(feature.get('start'), feature.get('end'), region, bpPerPx);
21
- const featureId = feature.id();
22
- const id = blockKey + '-' + featureId;
23
- let stroke = readConfObject(config, 'color', { feature });
24
- let textStroke = 'black';
25
- if (selectedFeatureId &&
26
- String(selectedFeatureId) === String(feature.id())) {
27
- stroke = textStroke = 'red';
28
- }
29
- const label = readConfObject(config, 'label', { feature });
30
- const caption = readConfObject(config, 'caption', { feature });
31
- const strokeWidth = readConfObject(config, 'thickness', { feature }) || 1;
32
- const height = readConfObject(config, 'height', { feature }) || 100;
33
- const ref = React.createRef();
34
- const tooltipWidth = 20 + measureText(caption === null || caption === void 0 ? void 0 : caption.toString());
35
- const t = 0.5;
36
- // formula: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves
37
- const textYCoord = (1 - t) * (1 - t) * (1 - t) * 0 +
38
- 3 * ((1 - t) * (1 - t)) * (t * height) +
39
- 3 * (1 - t) * (t * t) * height +
40
- t * t * t * 0;
41
- arcsRendered.push(React.createElement("g", { key: id, onClick: e => onClick(e, featureId) },
42
- React.createElement("path", { id: id, d: `M ${left} 0 C ${left} ${height}, ${right} ${height}, ${right} 0`, stroke: stroke, strokeWidth: strokeWidth, fill: "transparent", onClick: e => onClick(e, featureId), ref: ref, pointerEvents: "stroke" }),
43
- React.createElement(Tooltip, { triggerRef: ref },
44
- React.createElement("rect", { x: 12, y: 0, width: tooltipWidth, height: 20, rx: 5, ry: 5, fill: "black", fillOpacity: "50%" }),
45
- React.createElement("text", { x: 22, y: 14, fontSize: 10, fill: "white", textLength: tooltipWidth - 20 }, caption)),
46
- React.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: 'white', strokeWidth: '0.6em' } }, label),
47
- React.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: textStroke } }, label)));
40
+ }
41
+ function describeArc(x, y, radius, startAngle, endAngle) {
42
+ const start = polarToCartesian(x, y, radius, endAngle);
43
+ const end = polarToCartesian(x, y, radius, startAngle);
44
+ const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
45
+ return [
46
+ 'M',
47
+ start.x,
48
+ start.y,
49
+ 'A',
50
+ radius,
51
+ radius,
52
+ 0,
53
+ largeArcFlag,
54
+ 0,
55
+ end.x,
56
+ end.y,
57
+ ].join(' ');
58
+ }
59
+ function SemiCircles({ selectedFeatureId, region, bpPerPx, config, onFeatureClick, feature, }) {
60
+ const [left, right] = bpSpanPx(feature.get('start'), feature.get('end'), region, bpPerPx);
61
+ const featureId = feature.id();
62
+ let stroke = readConfObject(config, 'color', { feature });
63
+ let textStroke = 'black';
64
+ if (selectedFeatureId && String(selectedFeatureId) === String(feature.id())) {
65
+ stroke = textStroke = 'red';
48
66
  }
67
+ const label = readConfObject(config, 'label', { feature });
68
+ const caption = readConfObject(config, 'caption', { feature });
69
+ const strokeWidth = readConfObject(config, 'thickness', { feature }) || 1;
70
+ const ref = React.createRef();
71
+ const tooltipWidth = 20 + measureText(caption === null || caption === void 0 ? void 0 : caption.toString());
72
+ const textYCoord = (right - left) / 2;
73
+ 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)),
78
+ React.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: 'white', strokeWidth: '0.6em' } }, label),
79
+ React.createElement("text", { x: left + (right - left) / 2, y: textYCoord + 3, style: { stroke: textStroke } }, label)));
80
+ }
81
+ const ArcRendering = observer(function ({ features, config, regions, bpPerPx, height, exportSVG, displayModel: { selectedFeatureId }, }) {
82
+ const [region] = regions;
49
83
  const width = (region.end - region.start) / bpPerPx;
50
- const height = 500;
51
- return (React.createElement("svg", { className: "ArcRendering", width: width, height: height, style: {
52
- outline: 'none',
53
- position: 'relative',
54
- } }, arcsRendered));
84
+ const semicircles = readConfObject(config, 'displayMode') === 'semicircles';
85
+ return (React.createElement(Wrapper, { exportSVG: exportSVG, width: width, height: height }, [...features.values()].map(f => semicircles ? (React.createElement(SemiCircles, { key: f.id(), config: config, region: region, bpPerPx: bpPerPx, selectedFeatureId: selectedFeatureId, feature: f })) : (React.createElement(Arc, { key: f.id(), config: config, region: region, bpPerPx: bpPerPx, selectedFeatureId: selectedFeatureId, feature: f })))));
55
86
  });
87
+ function Wrapper({ exportSVG, width, height, children, }) {
88
+ return exportSVG ? (React.createElement(React.Fragment, null, children)) : (React.createElement("svg", { width: width, height: height }, children));
89
+ }
56
90
  export default ArcRendering;
@@ -44,5 +44,14 @@ declare const ArcRenderer: import("@jbrowse/core/configuration/configurationSche
44
44
  defaultValue: string;
45
45
  contextVariable: string[];
46
46
  };
47
+ /**
48
+ * #slot
49
+ */
50
+ displayMode: {
51
+ type: string;
52
+ defaultValue: string;
53
+ model: import("mobx-state-tree").ISimpleType<string>;
54
+ description: string;
55
+ };
47
56
  }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
48
57
  export default ArcRenderer;
@@ -1,4 +1,5 @@
1
1
  import { ConfigurationSchema } from '@jbrowse/core/configuration';
2
+ import { types } from 'mobx-state-tree';
2
3
  /**
3
4
  * #config ArcRenderer
4
5
  */
@@ -49,5 +50,14 @@ const ArcRenderer = ConfigurationSchema('ArcRenderer', {
49
50
  defaultValue: `jexl:get(feature,'name')`,
50
51
  contextVariable: ['feature'],
51
52
  },
53
+ /**
54
+ * #slot
55
+ */
56
+ displayMode: {
57
+ type: 'enum',
58
+ defaultValue: 'arcs',
59
+ model: types.enumeration('DisplayMode', ['arcs', 'semicircles']),
60
+ description: 'render semi-circles instead of arcs',
61
+ },
52
62
  }, { explicitlyTyped: true });
53
63
  export default ArcRenderer;
@@ -1,5 +1,9 @@
1
1
  /// <reference types="react" />
2
2
  import { AnyConfigurationSchemaType } from '@jbrowse/core/configuration';
3
+ /**
4
+ * #stateModel LinearArcDisplay
5
+ * extends BaseLinearDisplay
6
+ */
3
7
  export declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): import("mobx-state-tree").IModelType<{
4
8
  id: import("mobx-state-tree").IOptionalIType<import("mobx-state-tree").ISimpleType<string>, [undefined]>;
5
9
  type: import("mobx-state-tree").ISimpleType<string>;
@@ -59,7 +63,9 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
59
63
  configuration: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
60
64
  maxFeatureScreenDensity: {
61
65
  type: string;
62
- description: string;
66
+ description: string; /**
67
+ * #property
68
+ */
63
69
  defaultValue: number;
64
70
  };
65
71
  fetchSizeLimit: {
@@ -73,21 +79,36 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
73
79
  description: string;
74
80
  };
75
81
  mouseover: {
76
- type: string;
82
+ type: string; /**
83
+ * #getter
84
+ */
77
85
  description: string;
78
86
  defaultValue: string;
79
87
  contextVariable: string[];
80
88
  };
81
89
  }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, "displayId">>;
82
90
  } & {
91
+ /**
92
+ * #property
93
+ */
83
94
  type: import("mobx-state-tree").ISimpleType<"LinearArcDisplay">;
95
+ /**
96
+ * #property
97
+ */
84
98
  configuration: AnyConfigurationSchemaType;
99
+ /**
100
+ * #property
101
+ */
102
+ displayMode: import("mobx-state-tree").IMaybe<import("mobx-state-tree").ISimpleType<string>>;
85
103
  }, {
86
104
  rendererTypeName: string;
87
105
  error: unknown;
88
106
  message: string | undefined;
89
107
  } & {
90
108
  readonly RenderingComponent: import("react").FC<{
109
+ /**
110
+ * #property
111
+ */
91
112
  model: {
92
113
  id: string;
93
114
  type: string;
@@ -143,7 +164,9 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
143
164
  } & {
144
165
  scrollTop: number;
145
166
  } & {
146
- readonly height: number;
167
+ readonly height: number; /**
168
+ * #property
169
+ */
147
170
  } & {
148
171
  setScrollTop(scrollTop: number): void;
149
172
  setHeight(displayHeight: number): number;
@@ -157,7 +180,9 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
157
180
  readonly currentFeatureScreenDensity: number;
158
181
  readonly maxFeatureScreenDensity: any;
159
182
  readonly featureDensityStatsReady: boolean;
160
- readonly maxAllowableBytes: number;
183
+ readonly maxAllowableBytes: number; /**
184
+ * #method
185
+ */
161
186
  } & {
162
187
  afterAttach(): void;
163
188
  } & {
@@ -207,10 +232,55 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
207
232
  renderSvg(opts: import("@jbrowse/plugin-linear-genome-view").ExportSvgDisplayOptions): Promise<import("react").JSX.Element>;
208
233
  afterAttach(): void;
209
234
  } & {
235
+ /**
236
+ * #getter
237
+ */
210
238
  readonly blockType: string;
239
+ /**
240
+ * #getter
241
+ */
211
242
  readonly renderDelay: number;
212
- renderProps(): any;
243
+ /**
244
+ * #getter
245
+ */
213
246
  readonly rendererTypeName: any;
247
+ } & {
248
+ /**
249
+ * #getter
250
+ */
251
+ readonly displayModeSetting: any;
252
+ } & {
253
+ /**
254
+ * #getter
255
+ */
256
+ readonly rendererConfig: {
257
+ [x: string]: any;
258
+ } & import("mobx-state-tree/dist/internal").NonEmptyObject & {
259
+ setSubschema(slotName: string, data: unknown): any;
260
+ } & import("mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>;
261
+ } & {
262
+ /**
263
+ * #method
264
+ */
265
+ renderProps(): any;
266
+ } & {
267
+ /**
268
+ * #action
269
+ */
270
+ setDisplayMode(flag: string): void;
271
+ } & {
272
+ /**
273
+ * #method
274
+ */
275
+ trackMenuItems(): (import("@jbrowse/core/ui").MenuDivider | import("@jbrowse/core/ui").MenuSubHeader | import("@jbrowse/core/ui").NormalMenuItem | import("@jbrowse/core/ui").CheckboxMenuItem | import("@jbrowse/core/ui").RadioMenuItem | import("@jbrowse/core/ui").SubMenuItem | {
276
+ label: string;
277
+ subMenu: {
278
+ type: string;
279
+ label: string;
280
+ onClick: () => void;
281
+ checked: boolean;
282
+ }[];
283
+ })[];
214
284
  }, {
215
285
  type: string;
216
286
  } & Partial<import("mobx-state-tree/dist/internal").ExtractCFromProps<{
@@ -272,7 +342,9 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
272
342
  configuration: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
273
343
  maxFeatureScreenDensity: {
274
344
  type: string;
275
- description: string;
345
+ description: string; /**
346
+ * #property
347
+ */
276
348
  defaultValue: number;
277
349
  };
278
350
  fetchSizeLimit: {
@@ -286,7 +358,9 @@ export declare function stateModelFactory(configSchema: AnyConfigurationSchemaTy
286
358
  description: string;
287
359
  };
288
360
  mouseover: {
289
- type: string;
361
+ type: string; /**
362
+ * #getter
363
+ */
290
364
  description: string;
291
365
  defaultValue: string;
292
366
  contextVariable: string[];
@@ -1,30 +1,120 @@
1
- import { ConfigurationReference, } from '@jbrowse/core/configuration';
1
+ import { ConfigurationReference, getConf, } from '@jbrowse/core/configuration';
2
2
  import { types } from 'mobx-state-tree';
3
3
  import { BaseLinearDisplay } from '@jbrowse/plugin-linear-genome-view';
4
+ import { getEnv } from '@jbrowse/core/util';
5
+ /**
6
+ * #stateModel LinearArcDisplay
7
+ * extends BaseLinearDisplay
8
+ */
4
9
  export function stateModelFactory(configSchema) {
5
10
  return types
6
11
  .compose('LinearArcDisplay', BaseLinearDisplay, types.model({
12
+ /**
13
+ * #property
14
+ */
7
15
  type: types.literal('LinearArcDisplay'),
16
+ /**
17
+ * #property
18
+ */
8
19
  configuration: ConfigurationReference(configSchema),
20
+ /**
21
+ * #property
22
+ */
23
+ displayMode: types.maybe(types.string),
24
+ }))
25
+ .views(self => ({
26
+ /**
27
+ * #getter
28
+ */
29
+ get blockType() {
30
+ return 'staticBlocks';
31
+ },
32
+ /**
33
+ * #getter
34
+ */
35
+ get renderDelay() {
36
+ return 500;
37
+ },
38
+ /**
39
+ * #getter
40
+ */
41
+ get rendererTypeName() {
42
+ return self.configuration.renderer.type;
43
+ },
44
+ }))
45
+ .views(self => ({
46
+ /**
47
+ * #getter
48
+ */
49
+ get displayModeSetting() {
50
+ var _a;
51
+ return (_a = self.displayMode) !== null && _a !== void 0 ? _a : getConf(self, ['renderer', 'displayMode']);
52
+ },
53
+ }))
54
+ .views(self => ({
55
+ /**
56
+ * #getter
57
+ */
58
+ get rendererConfig() {
59
+ const configBlob = getConf(self, ['renderer']) || {};
60
+ const config = configBlob;
61
+ return self.rendererType.configSchema.create({
62
+ ...config,
63
+ displayMode: self.displayModeSetting,
64
+ }, getEnv(self));
65
+ },
9
66
  }))
10
67
  .views(self => {
11
68
  const { renderProps: superRenderProps } = self;
12
69
  return {
13
- get blockType() {
14
- return 'staticBlocks';
15
- },
16
- get renderDelay() {
17
- return 500;
18
- },
70
+ /**
71
+ * #method
72
+ */
19
73
  renderProps() {
20
74
  return {
21
75
  ...superRenderProps(),
22
76
  rpcDriverName: self.rpcDriverName,
23
- config: self.configuration.renderer,
77
+ config: self.rendererConfig,
78
+ height: self.height,
24
79
  };
25
80
  },
26
- get rendererTypeName() {
27
- return self.configuration.renderer.type;
81
+ };
82
+ })
83
+ .actions(self => ({
84
+ /**
85
+ * #action
86
+ */
87
+ setDisplayMode(flag) {
88
+ self.displayMode = flag;
89
+ },
90
+ }))
91
+ .views(self => {
92
+ const superMenuItems = self.trackMenuItems;
93
+ return {
94
+ /**
95
+ * #method
96
+ */
97
+ trackMenuItems() {
98
+ return [
99
+ ...superMenuItems(),
100
+ {
101
+ label: 'Display mode',
102
+ subMenu: [
103
+ {
104
+ type: 'radio',
105
+ label: 'Arcs',
106
+ onClick: () => self.setDisplayMode('arcs'),
107
+ checked: self.displayMode === 'arcs',
108
+ },
109
+ {
110
+ type: 'radio',
111
+ label: 'Semi-circles',
112
+ onClick: () => self.setDisplayMode('semicircles'),
113
+ checked: self.displayMode === 'semicircles',
114
+ },
115
+ ],
116
+ },
117
+ ];
28
118
  },
29
119
  };
30
120
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-arc",
3
- "version": "2.7.0",
3
+ "version": "2.7.2",
4
4
  "description": "JBrowse 2 arc adapters, tracks, etc.",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -56,5 +56,5 @@
56
56
  "publishConfig": {
57
57
  "access": "public"
58
58
  },
59
- "gitHead": "dbe7fb1af01fc89f833d2744635eb44a17365b41"
59
+ "gitHead": "9052b295f2d322e729254457ed9fe2231fb22cce"
60
60
  }