higlass 1.13.0 → 1.13.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.
- package/app/scripts/Autocomplete.jsx +1 -0
- package/app/scripts/CustomTrackDialog.jsx +4 -0
- package/app/scripts/GenomePositionSearchBox.jsx +1 -0
- package/app/scripts/ListWrapper.jsx +1 -0
- package/app/scripts/PixiTrack.js +106 -42
- package/app/scripts/SortableList.jsx +1 -0
- package/app/scripts/TiledPlot.jsx +2 -0
- package/app/scripts/Track.js +153 -45
- package/app/scripts/TrackRenderer.jsx +54 -53
- package/app/scripts/hocs/with-modal.jsx +1 -0
- package/app/scripts/hocs/with-pub-sub.jsx +4 -1
- package/app/scripts/hocs/with-theme.jsx +1 -0
- package/app/scripts/icons.jsx +6 -0
- package/app/scripts/services/tile-proxy.js +1 -1
- package/app/scripts/types.ts +26 -0
- package/app/scripts/utils/type-guards.js +24 -0
- package/dist/esm.html +283 -0
- package/dist/hglib.js +42 -82877
- package/dist/hglib.min.js +86 -105
- package/dist/higlass.mjs +207 -0
- package/dist/index.html +6 -6
- package/package.json +10 -3
|
@@ -11,6 +11,7 @@ class CustomTrackDialog extends React.PureComponent {
|
|
|
11
11
|
const childrenWithProp = [];
|
|
12
12
|
this.props.children.forEach((Child, i) => {
|
|
13
13
|
const key = `customTrackDialog_${i}`;
|
|
14
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
14
15
|
childrenWithProp.push(<Child key={key} {...this.props.bodyProps[i]} />);
|
|
15
16
|
});
|
|
16
17
|
|
|
@@ -33,6 +34,9 @@ CustomTrackDialog.defaultProps = {};
|
|
|
33
34
|
|
|
34
35
|
CustomTrackDialog.propTypes = {
|
|
35
36
|
onCancel: PropTypes.func.isRequired,
|
|
37
|
+
title: PropTypes.string,
|
|
38
|
+
bodyProps: PropTypes.array,
|
|
39
|
+
children: PropTypes.array,
|
|
36
40
|
};
|
|
37
41
|
|
|
38
42
|
export default CustomTrackDialog;
|
|
@@ -856,6 +856,7 @@ GenomePositionSearchBox.propTypes = {
|
|
|
856
856
|
autocompleteServer: PropTypes.string,
|
|
857
857
|
chromInfoId: PropTypes.string,
|
|
858
858
|
chromInfoServer: PropTypes.string,
|
|
859
|
+
chromInfoPath: PropTypes.string,
|
|
859
860
|
hideAvailableAssemblies: PropTypes.bool,
|
|
860
861
|
isFocused: PropTypes.bool,
|
|
861
862
|
pubSub: PropTypes.object,
|
package/app/scripts/PixiTrack.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
import { formatPrefix, precisionPrefix } from 'd3-format';
|
|
2
3
|
import slugid from 'slugid';
|
|
3
4
|
|
|
@@ -7,14 +8,18 @@ import { colorToHex } from './utils';
|
|
|
7
8
|
|
|
8
9
|
// Configs
|
|
9
10
|
import { GLOBALS } from './configs';
|
|
11
|
+
import {
|
|
12
|
+
isResolutionsTilesetInfo,
|
|
13
|
+
isLegacyTilesetInfo,
|
|
14
|
+
} from './utils/type-guards';
|
|
10
15
|
|
|
11
16
|
/**
|
|
12
17
|
* Format a resolution relative to the highest possible resolution.
|
|
13
18
|
*
|
|
14
19
|
* The highest possible resolution determines the granularity of the
|
|
15
20
|
* formatting (e.g. 20K vs 20000)
|
|
16
|
-
* @param {
|
|
17
|
-
* @param {
|
|
21
|
+
* @param {number} resolution The resolution to format (e.g. 30000)
|
|
22
|
+
* @param {number} maxResolutionSize The maximum possible resolution (e.g. 1000)
|
|
18
23
|
*
|
|
19
24
|
* @returns {string} A formatted resolution string (e.g. "30K")
|
|
20
25
|
*/
|
|
@@ -30,11 +35,10 @@ function formatResolutionText(resolution, maxResolutionSize) {
|
|
|
30
35
|
* Get a text description of a resolution based on a zoom level
|
|
31
36
|
* and a list of resolutions
|
|
32
37
|
*
|
|
33
|
-
* @param {
|
|
34
|
-
* @param {
|
|
38
|
+
* @param {Array<number>} resolutions: A list of resolutions (e.g. [1000,2000,3000])
|
|
39
|
+
* @param {number} zoomLevel: The current zoom level (e.g. 4)
|
|
35
40
|
*
|
|
36
|
-
* @returns {string} A formatted string representation of the zoom level
|
|
37
|
-
* (e.g. "30K")
|
|
41
|
+
* @returns {string} A formatted string representation of the zoom level (e.g. "30K")
|
|
38
42
|
*/
|
|
39
43
|
function getResolutionBasedResolutionText(resolutions, zoomLevel) {
|
|
40
44
|
const sortedResolutions = resolutions.map((x) => +x).sort((a, b) => b - a);
|
|
@@ -46,18 +50,14 @@ function getResolutionBasedResolutionText(resolutions, zoomLevel) {
|
|
|
46
50
|
|
|
47
51
|
/**
|
|
48
52
|
* Get a text description of the resolution based on the zoom level
|
|
49
|
-
* max width of the dataset, the bins per dimension and the maximum
|
|
50
|
-
* zoom.
|
|
53
|
+
* max width of the dataset, the bins per dimension and the maximum zoom.
|
|
51
54
|
*
|
|
52
|
-
* @param {
|
|
53
|
-
* @param {
|
|
54
|
-
*
|
|
55
|
-
* @param {
|
|
56
|
-
* (e.g. 256)
|
|
57
|
-
* @param {int} maxZoom The maximum zoom level for this tileset
|
|
55
|
+
* @param {number} zoomLevel - The current zoomLevel (e.g. 0)
|
|
56
|
+
* @param {number} maxWidth - The max width (e.g. 2 ** maxZoom * highestResolution * binsPerDimension)
|
|
57
|
+
* @param {number} binsPerDimension - The number of bins per tile dimension (e.g. 256)
|
|
58
|
+
* @param {number} maxZoom - The maximum zoom level for this tileset
|
|
58
59
|
*
|
|
59
|
-
* @returns {string} A formatted string representation of the zoom level
|
|
60
|
-
* (e.g. "30K")
|
|
60
|
+
* @returns {string} A formatted string representation of the zoom level (e.g. "30K")
|
|
61
61
|
*/
|
|
62
62
|
function getWidthBasedResolutionText(
|
|
63
63
|
zoomLevel,
|
|
@@ -84,14 +84,35 @@ function getWidthBasedResolutionText(
|
|
|
84
84
|
return '';
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* @typedef PixiTrackOptions
|
|
89
|
+
* @property {string} labelPosition - If the label is to be drawn, where should it be drawn?
|
|
90
|
+
* @property {string} labelText - What should be drawn in the label.
|
|
91
|
+
* If either labelPosition or labelText are false, no label will be drawn.
|
|
92
|
+
* @property {number=} trackBorderWidth
|
|
93
|
+
* @property {string=} trackBorderColor
|
|
94
|
+
* @property {string=} backgroundColor
|
|
95
|
+
* @property {string=} labelColor
|
|
96
|
+
* @property {string=} lineStrokeColor
|
|
97
|
+
* @property {string=} barFillColor
|
|
98
|
+
* @property {string=} name
|
|
99
|
+
* @property {number=} labelTextOpacity
|
|
100
|
+
* @property {string=} labelBackgroundColor
|
|
101
|
+
* @property {number=} labelLeftMargin
|
|
102
|
+
* @property {number=} labelRightMargin
|
|
103
|
+
* @property {number=} labelTopMargin
|
|
104
|
+
* @property {number=} labelBottomMargin
|
|
105
|
+
* @property {number=} labelBackgroundOpacity
|
|
106
|
+
* @property {boolean=} labelShowAssembly
|
|
107
|
+
* @property {boolean=} labelShowResolution
|
|
108
|
+
* @property {string=} dataTransform
|
|
109
|
+
*/
|
|
110
|
+
|
|
111
|
+
/** @extends {Track<PixiTrackOptions>} */
|
|
87
112
|
class PixiTrack extends Track {
|
|
88
113
|
/**
|
|
89
|
-
* @param scene:
|
|
90
|
-
* @param options
|
|
91
|
-
this.pMain.position.x = this.position[0];
|
|
92
|
-
* - labelPosition: If the label is to be drawn, where should it be drawn?
|
|
93
|
-
* - labelText: What should be drawn in the label. If either labelPosition
|
|
94
|
-
* or labelText are false, no label will be drawn.
|
|
114
|
+
* @param {import('./Track').ExtendedTrackContext<{ scene: import('pixi.js').Container}>} context - Includes the PIXI.js scene to draw to.
|
|
115
|
+
* @param {PixiTrackOptions} options - The options for this track.
|
|
95
116
|
*/
|
|
96
117
|
constructor(context, options) {
|
|
97
118
|
super(context, options);
|
|
@@ -99,27 +120,39 @@ class PixiTrack extends Track {
|
|
|
99
120
|
|
|
100
121
|
// the PIXI drawing areas
|
|
101
122
|
// pMain will have transforms applied to it as users scroll to and fro
|
|
123
|
+
/** @type {import('pixi.js').Container} */
|
|
102
124
|
this.scene = scene;
|
|
103
125
|
|
|
104
126
|
// this option is used to temporarily prevent drawing so that
|
|
105
127
|
// updates can be batched (e.g. zoomed and options changed)
|
|
128
|
+
/** @type {boolean} */
|
|
106
129
|
this.delayDrawing = false;
|
|
107
130
|
|
|
131
|
+
/** @type {import('pixi.js').Graphics} */
|
|
108
132
|
this.pBase = new GLOBALS.PIXI.Graphics();
|
|
109
|
-
|
|
133
|
+
/** @type {import('pixi.js').Graphics} */
|
|
110
134
|
this.pMasked = new GLOBALS.PIXI.Graphics();
|
|
135
|
+
/** @type {import('pixi.js').Graphics} */
|
|
111
136
|
this.pMask = new GLOBALS.PIXI.Graphics();
|
|
137
|
+
/** @type {import('pixi.js').Graphics} */
|
|
112
138
|
this.pMain = new GLOBALS.PIXI.Graphics();
|
|
113
139
|
|
|
114
140
|
// for drawing the track label (often its name)
|
|
141
|
+
/** @type {import('pixi.js').Graphics} */
|
|
115
142
|
this.pBorder = new GLOBALS.PIXI.Graphics();
|
|
143
|
+
/** @type {import('pixi.js').Graphics} */
|
|
116
144
|
this.pBackground = new GLOBALS.PIXI.Graphics();
|
|
145
|
+
/** @type {import('pixi.js').Graphics} */
|
|
117
146
|
this.pForeground = new GLOBALS.PIXI.Graphics();
|
|
147
|
+
/** @type {import('pixi.js').Graphics} */
|
|
118
148
|
this.pLabel = new GLOBALS.PIXI.Graphics();
|
|
149
|
+
/** @type {import('pixi.js').Graphics} */
|
|
119
150
|
this.pMobile = new GLOBALS.PIXI.Graphics();
|
|
151
|
+
/** @type {import('pixi.js').Graphics} */
|
|
120
152
|
this.pAxis = new GLOBALS.PIXI.Graphics();
|
|
121
153
|
|
|
122
154
|
// for drawing information on mouseover events
|
|
155
|
+
/** @type {import('pixi.js').Graphics} */
|
|
123
156
|
this.pMouseOver = new GLOBALS.PIXI.Graphics();
|
|
124
157
|
|
|
125
158
|
this.scene.addChild(this.pBase);
|
|
@@ -138,20 +171,28 @@ class PixiTrack extends Track {
|
|
|
138
171
|
|
|
139
172
|
this.pMasked.mask = this.pMask;
|
|
140
173
|
|
|
174
|
+
/** @type {string} */
|
|
141
175
|
this.prevOptions = '';
|
|
142
176
|
|
|
143
177
|
// pMobile will be a graphics object that is moved around
|
|
144
178
|
// tracks that wish to use it will replace this.pMain with it
|
|
145
179
|
|
|
180
|
+
/** @type {PixiTrackOptions} */
|
|
146
181
|
this.options = Object.assign(this.options, options);
|
|
147
182
|
|
|
183
|
+
/** @type {string} */
|
|
148
184
|
const labelTextText = this.getName();
|
|
149
|
-
|
|
185
|
+
/** @type {string} */
|
|
150
186
|
this.labelTextFontFamily = 'Arial';
|
|
187
|
+
/** @type {number} */
|
|
151
188
|
this.labelTextFontSize = 12;
|
|
152
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Used to avoid label/colormap clashes
|
|
191
|
+
* @type {number}
|
|
192
|
+
*/
|
|
153
193
|
this.labelXOffset = 0;
|
|
154
194
|
|
|
195
|
+
/** @type {import('pixi.js').Text} */
|
|
155
196
|
this.labelText = new GLOBALS.PIXI.Text(labelTextText, {
|
|
156
197
|
fontSize: `${this.labelTextFontSize}px`,
|
|
157
198
|
fontFamily: this.labelTextFontFamily,
|
|
@@ -159,6 +200,7 @@ class PixiTrack extends Track {
|
|
|
159
200
|
});
|
|
160
201
|
this.pLabel.addChild(this.labelText);
|
|
161
202
|
|
|
203
|
+
/** @type {import('pixi.js').Text} */
|
|
162
204
|
this.errorText = new GLOBALS.PIXI.Text('', {
|
|
163
205
|
fontSize: '12px',
|
|
164
206
|
fontFamily: 'Arial',
|
|
@@ -167,12 +209,19 @@ class PixiTrack extends Track {
|
|
|
167
209
|
this.errorText.anchor.x = 0.5;
|
|
168
210
|
this.errorText.anchor.y = 0.5;
|
|
169
211
|
this.pLabel.addChild(this.errorText);
|
|
212
|
+
/** @type {string} */
|
|
213
|
+
this.errorTextText = '';
|
|
214
|
+
/** @type {boolean} */
|
|
215
|
+
this.flipText = false;
|
|
216
|
+
/** @type {import('./types').TilesetInfo | undefined} */
|
|
217
|
+
this.tilesetInfo = undefined;
|
|
170
218
|
}
|
|
171
219
|
|
|
172
220
|
setLabelText() {
|
|
173
221
|
// will be drawn in draw() anyway
|
|
174
222
|
}
|
|
175
223
|
|
|
224
|
+
/** @param {[number, number]} newPosition */
|
|
176
225
|
setPosition(newPosition) {
|
|
177
226
|
this.position = newPosition;
|
|
178
227
|
|
|
@@ -183,6 +232,7 @@ class PixiTrack extends Track {
|
|
|
183
232
|
this.setForeground();
|
|
184
233
|
}
|
|
185
234
|
|
|
235
|
+
/** @param {[number, number]} newDimensions */
|
|
186
236
|
setDimensions(newDimensions) {
|
|
187
237
|
super.setDimensions(newDimensions);
|
|
188
238
|
|
|
@@ -193,6 +243,10 @@ class PixiTrack extends Track {
|
|
|
193
243
|
this.setForeground();
|
|
194
244
|
}
|
|
195
245
|
|
|
246
|
+
/**
|
|
247
|
+
* @param {[number, number]} position
|
|
248
|
+
* @param {[number, number]} dimensions
|
|
249
|
+
*/
|
|
196
250
|
setMask(position, dimensions) {
|
|
197
251
|
this.pMask.clear();
|
|
198
252
|
this.pMask.beginFill();
|
|
@@ -320,6 +374,8 @@ class PixiTrack extends Track {
|
|
|
320
374
|
|
|
321
375
|
graphics.clear();
|
|
322
376
|
|
|
377
|
+
// TODO(Trevor): I don't think this can ever be true. Options are always defined,
|
|
378
|
+
// and options.labelPosition can't be defined if this.options is undefined.
|
|
323
379
|
if (
|
|
324
380
|
!this.options ||
|
|
325
381
|
!this.options.labelPosition ||
|
|
@@ -330,11 +386,11 @@ class PixiTrack extends Track {
|
|
|
330
386
|
return;
|
|
331
387
|
}
|
|
332
388
|
|
|
389
|
+
const { labelBackgroundColor = 'white', labelBackgroundOpacity = 0.5 } =
|
|
390
|
+
this.options;
|
|
333
391
|
graphics.beginFill(
|
|
334
|
-
colorToHex(
|
|
335
|
-
+
|
|
336
|
-
? +this.options.labelBackgroundOpacity
|
|
337
|
-
: 0.5,
|
|
392
|
+
colorToHex(labelBackgroundColor),
|
|
393
|
+
+labelBackgroundOpacity,
|
|
338
394
|
);
|
|
339
395
|
|
|
340
396
|
const fontColor = colorToHex(this.getLabelColor());
|
|
@@ -356,8 +412,7 @@ class PixiTrack extends Track {
|
|
|
356
412
|
|
|
357
413
|
if (
|
|
358
414
|
this.options.labelShowResolution &&
|
|
359
|
-
this.tilesetInfo &&
|
|
360
|
-
this.tilesetInfo.max_width &&
|
|
415
|
+
isLegacyTilesetInfo(this.tilesetInfo) &&
|
|
361
416
|
this.tilesetInfo.bins_per_dimension
|
|
362
417
|
) {
|
|
363
418
|
const formattedResolution = getWidthBasedResolutionText(
|
|
@@ -370,8 +425,7 @@ class PixiTrack extends Track {
|
|
|
370
425
|
labelTextText += `\n[Current data resolution: ${formattedResolution}]`;
|
|
371
426
|
} else if (
|
|
372
427
|
this.options.labelShowResolution &&
|
|
373
|
-
this.tilesetInfo
|
|
374
|
-
this.tilesetInfo.resolutions
|
|
428
|
+
isResolutionsTilesetInfo(this.tilesetInfo)
|
|
375
429
|
) {
|
|
376
430
|
const formattedResolution = getResolutionBasedResolutionText(
|
|
377
431
|
this.tilesetInfo.resolutions,
|
|
@@ -418,10 +472,12 @@ class PixiTrack extends Track {
|
|
|
418
472
|
this.labelText.scale.x = -1;
|
|
419
473
|
}
|
|
420
474
|
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
475
|
+
const {
|
|
476
|
+
labelLeftMargin = 0,
|
|
477
|
+
labelRightMargin = 0,
|
|
478
|
+
labelTopMargin = 0,
|
|
479
|
+
labelBottomMargin = 0,
|
|
480
|
+
} = this.options;
|
|
425
481
|
|
|
426
482
|
if (this.options.labelPosition === 'topLeft') {
|
|
427
483
|
this.labelText.x = this.position[0] + labelLeftMargin + this.labelXOffset;
|
|
@@ -575,6 +631,7 @@ class PixiTrack extends Track {
|
|
|
575
631
|
}
|
|
576
632
|
}
|
|
577
633
|
|
|
634
|
+
/** @param {PixiTrackOptions} options */
|
|
578
635
|
rerender(options) {
|
|
579
636
|
this.options = options;
|
|
580
637
|
|
|
@@ -598,8 +655,8 @@ class PixiTrack extends Track {
|
|
|
598
655
|
/**
|
|
599
656
|
* Export an SVG representation of this track
|
|
600
657
|
*
|
|
601
|
-
* @returns {
|
|
602
|
-
* elements [base,track]. Base is a parent which contains track as a
|
|
658
|
+
* @returns {[HTMLElement, HTMLElement]} The two returned DOM nodes are both SVG
|
|
659
|
+
* elements [base, track]. Base is a parent which contains track as a
|
|
603
660
|
* child. Track is clipped with a clipping rectangle contained in base.
|
|
604
661
|
*
|
|
605
662
|
*/
|
|
@@ -693,15 +750,15 @@ class PixiTrack extends Track {
|
|
|
693
750
|
this.options.labelPosition === 'topRight'
|
|
694
751
|
) {
|
|
695
752
|
const dy = ddy + (i + 1) * (this.labelTextFontSize + 2);
|
|
696
|
-
text.setAttribute('dy', dy);
|
|
753
|
+
text.setAttribute('dy', String(dy));
|
|
697
754
|
} else if (
|
|
698
755
|
this.options.labelPosition === 'bottomLeft' ||
|
|
699
756
|
this.options.labelPosition === 'bottomRight'
|
|
700
757
|
) {
|
|
701
|
-
text.setAttribute('dy', ddy + i * (this.labelTextFontSize + 2));
|
|
758
|
+
text.setAttribute('dy', String(ddy + i * (this.labelTextFontSize + 2)));
|
|
702
759
|
}
|
|
703
760
|
|
|
704
|
-
text.setAttribute('fill', this.options.labelColor);
|
|
761
|
+
text.setAttribute('fill', this.options.labelColor ?? '');
|
|
705
762
|
|
|
706
763
|
if (this.labelText.anchor.x === 0.5) {
|
|
707
764
|
text.setAttribute('text-anchor', 'middle');
|
|
@@ -721,6 +778,13 @@ class PixiTrack extends Track {
|
|
|
721
778
|
// contents
|
|
722
779
|
return [gBase, gTrack];
|
|
723
780
|
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* @returns {number}
|
|
784
|
+
*/
|
|
785
|
+
calculateZoomLevel() {
|
|
786
|
+
throw new Error('Must be implemented by subclass');
|
|
787
|
+
}
|
|
724
788
|
}
|
|
725
789
|
|
|
726
790
|
export default PixiTrack;
|
|
@@ -2388,6 +2388,8 @@ TiledPlot.propTypes = {
|
|
|
2388
2388
|
canvasElement: PropTypes.object,
|
|
2389
2389
|
chooseTrackHandler: PropTypes.func,
|
|
2390
2390
|
chromInfoPath: PropTypes.string,
|
|
2391
|
+
customDialog: PropTypes.array,
|
|
2392
|
+
closeCustomDialog: PropTypes.func,
|
|
2391
2393
|
disableTrackMenu: PropTypes.bool,
|
|
2392
2394
|
dragging: PropTypes.bool,
|
|
2393
2395
|
draggingHappening: PropTypes.bool,
|