@jbrowse/plugin-wiggle 2.7.0 → 2.7.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.
- package/dist/DensityRenderer/DensityRenderer.js +2 -2
- package/dist/LinePlotRenderer/LinePlotRenderer.js +2 -2
- package/dist/MultiDensityRenderer/MultiDensityRenderer.js +5 -8
- package/dist/MultiLineRenderer/MultiLineRenderer.js +5 -9
- package/dist/MultiLinearWiggleDisplay/components/SetColorDialog.js +2 -102
- package/dist/MultiLinearWiggleDisplay/components/SourcesGrid.d.ts +8 -0
- package/dist/MultiLinearWiggleDisplay/components/SourcesGrid.js +138 -0
- package/dist/MultiRowLineRenderer/MultiRowLineRenderer.js +5 -5
- package/dist/MultiRowXYPlotRenderer/MultiRowXYPlotRenderer.js +5 -9
- package/dist/MultiXYPlotRenderer/MultiXYPlotRenderer.js +5 -8
- package/dist/XYPlotRenderer/XYPlotRenderer.js +2 -2
- package/dist/drawDensity.d.ts +17 -0
- package/dist/drawDensity.js +66 -0
- package/dist/drawLine.d.ts +19 -0
- package/dist/drawLine.js +73 -0
- package/dist/drawXY.d.ts +19 -0
- package/dist/{drawxy.js → drawXY.js} +11 -147
- package/dist/util.d.ts +1 -0
- package/dist/util.js +17 -1
- package/esm/DensityRenderer/DensityRenderer.js +1 -1
- package/esm/LinePlotRenderer/LinePlotRenderer.js +1 -1
- package/esm/MultiDensityRenderer/MultiDensityRenderer.js +4 -7
- package/esm/MultiLineRenderer/MultiLineRenderer.js +4 -8
- package/esm/MultiLinearWiggleDisplay/components/SetColorDialog.js +2 -102
- package/esm/MultiLinearWiggleDisplay/components/SourcesGrid.d.ts +8 -0
- package/esm/MultiLinearWiggleDisplay/components/SourcesGrid.js +110 -0
- package/esm/MultiRowLineRenderer/MultiRowLineRenderer.js +4 -4
- package/esm/MultiRowXYPlotRenderer/MultiRowXYPlotRenderer.js +4 -8
- package/esm/MultiXYPlotRenderer/MultiXYPlotRenderer.js +4 -7
- package/esm/XYPlotRenderer/XYPlotRenderer.js +1 -1
- package/esm/drawDensity.d.ts +17 -0
- package/esm/drawDensity.js +62 -0
- package/esm/drawLine.d.ts +19 -0
- package/esm/drawLine.js +69 -0
- package/esm/drawXY.d.ts +19 -0
- package/esm/{drawxy.js → drawXY.js} +2 -136
- package/esm/util.d.ts +1 -0
- package/esm/util.js +15 -0
- package/package.json +2 -2
- package/dist/drawxy.d.ts +0 -49
- package/esm/drawxy.d.ts +0 -49
package/dist/drawLine.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.drawLine = void 0;
|
|
4
|
+
const configuration_1 = require("@jbrowse/core/configuration");
|
|
5
|
+
const util_1 = require("@jbrowse/core/util");
|
|
6
|
+
// locals
|
|
7
|
+
const util_2 = require("./util");
|
|
8
|
+
const fudgeFactor = 0.3;
|
|
9
|
+
const clipHeight = 2;
|
|
10
|
+
function drawLine(ctx, props) {
|
|
11
|
+
const { features, regions, bpPerPx, scaleOpts, height: unadjustedHeight, ticks: { values }, displayCrossHatches, colorCallback, config, offset = 0, } = props;
|
|
12
|
+
const [region] = regions;
|
|
13
|
+
const width = (region.end - region.start) / bpPerPx;
|
|
14
|
+
// the adjusted height takes into account YSCALEBAR_LABEL_OFFSET from the
|
|
15
|
+
// wiggle display, and makes the height of the actual drawn area add
|
|
16
|
+
// "padding" to the top and bottom of the display
|
|
17
|
+
const height = unadjustedHeight - offset * 2;
|
|
18
|
+
const clipColor = (0, configuration_1.readConfObject)(config, 'clipColor');
|
|
19
|
+
const scale = (0, util_2.getScale)({ ...scaleOpts, range: [0, height] });
|
|
20
|
+
const [niceMin, niceMax] = scale.domain();
|
|
21
|
+
const toY = (n) => (0, util_1.clamp)(height - (scale(n) || 0), 0, height) + offset;
|
|
22
|
+
let lastVal;
|
|
23
|
+
let prevLeftPx = -Infinity;
|
|
24
|
+
const reducedFeatures = [];
|
|
25
|
+
for (const feature of features.values()) {
|
|
26
|
+
const [leftPx, rightPx] = (0, util_1.featureSpanPx)(feature, region, bpPerPx);
|
|
27
|
+
// create reduced features, avoiding multiple features per px
|
|
28
|
+
if (Math.floor(leftPx) !== Math.floor(prevLeftPx)) {
|
|
29
|
+
reducedFeatures.push(feature);
|
|
30
|
+
prevLeftPx = leftPx;
|
|
31
|
+
}
|
|
32
|
+
const score = feature.get('score');
|
|
33
|
+
const lowClipping = score < niceMin;
|
|
34
|
+
const highClipping = score > niceMax;
|
|
35
|
+
const w = rightPx - leftPx + fudgeFactor;
|
|
36
|
+
const c = colorCallback(feature, score);
|
|
37
|
+
ctx.beginPath();
|
|
38
|
+
ctx.strokeStyle = c;
|
|
39
|
+
const startPos = lastVal !== undefined ? lastVal : score;
|
|
40
|
+
if (!region.reversed) {
|
|
41
|
+
ctx.moveTo(leftPx, toY(startPos));
|
|
42
|
+
ctx.lineTo(leftPx, toY(score));
|
|
43
|
+
ctx.lineTo(rightPx, toY(score));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
ctx.moveTo(rightPx, toY(startPos));
|
|
47
|
+
ctx.lineTo(rightPx, toY(score));
|
|
48
|
+
ctx.lineTo(leftPx, toY(score));
|
|
49
|
+
}
|
|
50
|
+
ctx.stroke();
|
|
51
|
+
lastVal = score;
|
|
52
|
+
if (highClipping) {
|
|
53
|
+
ctx.fillStyle = clipColor;
|
|
54
|
+
ctx.fillRect(leftPx, offset, w, clipHeight);
|
|
55
|
+
}
|
|
56
|
+
else if (lowClipping && scaleOpts.scaleType !== 'log') {
|
|
57
|
+
ctx.fillStyle = clipColor;
|
|
58
|
+
ctx.fillRect(leftPx, height - clipHeight, w, height);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (displayCrossHatches) {
|
|
62
|
+
ctx.lineWidth = 1;
|
|
63
|
+
ctx.strokeStyle = 'rgba(200,200,200,0.5)';
|
|
64
|
+
values.forEach(tick => {
|
|
65
|
+
ctx.beginPath();
|
|
66
|
+
ctx.moveTo(0, Math.round(toY(tick)));
|
|
67
|
+
ctx.lineTo(width, Math.round(toY(tick)));
|
|
68
|
+
ctx.stroke();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return { reducedFeatures };
|
|
72
|
+
}
|
|
73
|
+
exports.drawLine = drawLine;
|
package/dist/drawXY.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
2
|
+
import { Feature, Region } from '@jbrowse/core/util';
|
|
3
|
+
import { ScaleOpts } from './util';
|
|
4
|
+
export declare function drawXY(ctx: CanvasRenderingContext2D, props: {
|
|
5
|
+
features: Map<string, Feature> | Feature[];
|
|
6
|
+
bpPerPx: number;
|
|
7
|
+
regions: Region[];
|
|
8
|
+
scaleOpts: ScaleOpts;
|
|
9
|
+
height: number;
|
|
10
|
+
ticks: {
|
|
11
|
+
values: number[];
|
|
12
|
+
};
|
|
13
|
+
config: AnyConfigurationModel;
|
|
14
|
+
displayCrossHatches: boolean;
|
|
15
|
+
offset?: number;
|
|
16
|
+
colorCallback: (f: Feature, score: number) => string;
|
|
17
|
+
}): {
|
|
18
|
+
reducedFeatures: Feature[];
|
|
19
|
+
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.drawXY = void 0;
|
|
4
4
|
const configuration_1 = require("@jbrowse/core/configuration");
|
|
5
5
|
const colord_1 = require("@jbrowse/core/util/colord");
|
|
6
6
|
const util_1 = require("@jbrowse/core/util");
|
|
7
|
+
// locals
|
|
8
|
+
const util_2 = require("./util");
|
|
7
9
|
function lighten(color, amount) {
|
|
8
10
|
const hslColor = color.toHsl();
|
|
9
11
|
const l = hslColor.l * (1 + amount);
|
|
@@ -14,23 +16,6 @@ function darken(color, amount) {
|
|
|
14
16
|
const l = hslColor.l * (1 - amount);
|
|
15
17
|
return (0, colord_1.colord)({ ...hslColor, l: (0, util_1.clamp)(l, 0, 100) });
|
|
16
18
|
}
|
|
17
|
-
// locals
|
|
18
|
-
const util_2 = require("./util");
|
|
19
|
-
// avoid drawing negative width features for SVG exports
|
|
20
|
-
function fillRectCtx(x, y, width, height, ctx, color) {
|
|
21
|
-
if (width < 0) {
|
|
22
|
-
x += width;
|
|
23
|
-
width = -width;
|
|
24
|
-
}
|
|
25
|
-
if (height < 0) {
|
|
26
|
-
y += height;
|
|
27
|
-
height = -height;
|
|
28
|
-
}
|
|
29
|
-
if (color) {
|
|
30
|
-
ctx.fillStyle = color;
|
|
31
|
-
}
|
|
32
|
-
ctx.fillRect(x, y, width, height);
|
|
33
|
-
}
|
|
34
19
|
const fudgeFactor = 0.3;
|
|
35
20
|
const clipHeight = 2;
|
|
36
21
|
function drawXY(ctx, props) {
|
|
@@ -73,7 +58,7 @@ function drawXY(ctx, props) {
|
|
|
73
58
|
: c === lastCol
|
|
74
59
|
? lastMix
|
|
75
60
|
: (lastMix = lighten((0, colord_1.colord)(c), 0.4).toHex());
|
|
76
|
-
fillRectCtx(leftPx, toY(max), w, getHeight(max), ctx, effectiveC);
|
|
61
|
+
(0, util_2.fillRectCtx)(leftPx, toY(max), w, getHeight(max), ctx, effectiveC);
|
|
77
62
|
lastCol = c;
|
|
78
63
|
}
|
|
79
64
|
}
|
|
@@ -100,7 +85,7 @@ function drawXY(ctx, props) {
|
|
|
100
85
|
prevLeftPx = leftPx;
|
|
101
86
|
}
|
|
102
87
|
hasClipping = hasClipping || score < niceMin || score > niceMax;
|
|
103
|
-
fillRectCtx(leftPx, toY(score), w, getHeight(score), ctx, effectiveC);
|
|
88
|
+
(0, util_2.fillRectCtx)(leftPx, toY(score), w, getHeight(score), ctx, effectiveC);
|
|
104
89
|
lastCol = c;
|
|
105
90
|
}
|
|
106
91
|
lastMix = undefined;
|
|
@@ -116,7 +101,7 @@ function drawXY(ctx, props) {
|
|
|
116
101
|
: c === lastCol
|
|
117
102
|
? lastMix
|
|
118
103
|
: (lastMix = darken((0, colord_1.colord)(c), 0.4).toHex());
|
|
119
|
-
fillRectCtx(leftPx, toY(min), w, getHeight(min), ctx, effectiveC);
|
|
104
|
+
(0, util_2.fillRectCtx)(leftPx, toY(min), w, getHeight(min), ctx, effectiveC);
|
|
120
105
|
lastCol = c;
|
|
121
106
|
}
|
|
122
107
|
}
|
|
@@ -135,14 +120,14 @@ function drawXY(ctx, props) {
|
|
|
135
120
|
const w = Math.max(rightPx - leftPx + fudgeFactor, minSize);
|
|
136
121
|
if (summaryScoreMode === 'max') {
|
|
137
122
|
const s = feature.get('summary') ? feature.get('maxScore') : score;
|
|
138
|
-
fillRectCtx(leftPx, toY(s), w, getHeight(s), ctx, c);
|
|
123
|
+
(0, util_2.fillRectCtx)(leftPx, toY(s), w, getHeight(s), ctx, c);
|
|
139
124
|
}
|
|
140
125
|
else if (summaryScoreMode === 'min') {
|
|
141
126
|
const s = feature.get('summary') ? feature.get('minScore') : score;
|
|
142
|
-
fillRectCtx(leftPx, toY(s), w, getHeight(s), ctx, c);
|
|
127
|
+
(0, util_2.fillRectCtx)(leftPx, toY(s), w, getHeight(s), ctx, c);
|
|
143
128
|
}
|
|
144
129
|
else {
|
|
145
|
-
fillRectCtx(leftPx, toY(score), w, getHeight(score), ctx, c);
|
|
130
|
+
(0, util_2.fillRectCtx)(leftPx, toY(score), w, getHeight(score), ctx, c);
|
|
146
131
|
}
|
|
147
132
|
}
|
|
148
133
|
}
|
|
@@ -156,10 +141,10 @@ function drawXY(ctx, props) {
|
|
|
156
141
|
const w = rightPx - leftPx + fudgeFactor;
|
|
157
142
|
const score = feature.get('score');
|
|
158
143
|
if (score > niceMax) {
|
|
159
|
-
fillRectCtx(leftPx, offset, w, clipHeight, ctx);
|
|
144
|
+
(0, util_2.fillRectCtx)(leftPx, offset, w, clipHeight, ctx);
|
|
160
145
|
}
|
|
161
146
|
else if (score < niceMin && scaleOpts.scaleType !== 'log') {
|
|
162
|
-
fillRectCtx(leftPx, unadjustedHeight, w, clipHeight, ctx);
|
|
147
|
+
(0, util_2.fillRectCtx)(leftPx, unadjustedHeight, w, clipHeight, ctx);
|
|
163
148
|
}
|
|
164
149
|
}
|
|
165
150
|
}
|
|
@@ -177,124 +162,3 @@ function drawXY(ctx, props) {
|
|
|
177
162
|
return { reducedFeatures };
|
|
178
163
|
}
|
|
179
164
|
exports.drawXY = drawXY;
|
|
180
|
-
function drawLine(ctx, props) {
|
|
181
|
-
const { features, regions, bpPerPx, scaleOpts, height: unadjustedHeight, ticks: { values }, displayCrossHatches, colorCallback, config, offset = 0, } = props;
|
|
182
|
-
const [region] = regions;
|
|
183
|
-
const width = (region.end - region.start) / bpPerPx;
|
|
184
|
-
// the adjusted height takes into account YSCALEBAR_LABEL_OFFSET from the
|
|
185
|
-
// wiggle display, and makes the height of the actual drawn area add
|
|
186
|
-
// "padding" to the top and bottom of the display
|
|
187
|
-
const height = unadjustedHeight - offset * 2;
|
|
188
|
-
const clipColor = (0, configuration_1.readConfObject)(config, 'clipColor');
|
|
189
|
-
const scale = (0, util_2.getScale)({ ...scaleOpts, range: [0, height] });
|
|
190
|
-
const [niceMin, niceMax] = scale.domain();
|
|
191
|
-
const toY = (n) => (0, util_1.clamp)(height - (scale(n) || 0), 0, height) + offset;
|
|
192
|
-
let lastVal;
|
|
193
|
-
let prevLeftPx = -Infinity;
|
|
194
|
-
const reducedFeatures = [];
|
|
195
|
-
for (const feature of features.values()) {
|
|
196
|
-
const [leftPx, rightPx] = (0, util_1.featureSpanPx)(feature, region, bpPerPx);
|
|
197
|
-
// create reduced features, avoiding multiple features per px
|
|
198
|
-
if (Math.floor(leftPx) !== Math.floor(prevLeftPx)) {
|
|
199
|
-
reducedFeatures.push(feature);
|
|
200
|
-
prevLeftPx = leftPx;
|
|
201
|
-
}
|
|
202
|
-
const score = feature.get('score');
|
|
203
|
-
const lowClipping = score < niceMin;
|
|
204
|
-
const highClipping = score > niceMax;
|
|
205
|
-
const w = rightPx - leftPx + fudgeFactor;
|
|
206
|
-
const c = colorCallback(feature, score);
|
|
207
|
-
ctx.beginPath();
|
|
208
|
-
ctx.strokeStyle = c;
|
|
209
|
-
const startPos = lastVal !== undefined ? lastVal : score;
|
|
210
|
-
if (!region.reversed) {
|
|
211
|
-
ctx.moveTo(leftPx, toY(startPos));
|
|
212
|
-
ctx.lineTo(leftPx, toY(score));
|
|
213
|
-
ctx.lineTo(rightPx, toY(score));
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
ctx.moveTo(rightPx, toY(startPos));
|
|
217
|
-
ctx.lineTo(rightPx, toY(score));
|
|
218
|
-
ctx.lineTo(leftPx, toY(score));
|
|
219
|
-
}
|
|
220
|
-
ctx.stroke();
|
|
221
|
-
lastVal = score;
|
|
222
|
-
if (highClipping) {
|
|
223
|
-
ctx.fillStyle = clipColor;
|
|
224
|
-
ctx.fillRect(leftPx, offset, w, clipHeight);
|
|
225
|
-
}
|
|
226
|
-
else if (lowClipping && scaleOpts.scaleType !== 'log') {
|
|
227
|
-
ctx.fillStyle = clipColor;
|
|
228
|
-
ctx.fillRect(leftPx, height - clipHeight, w, height);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
if (displayCrossHatches) {
|
|
232
|
-
ctx.lineWidth = 1;
|
|
233
|
-
ctx.strokeStyle = 'rgba(200,200,200,0.5)';
|
|
234
|
-
values.forEach(tick => {
|
|
235
|
-
ctx.beginPath();
|
|
236
|
-
ctx.moveTo(0, Math.round(toY(tick)));
|
|
237
|
-
ctx.lineTo(width, Math.round(toY(tick)));
|
|
238
|
-
ctx.stroke();
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
return { reducedFeatures };
|
|
242
|
-
}
|
|
243
|
-
exports.drawLine = drawLine;
|
|
244
|
-
function drawDensity(ctx, props) {
|
|
245
|
-
const { features, regions, bpPerPx, scaleOpts, height, config } = props;
|
|
246
|
-
const [region] = regions;
|
|
247
|
-
const pivot = (0, configuration_1.readConfObject)(config, 'bicolorPivot');
|
|
248
|
-
const pivotValue = (0, configuration_1.readConfObject)(config, 'bicolorPivotValue');
|
|
249
|
-
const negColor = (0, configuration_1.readConfObject)(config, 'negColor');
|
|
250
|
-
const posColor = (0, configuration_1.readConfObject)(config, 'posColor');
|
|
251
|
-
const color = (0, configuration_1.readConfObject)(config, 'color');
|
|
252
|
-
const clipColor = (0, configuration_1.readConfObject)(config, 'clipColor');
|
|
253
|
-
const crossing = pivot !== 'none' && scaleOpts.scaleType !== 'log';
|
|
254
|
-
const scale = (0, util_2.getScale)({
|
|
255
|
-
...scaleOpts,
|
|
256
|
-
pivotValue: crossing ? pivotValue : undefined,
|
|
257
|
-
range: crossing ? [negColor, 'white', posColor] : ['white', posColor],
|
|
258
|
-
});
|
|
259
|
-
const scale2 = (0, util_2.getScale)({ ...scaleOpts, range: [0, height] });
|
|
260
|
-
const cb = color === '#f0f'
|
|
261
|
-
? (_, score) => scale(score)
|
|
262
|
-
: (feature, score) => (0, configuration_1.readConfObject)(config, 'color', { feature, score });
|
|
263
|
-
const [niceMin, niceMax] = scale2.domain();
|
|
264
|
-
let prevLeftPx = -Infinity;
|
|
265
|
-
let hasClipping = false;
|
|
266
|
-
const reducedFeatures = [];
|
|
267
|
-
for (const feature of features.values()) {
|
|
268
|
-
const [leftPx, rightPx] = (0, util_1.featureSpanPx)(feature, region, bpPerPx);
|
|
269
|
-
// create reduced features, avoiding multiple features per px
|
|
270
|
-
if (Math.floor(leftPx) !== Math.floor(prevLeftPx)) {
|
|
271
|
-
reducedFeatures.push(feature);
|
|
272
|
-
prevLeftPx = leftPx;
|
|
273
|
-
}
|
|
274
|
-
const score = feature.get('score');
|
|
275
|
-
hasClipping = hasClipping || score > niceMax || score < niceMin;
|
|
276
|
-
const w = rightPx - leftPx + fudgeFactor;
|
|
277
|
-
ctx.fillStyle = cb(feature, score);
|
|
278
|
-
ctx.fillRect(leftPx, 0, w, height);
|
|
279
|
-
}
|
|
280
|
-
// second pass: draw clipping
|
|
281
|
-
// avoid persisting the red fillstyle with save/restore
|
|
282
|
-
ctx.save();
|
|
283
|
-
if (hasClipping) {
|
|
284
|
-
ctx.fillStyle = clipColor;
|
|
285
|
-
for (const feature of features.values()) {
|
|
286
|
-
const [leftPx, rightPx] = (0, util_1.featureSpanPx)(feature, region, bpPerPx);
|
|
287
|
-
const w = rightPx - leftPx + fudgeFactor;
|
|
288
|
-
const score = feature.get('score');
|
|
289
|
-
if (score > niceMax) {
|
|
290
|
-
fillRectCtx(leftPx, 0, w, clipHeight, ctx);
|
|
291
|
-
}
|
|
292
|
-
else if (score < niceMin && scaleOpts.scaleType !== 'log') {
|
|
293
|
-
fillRectCtx(leftPx, 0, w, clipHeight, ctx);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
ctx.restore();
|
|
298
|
-
return { reducedFeatures };
|
|
299
|
-
}
|
|
300
|
-
exports.drawDensity = drawDensity;
|
package/dist/util.d.ts
CHANGED
|
@@ -72,3 +72,4 @@ export declare function quantitativeStatsAutorun(self: {
|
|
|
72
72
|
}): void;
|
|
73
73
|
export declare function toP(s?: number): number;
|
|
74
74
|
export declare function round(value: number): number;
|
|
75
|
+
export declare function fillRectCtx(x: number, y: number, width: number, height: number, ctx: CanvasRenderingContext2D, color?: string): void;
|
package/dist/util.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.round = exports.toP = exports.quantitativeStatsAutorun = exports.getQuantitativeStats = exports.getNiceDomain = exports.getOrigin = exports.getScale = exports.YSCALEBAR_LABEL_OFFSET = void 0;
|
|
3
|
+
exports.fillRectCtx = exports.round = exports.toP = exports.quantitativeStatsAutorun = exports.getQuantitativeStats = exports.getNiceDomain = exports.getOrigin = exports.getScale = exports.YSCALEBAR_LABEL_OFFSET = void 0;
|
|
4
4
|
const d3_scale_1 = require("d3-scale");
|
|
5
5
|
const mobx_1 = require("mobx");
|
|
6
6
|
const util_1 = require("@jbrowse/core/util");
|
|
@@ -231,3 +231,19 @@ function round(value) {
|
|
|
231
231
|
return Math.round(value * 1e5) / 1e5;
|
|
232
232
|
}
|
|
233
233
|
exports.round = round;
|
|
234
|
+
// avoid drawing negative width features for SVG exports
|
|
235
|
+
function fillRectCtx(x, y, width, height, ctx, color) {
|
|
236
|
+
if (width < 0) {
|
|
237
|
+
x += width;
|
|
238
|
+
width = -width;
|
|
239
|
+
}
|
|
240
|
+
if (height < 0) {
|
|
241
|
+
y += height;
|
|
242
|
+
height = -height;
|
|
243
|
+
}
|
|
244
|
+
if (color) {
|
|
245
|
+
ctx.fillStyle = color;
|
|
246
|
+
}
|
|
247
|
+
ctx.fillRect(x, y, width, height);
|
|
248
|
+
}
|
|
249
|
+
exports.fillRectCtx = fillRectCtx;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import WiggleBaseRenderer from '../WiggleBaseRenderer';
|
|
2
2
|
import { YSCALEBAR_LABEL_OFFSET } from '../util';
|
|
3
|
-
import { drawLine } from '../
|
|
3
|
+
import { drawLine } from '../drawLine';
|
|
4
4
|
export default class LinePlotRenderer extends WiggleBaseRenderer {
|
|
5
5
|
async draw(ctx, props) {
|
|
6
6
|
return drawLine(ctx, {
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import { groupBy } from '@jbrowse/core/util';
|
|
2
2
|
import WiggleBaseRenderer from '../WiggleBaseRenderer';
|
|
3
|
-
import { drawDensity } from '../
|
|
3
|
+
import { drawDensity } from '../drawDensity';
|
|
4
4
|
export default class MultiXYPlotRenderer extends WiggleBaseRenderer {
|
|
5
5
|
// @ts-expect-error
|
|
6
6
|
async draw(ctx, props) {
|
|
7
7
|
const { bpPerPx, sources, regions, features } = props;
|
|
8
8
|
const [region] = regions;
|
|
9
|
-
const groups = groupBy(
|
|
9
|
+
const groups = groupBy(features.values(), f => f.get('source'));
|
|
10
10
|
const height = props.height / sources.length;
|
|
11
11
|
const width = (region.end - region.start) / bpPerPx;
|
|
12
12
|
let feats = [];
|
|
13
13
|
ctx.save();
|
|
14
14
|
sources.forEach(source => {
|
|
15
|
-
const features = groups[source.name];
|
|
16
|
-
if (!features) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
15
|
+
const features = groups[source.name] || [];
|
|
19
16
|
const { reducedFeatures } = drawDensity(ctx, {
|
|
20
17
|
...props,
|
|
21
18
|
features,
|
|
@@ -27,7 +24,7 @@ export default class MultiXYPlotRenderer extends WiggleBaseRenderer {
|
|
|
27
24
|
ctx.lineTo(width, height);
|
|
28
25
|
ctx.stroke();
|
|
29
26
|
ctx.translate(0, height);
|
|
30
|
-
feats =
|
|
27
|
+
feats = feats.concat(reducedFeatures);
|
|
31
28
|
});
|
|
32
29
|
ctx.restore();
|
|
33
30
|
return { reducedFeatures: feats };
|
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import { groupBy } from '@jbrowse/core/util';
|
|
2
|
-
import { drawLine } from '../
|
|
2
|
+
import { drawLine } from '../drawLine';
|
|
3
3
|
import WiggleBaseRenderer from '../WiggleBaseRenderer';
|
|
4
4
|
export default class MultiLineRenderer extends WiggleBaseRenderer {
|
|
5
5
|
// @ts-expect-error
|
|
6
6
|
async draw(ctx, props) {
|
|
7
7
|
const { sources, features } = props;
|
|
8
|
-
const groups = groupBy(
|
|
8
|
+
const groups = groupBy(features.values(), f => f.get('source'));
|
|
9
9
|
let feats = [];
|
|
10
10
|
sources.forEach(source => {
|
|
11
|
-
const features = groups[source.name];
|
|
12
|
-
if (!features) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
11
|
const { reducedFeatures } = drawLine(ctx, {
|
|
16
12
|
...props,
|
|
17
|
-
features,
|
|
13
|
+
features: groups[source.name] || [],
|
|
18
14
|
colorCallback: () => source.color || 'blue',
|
|
19
15
|
});
|
|
20
|
-
feats =
|
|
16
|
+
feats = feats.concat(reducedFeatures);
|
|
21
17
|
});
|
|
22
18
|
return { reducedFeatures: feats };
|
|
23
19
|
}
|
|
@@ -1,28 +1,15 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { Button, DialogContent, DialogActions } from '@mui/material';
|
|
3
3
|
import { makeStyles } from 'tss-react/mui';
|
|
4
|
-
import {
|
|
5
|
-
import { DataGrid } from '@mui/x-data-grid';
|
|
4
|
+
import { useLocalStorage } from '@jbrowse/core/util';
|
|
6
5
|
import clone from 'clone';
|
|
7
6
|
// locals
|
|
8
7
|
import DraggableDialog from './DraggableDialog';
|
|
9
|
-
import
|
|
10
|
-
import { moveUp, moveDown } from './util';
|
|
11
|
-
// icons
|
|
12
|
-
import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp';
|
|
13
|
-
import KeyboardDoubleArrowDownIcon from '@mui/icons-material/KeyboardDoubleArrowDown';
|
|
14
|
-
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
|
15
|
-
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
|
16
|
-
import UriLink from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail/UriLink';
|
|
8
|
+
import SourcesGrid from './SourcesGrid';
|
|
17
9
|
const useStyles = makeStyles()({
|
|
18
10
|
content: {
|
|
19
11
|
minWidth: 800,
|
|
20
12
|
},
|
|
21
|
-
cell: {
|
|
22
|
-
whiteSpace: 'nowrap',
|
|
23
|
-
overflow: 'hidden',
|
|
24
|
-
textOverflow: 'ellipsis',
|
|
25
|
-
},
|
|
26
13
|
});
|
|
27
14
|
export default function SetColorDialog({ model, handleClose, }) {
|
|
28
15
|
const { classes } = useStyles();
|
|
@@ -58,90 +45,3 @@ export default function SetColorDialog({ model, handleClose, }) {
|
|
|
58
45
|
handleClose();
|
|
59
46
|
} }, "Submit"))));
|
|
60
47
|
}
|
|
61
|
-
function SourcesGrid({ rows, onChange, showTips, }) {
|
|
62
|
-
const { classes } = useStyles();
|
|
63
|
-
const [anchorEl, setAnchorEl] = useState(null);
|
|
64
|
-
const [selected, setSelected] = useState([]);
|
|
65
|
-
// @ts-expect-error
|
|
66
|
-
const { name: _name, color: _color, baseUri: _baseUri, ...rest } = rows[0];
|
|
67
|
-
const [widgetColor, setWidgetColor] = useState('blue');
|
|
68
|
-
const [currSort, setCurrSort] = useState({
|
|
69
|
-
idx: 0,
|
|
70
|
-
field: null,
|
|
71
|
-
});
|
|
72
|
-
return (React.createElement("div", null,
|
|
73
|
-
React.createElement(Button, { disabled: !selected.length, onClick: event => setAnchorEl(event.currentTarget) }, "Change color of selected items"),
|
|
74
|
-
React.createElement(Button, { onClick: () => onChange(moveUp([...rows], selected)), disabled: !selected.length },
|
|
75
|
-
React.createElement(KeyboardArrowUpIcon, null),
|
|
76
|
-
showTips ? 'Move selected items up' : null),
|
|
77
|
-
React.createElement(Button, { onClick: () => onChange(moveDown([...rows], selected)), disabled: !selected.length },
|
|
78
|
-
React.createElement(KeyboardArrowDownIcon, null),
|
|
79
|
-
showTips ? 'Move selected items down' : null),
|
|
80
|
-
React.createElement(Button, { onClick: () => onChange(moveUp([...rows], selected, rows.length)), disabled: !selected.length },
|
|
81
|
-
React.createElement(KeyboardDoubleArrowUpIcon, null),
|
|
82
|
-
showTips ? 'Move selected items to top' : null),
|
|
83
|
-
React.createElement(Button, { onClick: () => onChange(moveDown([...rows], selected, rows.length)), disabled: !selected.length },
|
|
84
|
-
React.createElement(KeyboardDoubleArrowDownIcon, null),
|
|
85
|
-
showTips ? 'Move selected items to bottom' : null),
|
|
86
|
-
React.createElement(ColorPopover, { anchorEl: anchorEl, color: widgetColor, onChange: c => {
|
|
87
|
-
setWidgetColor(c);
|
|
88
|
-
selected.forEach(id => {
|
|
89
|
-
const elt = rows.find(f => f.name === id);
|
|
90
|
-
if (elt) {
|
|
91
|
-
elt.color = c;
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
onChange([...rows]);
|
|
95
|
-
}, onClose: () => setAnchorEl(null) }),
|
|
96
|
-
React.createElement("div", { style: { height: 400, width: '100%' } },
|
|
97
|
-
React.createElement(DataGrid, { getRowId: row => row.name, checkboxSelection: true, disableRowSelectionOnClick: true, onRowSelectionModelChange: arg => setSelected(arg), rows: rows, rowHeight: 25, columnHeaderHeight: 33, columns: [
|
|
98
|
-
{
|
|
99
|
-
field: 'color',
|
|
100
|
-
headerName: 'Color',
|
|
101
|
-
renderCell: params => {
|
|
102
|
-
const { value, id } = params;
|
|
103
|
-
return (React.createElement(ColorPicker, { color: value || 'blue', onChange: c => {
|
|
104
|
-
const elt = rows.find(f => f.name === id);
|
|
105
|
-
if (elt) {
|
|
106
|
-
elt.color = c;
|
|
107
|
-
}
|
|
108
|
-
onChange([...rows]);
|
|
109
|
-
} }));
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
field: 'name',
|
|
114
|
-
sortingOrder: [null],
|
|
115
|
-
headerName: 'Name',
|
|
116
|
-
width: measureGridWidth(rows.map(r => r.name)),
|
|
117
|
-
},
|
|
118
|
-
...Object.keys(rest).map(val => ({
|
|
119
|
-
field: val,
|
|
120
|
-
sortingOrder: [null],
|
|
121
|
-
renderCell: (params) => {
|
|
122
|
-
const { value } = params;
|
|
123
|
-
return (React.createElement("div", { className: classes.cell }, isUriLocation(value) ? (React.createElement(UriLink, { value: value })) : (React.createElement(React.Fragment, null, getStr(value)))));
|
|
124
|
-
},
|
|
125
|
-
// @ts-ignore
|
|
126
|
-
width: measureGridWidth(rows.map(r => r[val])),
|
|
127
|
-
})),
|
|
128
|
-
], sortModel: [
|
|
129
|
-
/* we control the sort as a controlled component using onSortModelChange */
|
|
130
|
-
], onSortModelChange: args => {
|
|
131
|
-
const sort = args[0];
|
|
132
|
-
const idx = (currSort.idx + 1) % 2;
|
|
133
|
-
const field = (sort === null || sort === void 0 ? void 0 : sort.field) || currSort.field;
|
|
134
|
-
setCurrSort({ idx, field });
|
|
135
|
-
onChange(field
|
|
136
|
-
? [...rows].sort((a, b) => {
|
|
137
|
-
// @ts-expect-error
|
|
138
|
-
const aa = getStr(a[field]);
|
|
139
|
-
// @ts-expect-error
|
|
140
|
-
const bb = getStr(b[field]);
|
|
141
|
-
return idx === 1
|
|
142
|
-
? aa.localeCompare(bb)
|
|
143
|
-
: bb.localeCompare(aa);
|
|
144
|
-
})
|
|
145
|
-
: rows);
|
|
146
|
-
} }))));
|
|
147
|
-
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Button } from '@mui/material';
|
|
3
|
+
import { getStr, measureGridWidth } from '@jbrowse/core/util';
|
|
4
|
+
import { DataGrid } from '@mui/x-data-grid';
|
|
5
|
+
import { makeStyles } from 'tss-react/mui';
|
|
6
|
+
// locals
|
|
7
|
+
import ColorPicker, { ColorPopover } from '@jbrowse/core/ui/ColorPicker';
|
|
8
|
+
import { moveUp, moveDown } from './util';
|
|
9
|
+
// icons
|
|
10
|
+
import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp';
|
|
11
|
+
import KeyboardDoubleArrowDownIcon from '@mui/icons-material/KeyboardDoubleArrowDown';
|
|
12
|
+
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
|
13
|
+
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
|
14
|
+
import { SanitizedHTML } from '@jbrowse/core/ui';
|
|
15
|
+
const useStyles = makeStyles()({
|
|
16
|
+
cell: {
|
|
17
|
+
whiteSpace: 'nowrap',
|
|
18
|
+
overflow: 'hidden',
|
|
19
|
+
textOverflow: 'ellipsis',
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
function SourcesGrid({ rows, onChange, showTips, }) {
|
|
23
|
+
const { classes } = useStyles();
|
|
24
|
+
const [anchorEl, setAnchorEl] = useState(null);
|
|
25
|
+
const [selected, setSelected] = useState([]);
|
|
26
|
+
// @ts-expect-error
|
|
27
|
+
const { name: _name, color: _color, baseUri: _baseUri, ...rest } = rows[0];
|
|
28
|
+
const [widgetColor, setWidgetColor] = useState('blue');
|
|
29
|
+
const [currSort, setCurrSort] = useState({
|
|
30
|
+
idx: 0,
|
|
31
|
+
field: null,
|
|
32
|
+
});
|
|
33
|
+
return (React.createElement("div", null,
|
|
34
|
+
React.createElement(Button, { disabled: !selected.length, onClick: event => setAnchorEl(event.currentTarget) }, "Change color of selected items"),
|
|
35
|
+
React.createElement(Button, { onClick: () => onChange(moveUp([...rows], selected)), disabled: !selected.length },
|
|
36
|
+
React.createElement(KeyboardArrowUpIcon, null),
|
|
37
|
+
showTips ? 'Move selected items up' : null),
|
|
38
|
+
React.createElement(Button, { onClick: () => onChange(moveDown([...rows], selected)), disabled: !selected.length },
|
|
39
|
+
React.createElement(KeyboardArrowDownIcon, null),
|
|
40
|
+
showTips ? 'Move selected items down' : null),
|
|
41
|
+
React.createElement(Button, { onClick: () => onChange(moveUp([...rows], selected, rows.length)), disabled: !selected.length },
|
|
42
|
+
React.createElement(KeyboardDoubleArrowUpIcon, null),
|
|
43
|
+
showTips ? 'Move selected items to top' : null),
|
|
44
|
+
React.createElement(Button, { onClick: () => onChange(moveDown([...rows], selected, rows.length)), disabled: !selected.length },
|
|
45
|
+
React.createElement(KeyboardDoubleArrowDownIcon, null),
|
|
46
|
+
showTips ? 'Move selected items to bottom' : null),
|
|
47
|
+
React.createElement(ColorPopover, { anchorEl: anchorEl, color: widgetColor, onChange: c => {
|
|
48
|
+
setWidgetColor(c);
|
|
49
|
+
selected.forEach(id => {
|
|
50
|
+
const elt = rows.find(f => f.name === id);
|
|
51
|
+
if (elt) {
|
|
52
|
+
elt.color = c;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
onChange([...rows]);
|
|
56
|
+
}, onClose: () => setAnchorEl(null) }),
|
|
57
|
+
React.createElement("div", { style: { height: 400, width: '100%' } },
|
|
58
|
+
React.createElement(DataGrid, { getRowId: row => row.name, checkboxSelection: true, disableRowSelectionOnClick: true, onRowSelectionModelChange: arg => setSelected(arg), rows: rows, rowHeight: 25, columnHeaderHeight: 33, columns: [
|
|
59
|
+
{
|
|
60
|
+
field: 'color',
|
|
61
|
+
headerName: 'Color',
|
|
62
|
+
renderCell: params => {
|
|
63
|
+
const { value, id } = params;
|
|
64
|
+
return (React.createElement(ColorPicker, { color: value || 'blue', onChange: c => {
|
|
65
|
+
const elt = rows.find(f => f.name === id);
|
|
66
|
+
if (elt) {
|
|
67
|
+
elt.color = c;
|
|
68
|
+
}
|
|
69
|
+
onChange([...rows]);
|
|
70
|
+
} }));
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
field: 'name',
|
|
75
|
+
sortingOrder: [null],
|
|
76
|
+
headerName: 'Name',
|
|
77
|
+
width: measureGridWidth(rows.map(r => r.name)),
|
|
78
|
+
},
|
|
79
|
+
...Object.keys(rest).map(val => ({
|
|
80
|
+
field: val,
|
|
81
|
+
sortingOrder: [null],
|
|
82
|
+
renderCell: (params) => {
|
|
83
|
+
const { value } = params;
|
|
84
|
+
return (React.createElement("div", { className: classes.cell },
|
|
85
|
+
React.createElement(SanitizedHTML, { html: getStr(value) })));
|
|
86
|
+
},
|
|
87
|
+
// @ts-ignore
|
|
88
|
+
width: measureGridWidth(rows.map(r => r[val])),
|
|
89
|
+
})),
|
|
90
|
+
], sortModel: [
|
|
91
|
+
/* we control the sort as a controlled component using onSortModelChange */
|
|
92
|
+
], onSortModelChange: args => {
|
|
93
|
+
const sort = args[0];
|
|
94
|
+
const idx = (currSort.idx + 1) % 2;
|
|
95
|
+
const field = (sort === null || sort === void 0 ? void 0 : sort.field) || currSort.field;
|
|
96
|
+
setCurrSort({ idx, field });
|
|
97
|
+
onChange(field
|
|
98
|
+
? [...rows].sort((a, b) => {
|
|
99
|
+
// @ts-expect-error
|
|
100
|
+
const aa = getStr(a[field]);
|
|
101
|
+
// @ts-expect-error
|
|
102
|
+
const bb = getStr(b[field]);
|
|
103
|
+
return idx === 1
|
|
104
|
+
? aa.localeCompare(bb)
|
|
105
|
+
: bb.localeCompare(aa);
|
|
106
|
+
})
|
|
107
|
+
: rows);
|
|
108
|
+
} }))));
|
|
109
|
+
}
|
|
110
|
+
export default SourcesGrid;
|