oncoprintjs 6.1.4 → 6.1.5
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/.turbo/turbo-build.log +3 -3
- package/dist/index.es.js +31577 -7586
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +31578 -7588
- package/dist/index.js.map +1 -1
- package/dist/js/oncoprintmodel.d.ts +13 -0
- package/dist/js/oncoprintruleset.d.ts +1 -0
- package/dist/js/oncoprinttrackoptionsview.d.ts +1 -0
- package/package.json +2 -2
- package/src/js/oncoprintmodel.ts +54 -9
- package/src/js/oncoprintruleset.ts +80 -21
- package/src/js/oncoprinttrackoptionsview.ts +167 -7
- package/src/js/oncoprintwebglcellview.ts +6 -2
|
@@ -58,6 +58,7 @@ export type CustomTrackOption = {
|
|
|
58
58
|
weight?: string;
|
|
59
59
|
disabled?: boolean;
|
|
60
60
|
gapLabelsFn?: (model: OncoprintModel) => OncoprintGapConfig[];
|
|
61
|
+
children?: CustomTrackOption[];
|
|
61
62
|
};
|
|
62
63
|
export type CustomTrackGroupOption = {
|
|
63
64
|
label?: string;
|
|
@@ -103,6 +104,10 @@ export type UserTrackSpec<D> = {
|
|
|
103
104
|
$track_info_tooltip_elt?: JQuery;
|
|
104
105
|
track_can_show_gaps?: boolean;
|
|
105
106
|
show_gaps_on_init?: boolean;
|
|
107
|
+
on_move_up?: () => void;
|
|
108
|
+
on_move_down?: () => void;
|
|
109
|
+
move_up_disabled?: boolean;
|
|
110
|
+
move_down_disabled?: boolean;
|
|
106
111
|
};
|
|
107
112
|
export type LibraryTrackSpec<D> = UserTrackSpec<D> & {
|
|
108
113
|
rule_set: RuleSet;
|
|
@@ -198,6 +203,10 @@ export default class OncoprintModel {
|
|
|
198
203
|
private track_removable;
|
|
199
204
|
private track_remove_callback;
|
|
200
205
|
private track_remove_option_callback;
|
|
206
|
+
private track_on_move_up;
|
|
207
|
+
private track_on_move_down;
|
|
208
|
+
private track_move_up_disabled;
|
|
209
|
+
private track_move_down_disabled;
|
|
201
210
|
private track_sort_cmp_fn;
|
|
202
211
|
private track_sort_direction_changeable;
|
|
203
212
|
private track_sort_direction;
|
|
@@ -365,6 +374,10 @@ export default class OncoprintModel {
|
|
|
365
374
|
getTrackGroupPadding(base?: boolean): number;
|
|
366
375
|
isTrackRemovable(track_id: TrackId): boolean;
|
|
367
376
|
getTrackRemoveOptionCallback(track_id: TrackId): (track_id: number) => void;
|
|
377
|
+
getTrackOnMoveUp(track_id: TrackId): () => void;
|
|
378
|
+
getTrackOnMoveDown(track_id: TrackId): () => void;
|
|
379
|
+
isTrackMoveUpDisabled(track_id: TrackId): boolean;
|
|
380
|
+
isTrackMoveDownDisabled(track_id: TrackId): boolean;
|
|
368
381
|
isTrackSortDirectionChangeable(track_id: TrackId): boolean;
|
|
369
382
|
isTrackExpandable(track_id: TrackId): boolean;
|
|
370
383
|
expandTrack(track_id: TrackId): void;
|
|
@@ -58,6 +58,7 @@ export interface IStackedBarRuleSetParams extends IGeneralRuleSetParams {
|
|
|
58
58
|
value_key: string;
|
|
59
59
|
categories: string[];
|
|
60
60
|
fills?: RGBAColor[];
|
|
61
|
+
max_total?: number;
|
|
61
62
|
}
|
|
62
63
|
export interface IGeneticAlterationRuleSetParams extends IGeneralRuleSetParams {
|
|
63
64
|
type: RuleSetType.GENE;
|
|
@@ -31,6 +31,7 @@ export default class OncoprintTrackOptionsView {
|
|
|
31
31
|
private hideMenusExcept;
|
|
32
32
|
private static $makeDropdownOption;
|
|
33
33
|
private static $makeDropdownSeparator;
|
|
34
|
+
private static $makeDropdownSubmenuOption;
|
|
34
35
|
private static renderSortArrow;
|
|
35
36
|
private renderTrackOptions;
|
|
36
37
|
enableInteraction(): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oncoprintjs",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.5",
|
|
4
4
|
"description": "A data visualization for cancer genomic data.",
|
|
5
5
|
"types": "./dist/js/oncoprint.d.ts",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"tayden-clusterfck": "^0.7.0",
|
|
58
58
|
"typescript": "4.0.3"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "a7270762c0180a3e41ebce361df3c1417072c72d"
|
|
61
61
|
}
|
package/src/js/oncoprintmodel.ts
CHANGED
|
@@ -79,6 +79,9 @@ export type CustomTrackOption = {
|
|
|
79
79
|
weight?: string;
|
|
80
80
|
disabled?: boolean;
|
|
81
81
|
gapLabelsFn?: (model: OncoprintModel) => OncoprintGapConfig[];
|
|
82
|
+
// When set, this option is a parent item: hovering shows a submenu of the
|
|
83
|
+
// nested options. Mutually exclusive with onClick.
|
|
84
|
+
children?: CustomTrackOption[];
|
|
82
85
|
};
|
|
83
86
|
export type CustomTrackGroupOption = {
|
|
84
87
|
label?: string;
|
|
@@ -124,6 +127,12 @@ export type UserTrackSpec<D> = {
|
|
|
124
127
|
$track_info_tooltip_elt?: JQuery;
|
|
125
128
|
track_can_show_gaps?: boolean;
|
|
126
129
|
show_gaps_on_init?: boolean;
|
|
130
|
+
// Optional overrides for Move up / Move down: a callback fully replaces the
|
|
131
|
+
// default within-group move; the disabled flags gray out the item.
|
|
132
|
+
on_move_up?: () => void;
|
|
133
|
+
on_move_down?: () => void;
|
|
134
|
+
move_up_disabled?: boolean;
|
|
135
|
+
move_down_disabled?: boolean;
|
|
127
136
|
};
|
|
128
137
|
export type LibraryTrackSpec<D> = UserTrackSpec<D> & {
|
|
129
138
|
rule_set: RuleSet;
|
|
@@ -305,6 +314,10 @@ export default class OncoprintModel {
|
|
|
305
314
|
private track_remove_option_callback: TrackProp<
|
|
306
315
|
(track_id: TrackId) => void
|
|
307
316
|
>;
|
|
317
|
+
private track_on_move_up: TrackProp<(() => void) | undefined>;
|
|
318
|
+
private track_on_move_down: TrackProp<(() => void) | undefined>;
|
|
319
|
+
private track_move_up_disabled: TrackProp<boolean>;
|
|
320
|
+
private track_move_down_disabled: TrackProp<boolean>;
|
|
308
321
|
private track_sort_cmp_fn: TrackProp<TrackSortSpecification<Datum>>;
|
|
309
322
|
private track_sort_direction_changeable: TrackProp<boolean>;
|
|
310
323
|
private track_sort_direction: TrackProp<TrackSortDirection>;
|
|
@@ -416,6 +429,10 @@ export default class OncoprintModel {
|
|
|
416
429
|
this.track_removable = {};
|
|
417
430
|
this.track_remove_callback = {};
|
|
418
431
|
this.track_remove_option_callback = {};
|
|
432
|
+
this.track_on_move_up = {};
|
|
433
|
+
this.track_on_move_down = {};
|
|
434
|
+
this.track_move_up_disabled = {};
|
|
435
|
+
this.track_move_down_disabled = {};
|
|
419
436
|
this.track_sort_cmp_fn = {};
|
|
420
437
|
this.track_sort_direction_changeable = {};
|
|
421
438
|
this.track_sort_direction = {}; // 1: ascending, -1: descending, 0: not
|
|
@@ -1142,11 +1159,11 @@ export default class OncoprintModel {
|
|
|
1142
1159
|
) {
|
|
1143
1160
|
return self.track_rule_set_id[track_id];
|
|
1144
1161
|
});
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
);
|
|
1162
|
+
// Dedupe numeric IDs directly to avoid the core-js parseInt polyfill's
|
|
1163
|
+
// per-call trim(), which dominated chart-type-switch cost on large studies.
|
|
1164
|
+
const unique_rule_set_ids = Array.from(new Set(rule_set_ids));
|
|
1148
1165
|
return unique_rule_set_ids.map(function(rule_set_id) {
|
|
1149
|
-
return self.rule_sets[
|
|
1166
|
+
return self.rule_sets[rule_set_id];
|
|
1150
1167
|
});
|
|
1151
1168
|
}
|
|
1152
1169
|
|
|
@@ -1375,6 +1392,10 @@ export default class OncoprintModel {
|
|
|
1375
1392
|
const params = params_list[i];
|
|
1376
1393
|
this.addTrack(params);
|
|
1377
1394
|
}
|
|
1395
|
+
// Update track_tops synchronously before the (un-awaited) sort below, so
|
|
1396
|
+
// views reading getZoomedTrackTops() for the new track don't get
|
|
1397
|
+
// `undefined` and compute NaN heights — i.e. a zero-height canvas.
|
|
1398
|
+
this.track_tops.update();
|
|
1378
1399
|
if (this.rendering_suppressed_depth === 0) {
|
|
1379
1400
|
if (this.keep_sorted) {
|
|
1380
1401
|
await this.sort();
|
|
@@ -1424,6 +1445,16 @@ export default class OncoprintModel {
|
|
|
1424
1445
|
this.track_remove_option_callback[
|
|
1425
1446
|
track_id
|
|
1426
1447
|
] = ifndef(params.onClickRemoveInTrackMenu, function() {});
|
|
1448
|
+
this.track_on_move_up[track_id] = params.on_move_up;
|
|
1449
|
+
this.track_on_move_down[track_id] = params.on_move_down;
|
|
1450
|
+
this.track_move_up_disabled[track_id] = ifndef(
|
|
1451
|
+
params.move_up_disabled,
|
|
1452
|
+
false
|
|
1453
|
+
);
|
|
1454
|
+
this.track_move_down_disabled[track_id] = ifndef(
|
|
1455
|
+
params.move_down_disabled,
|
|
1456
|
+
false
|
|
1457
|
+
);
|
|
1427
1458
|
|
|
1428
1459
|
if (typeof params.expandCallback !== 'undefined') {
|
|
1429
1460
|
this.track_expand_callback[track_id] = params.expandCallback;
|
|
@@ -1493,7 +1524,8 @@ export default class OncoprintModel {
|
|
|
1493
1524
|
const group_arrays = [this.track_groups[params.target_group].tracks];
|
|
1494
1525
|
if (
|
|
1495
1526
|
this.sort_config.type === 'cluster' &&
|
|
1496
|
-
this.sort_config.track_group_index === params.target_group
|
|
1527
|
+
this.sort_config.track_group_index === params.target_group &&
|
|
1528
|
+
this.unclustered_track_group_order
|
|
1497
1529
|
) {
|
|
1498
1530
|
// if target group is clustered, also add track to unclustered order
|
|
1499
1531
|
group_arrays.push(this.unclustered_track_group_order);
|
|
@@ -1633,6 +1665,10 @@ export default class OncoprintModel {
|
|
|
1633
1665
|
delete this.track_movable[track_id];
|
|
1634
1666
|
delete this.track_removable[track_id];
|
|
1635
1667
|
delete this.track_remove_callback[track_id];
|
|
1668
|
+
delete this.track_on_move_up[track_id];
|
|
1669
|
+
delete this.track_on_move_down[track_id];
|
|
1670
|
+
delete this.track_move_up_disabled[track_id];
|
|
1671
|
+
delete this.track_move_down_disabled[track_id];
|
|
1636
1672
|
delete this.track_sort_cmp_fn[track_id];
|
|
1637
1673
|
delete this.track_sort_direction_changeable[track_id];
|
|
1638
1674
|
delete this.track_sort_direction[track_id];
|
|
@@ -1737,10 +1773,7 @@ export default class OncoprintModel {
|
|
|
1737
1773
|
// Binary search failed (tracks out of order) - linear fallback
|
|
1738
1774
|
for (let t = 0; t < tracks.length; t++) {
|
|
1739
1775
|
const top = cell_tops[tracks[t]];
|
|
1740
|
-
if (
|
|
1741
|
-
y >= top &&
|
|
1742
|
-
y < top + this.getCellHeight(tracks[t])
|
|
1743
|
-
) {
|
|
1776
|
+
if (y >= top && y < top + this.getCellHeight(tracks[t])) {
|
|
1744
1777
|
nearest_track = tracks[t];
|
|
1745
1778
|
break;
|
|
1746
1779
|
}
|
|
@@ -2082,6 +2115,18 @@ export default class OncoprintModel {
|
|
|
2082
2115
|
public getTrackRemoveOptionCallback(track_id: TrackId) {
|
|
2083
2116
|
return this.track_remove_option_callback[track_id];
|
|
2084
2117
|
}
|
|
2118
|
+
public getTrackOnMoveUp(track_id: TrackId) {
|
|
2119
|
+
return this.track_on_move_up[track_id];
|
|
2120
|
+
}
|
|
2121
|
+
public getTrackOnMoveDown(track_id: TrackId) {
|
|
2122
|
+
return this.track_on_move_down[track_id];
|
|
2123
|
+
}
|
|
2124
|
+
public isTrackMoveUpDisabled(track_id: TrackId) {
|
|
2125
|
+
return this.track_move_up_disabled[track_id] === true;
|
|
2126
|
+
}
|
|
2127
|
+
public isTrackMoveDownDisabled(track_id: TrackId) {
|
|
2128
|
+
return this.track_move_down_disabled[track_id] === true;
|
|
2129
|
+
}
|
|
2085
2130
|
|
|
2086
2131
|
public isTrackSortDirectionChangeable(track_id: TrackId) {
|
|
2087
2132
|
return this.track_sort_direction_changeable[track_id];
|
|
@@ -116,6 +116,10 @@ export interface IStackedBarRuleSetParams extends IGeneralRuleSetParams {
|
|
|
116
116
|
value_key: string;
|
|
117
117
|
categories: string[];
|
|
118
118
|
fills?: RGBAColor[];
|
|
119
|
+
// When set, bar heights scale against this constant instead of each datum's
|
|
120
|
+
// own total, so height reflects absolute magnitude rather than per-sample
|
|
121
|
+
// composition. Typically max(sum(datum values)) across all data.
|
|
122
|
+
max_total?: number;
|
|
119
123
|
}
|
|
120
124
|
|
|
121
125
|
export interface IGeneticAlterationRuleSetParams extends IGeneralRuleSetParams {
|
|
@@ -1169,6 +1173,7 @@ class StackedBarRuleSet extends ConditionRuleSet {
|
|
|
1169
1173
|
const value_key = params.value_key;
|
|
1170
1174
|
const fills = params.fills || [];
|
|
1171
1175
|
const categories = params.categories || [];
|
|
1176
|
+
const max_total = params.max_total;
|
|
1172
1177
|
const getUnusedColor = makeUniqueColorGetter(fills.map(rgbaToHex));
|
|
1173
1178
|
|
|
1174
1179
|
// Initialize with default values
|
|
@@ -1196,41 +1201,95 @@ class StackedBarRuleSet extends ConditionRuleSet {
|
|
|
1196
1201
|
fill: fills[I],
|
|
1197
1202
|
width: 100,
|
|
1198
1203
|
height: function(d) {
|
|
1199
|
-
var
|
|
1204
|
+
var denom;
|
|
1205
|
+
if (max_total) {
|
|
1206
|
+
denom = max_total;
|
|
1207
|
+
return (
|
|
1208
|
+
(+d[value_key][categories[I]] *
|
|
1209
|
+
100) /
|
|
1210
|
+
denom
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
// Composition mode: rows fill the full cell
|
|
1214
|
+
// height. Snap the last rect to
|
|
1215
|
+
// "100 - prev_sum_pct" so float drift in the
|
|
1216
|
+
// fractions never leaves a gap at the bottom.
|
|
1217
|
+
denom = 0;
|
|
1200
1218
|
for (
|
|
1201
1219
|
var j = 0;
|
|
1202
1220
|
j < categories.length;
|
|
1203
1221
|
j++
|
|
1204
1222
|
) {
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1223
|
+
denom += +d[value_key][categories[j]];
|
|
1224
|
+
}
|
|
1225
|
+
if (denom === 0) {
|
|
1226
|
+
return 0;
|
|
1227
|
+
}
|
|
1228
|
+
if (I === categories.length - 1) {
|
|
1229
|
+
var prev_pct = 0;
|
|
1230
|
+
for (var k = 0; k < I; k++) {
|
|
1231
|
+
prev_pct +=
|
|
1232
|
+
(+d[value_key][categories[k]] *
|
|
1233
|
+
100) /
|
|
1234
|
+
denom;
|
|
1235
|
+
}
|
|
1236
|
+
return 100 - prev_pct;
|
|
1208
1237
|
}
|
|
1209
1238
|
return (
|
|
1210
|
-
(
|
|
1211
|
-
|
|
1212
|
-
) *
|
|
1213
|
-
100) /
|
|
1214
|
-
total
|
|
1239
|
+
(+d[value_key][categories[I]] * 100) /
|
|
1240
|
+
denom
|
|
1215
1241
|
);
|
|
1216
1242
|
},
|
|
1217
1243
|
y: function(d) {
|
|
1218
|
-
var
|
|
1244
|
+
var denom;
|
|
1219
1245
|
var prev_vals_sum = 0;
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
var
|
|
1226
|
-
|
|
1246
|
+
if (max_total) {
|
|
1247
|
+
// Absolute mode: anchor bars to the
|
|
1248
|
+
// bottom baseline. empty_pct is the
|
|
1249
|
+
// whitespace above a short bar.
|
|
1250
|
+
denom = max_total;
|
|
1251
|
+
var total = 0;
|
|
1252
|
+
for (
|
|
1253
|
+
var j = 0;
|
|
1254
|
+
j < categories.length;
|
|
1255
|
+
j++
|
|
1256
|
+
) {
|
|
1257
|
+
total += +d[value_key][
|
|
1258
|
+
categories[j]
|
|
1259
|
+
];
|
|
1260
|
+
}
|
|
1261
|
+
var empty_pct =
|
|
1262
|
+
((max_total - total) * 100) /
|
|
1263
|
+
max_total;
|
|
1264
|
+
for (var j = 0; j < I; j++) {
|
|
1265
|
+
prev_vals_sum += +d[value_key][
|
|
1266
|
+
categories[j]
|
|
1267
|
+
];
|
|
1268
|
+
}
|
|
1269
|
+
return (
|
|
1270
|
+
empty_pct +
|
|
1271
|
+
(prev_vals_sum * 100) / denom
|
|
1227
1272
|
);
|
|
1228
|
-
|
|
1229
|
-
|
|
1273
|
+
} else {
|
|
1274
|
+
denom = 0;
|
|
1275
|
+
for (
|
|
1276
|
+
var j = 0;
|
|
1277
|
+
j < categories.length;
|
|
1278
|
+
j++
|
|
1279
|
+
) {
|
|
1280
|
+
var new_val = +d[value_key][
|
|
1281
|
+
categories[j]
|
|
1282
|
+
];
|
|
1283
|
+
if (j < I) {
|
|
1284
|
+
prev_vals_sum += new_val;
|
|
1285
|
+
}
|
|
1286
|
+
denom += new_val;
|
|
1287
|
+
}
|
|
1288
|
+
if (denom === 0) {
|
|
1289
|
+
return 0;
|
|
1230
1290
|
}
|
|
1231
|
-
|
|
1291
|
+
return (prev_vals_sum * 100) / denom;
|
|
1232
1292
|
}
|
|
1233
|
-
return (prev_vals_sum * 100) / total;
|
|
1234
1293
|
},
|
|
1235
1294
|
},
|
|
1236
1295
|
],
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import $ from 'jquery';
|
|
2
2
|
import menuDotsIcon from '../img/menudots.svg';
|
|
3
3
|
import OncoprintModel, {
|
|
4
|
+
CustomTrackOption,
|
|
4
5
|
GAP_MODE_ENUM,
|
|
5
6
|
TrackId,
|
|
6
7
|
TrackProp,
|
|
@@ -176,12 +177,31 @@ export default class OncoprintTrackOptionsView {
|
|
|
176
177
|
'font-weight': weight,
|
|
177
178
|
'font-size': 12,
|
|
178
179
|
'border-bottom': '1px solid rgba(0,0,0,0.3)',
|
|
180
|
+
// Disables the iOS 300ms tap delay and lets click fire on first tap.
|
|
181
|
+
'touch-action': 'manipulation',
|
|
179
182
|
});
|
|
180
183
|
if (!disabled) {
|
|
181
184
|
if (callback) {
|
|
182
185
|
li.addClass('clickable');
|
|
183
186
|
li.css({ cursor: 'pointer' });
|
|
184
|
-
|
|
187
|
+
// Also fire on touchend so taps work when the synthetic click
|
|
188
|
+
// doesn't arrive (sticky hover on iOS). preventDefault on the
|
|
189
|
+
// touch stops the follow-up click so the callback runs once.
|
|
190
|
+
let fired = false;
|
|
191
|
+
const invoke = function(evt: any) {
|
|
192
|
+
if (fired) return;
|
|
193
|
+
fired = true;
|
|
194
|
+
setTimeout(() => {
|
|
195
|
+
fired = false;
|
|
196
|
+
}, 400);
|
|
197
|
+
callback(evt);
|
|
198
|
+
};
|
|
199
|
+
li.on('click', invoke);
|
|
200
|
+
li.on('touchend', function(evt) {
|
|
201
|
+
evt.preventDefault();
|
|
202
|
+
invoke(evt);
|
|
203
|
+
});
|
|
204
|
+
li.hover(
|
|
185
205
|
function() {
|
|
186
206
|
$(this).css({ 'background-color': 'rgb(200,200,200)' });
|
|
187
207
|
},
|
|
@@ -209,6 +229,110 @@ export default class OncoprintTrackOptionsView {
|
|
|
209
229
|
.addClass(SEPARATOR_CLASS);
|
|
210
230
|
}
|
|
211
231
|
|
|
232
|
+
// Hover-expanding parent item that reveals a floating submenu with leaf
|
|
233
|
+
// options. Used for nested "Sort by > <category>" style menus without
|
|
234
|
+
// cluttering the top-level dropdown.
|
|
235
|
+
private static $makeDropdownSubmenuOption(
|
|
236
|
+
text: string,
|
|
237
|
+
children: CustomTrackOption[],
|
|
238
|
+
track_id: TrackId,
|
|
239
|
+
closeOuterMenu: () => void
|
|
240
|
+
) {
|
|
241
|
+
const $li = $('<li>')
|
|
242
|
+
.text(text + ' \u25B8')
|
|
243
|
+
.css({
|
|
244
|
+
'font-weight': 'normal',
|
|
245
|
+
'font-size': 12,
|
|
246
|
+
'border-bottom': '1px solid rgba(0,0,0,0.3)',
|
|
247
|
+
cursor: 'default',
|
|
248
|
+
position: 'relative',
|
|
249
|
+
})
|
|
250
|
+
.addClass('has-submenu');
|
|
251
|
+
const $submenu = $('<ul>')
|
|
252
|
+
.appendTo($li)
|
|
253
|
+
.css({
|
|
254
|
+
position: 'absolute',
|
|
255
|
+
left: '100%',
|
|
256
|
+
top: 0,
|
|
257
|
+
display: 'none',
|
|
258
|
+
'list-style-type': 'none',
|
|
259
|
+
padding: 0,
|
|
260
|
+
margin: 0,
|
|
261
|
+
'padding-left': '6px',
|
|
262
|
+
'padding-right': '6px',
|
|
263
|
+
'background-color': 'rgb(255,255,255)',
|
|
264
|
+
border: '1px solid rgba(0,0,0,0.2)',
|
|
265
|
+
'z-index': 100,
|
|
266
|
+
'min-width': '140px',
|
|
267
|
+
});
|
|
268
|
+
let clickedOpen = false;
|
|
269
|
+
for (const child of children) {
|
|
270
|
+
if (child.separator) {
|
|
271
|
+
$submenu.append(
|
|
272
|
+
OncoprintTrackOptionsView.$makeDropdownSeparator()
|
|
273
|
+
);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
$submenu.append(
|
|
277
|
+
OncoprintTrackOptionsView.$makeDropdownOption(
|
|
278
|
+
child.label || '',
|
|
279
|
+
child.weight || 'normal',
|
|
280
|
+
child.disabled,
|
|
281
|
+
child.onClick &&
|
|
282
|
+
function(evt) {
|
|
283
|
+
evt.stopPropagation();
|
|
284
|
+
clickedOpen = false;
|
|
285
|
+
$submenu.hide();
|
|
286
|
+
child.onClick!(track_id);
|
|
287
|
+
closeOuterMenu();
|
|
288
|
+
}
|
|
289
|
+
)
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
$li.hover(
|
|
293
|
+
function() {
|
|
294
|
+
$(this).css({ 'background-color': 'rgb(200,200,200)' });
|
|
295
|
+
$submenu.show();
|
|
296
|
+
},
|
|
297
|
+
function() {
|
|
298
|
+
$(this).css({ 'background-color': 'rgba(255,255,255,0)' });
|
|
299
|
+
if (!clickedOpen) $submenu.hide();
|
|
300
|
+
}
|
|
301
|
+
);
|
|
302
|
+
// Touch devices have no hover — tapping the parent toggles the submenu
|
|
303
|
+
// and keeps it open until a child is picked or the outer menu closes.
|
|
304
|
+
$li.css({ 'touch-action': 'manipulation' });
|
|
305
|
+
let parentFired = false;
|
|
306
|
+
const toggleSubmenu = function(evt: any) {
|
|
307
|
+
if (
|
|
308
|
+
$(evt.target)
|
|
309
|
+
.closest('ul')
|
|
310
|
+
.is($submenu)
|
|
311
|
+
)
|
|
312
|
+
return;
|
|
313
|
+
if (parentFired) return;
|
|
314
|
+
parentFired = true;
|
|
315
|
+
setTimeout(() => {
|
|
316
|
+
parentFired = false;
|
|
317
|
+
}, 400);
|
|
318
|
+
evt.stopPropagation();
|
|
319
|
+
clickedOpen = !clickedOpen;
|
|
320
|
+
$submenu.toggle(clickedOpen);
|
|
321
|
+
};
|
|
322
|
+
$li.on('click', toggleSubmenu);
|
|
323
|
+
$li.on('touchend', function(evt) {
|
|
324
|
+
if (
|
|
325
|
+
$(evt.target)
|
|
326
|
+
.closest('ul')
|
|
327
|
+
.is($submenu)
|
|
328
|
+
)
|
|
329
|
+
return;
|
|
330
|
+
evt.preventDefault();
|
|
331
|
+
toggleSubmenu(evt);
|
|
332
|
+
});
|
|
333
|
+
return $li;
|
|
334
|
+
}
|
|
335
|
+
|
|
212
336
|
// 11/2/2023 we are removing sort arrow
|
|
213
337
|
// leaving commented out if it needs to be restored based on complaint
|
|
214
338
|
private static renderSortArrow(
|
|
@@ -302,7 +426,14 @@ export default class OncoprintTrackOptionsView {
|
|
|
302
426
|
}
|
|
303
427
|
}
|
|
304
428
|
);
|
|
305
|
-
$img.
|
|
429
|
+
$img.css({ 'touch-action': 'manipulation' });
|
|
430
|
+
let imgFired = false;
|
|
431
|
+
const toggleImgMenu = function(evt: any) {
|
|
432
|
+
if (imgFired) return;
|
|
433
|
+
imgFired = true;
|
|
434
|
+
setTimeout(() => {
|
|
435
|
+
imgFired = false;
|
|
436
|
+
}, 400);
|
|
306
437
|
evt.stopPropagation();
|
|
307
438
|
if ($dropdown.is(':visible')) {
|
|
308
439
|
$img.addClass(TOGGLE_BTN_OPEN_CLASS);
|
|
@@ -312,21 +443,40 @@ export default class OncoprintTrackOptionsView {
|
|
|
312
443
|
self.showTrackMenu(track_id);
|
|
313
444
|
}
|
|
314
445
|
self.hideMenusExcept(track_id);
|
|
446
|
+
};
|
|
447
|
+
$img.on('click', toggleImgMenu);
|
|
448
|
+
$img.on('touchend', function(evt) {
|
|
449
|
+
evt.preventDefault();
|
|
450
|
+
toggleImgMenu(evt);
|
|
315
451
|
});
|
|
316
452
|
|
|
453
|
+
// Gray out Move up/down only when the track belongs to a clustered
|
|
454
|
+
// group AND there are siblings to move against. A single-track group
|
|
455
|
+
// can never be "clustered" in any meaningful way, and showing the
|
|
456
|
+
// items as disabled there is confusing (users see a grayed-out option
|
|
457
|
+
// with no explanation).
|
|
458
|
+
const containingTracks = model.getContainingTrackGroup(track_id) || [];
|
|
317
459
|
const movingDisabled =
|
|
318
460
|
model.getTrackMovable(track_id) &&
|
|
319
|
-
model.isTrackInClusteredGroup(track_id)
|
|
461
|
+
model.isTrackInClusteredGroup(track_id) &&
|
|
462
|
+
containingTracks.length > 1;
|
|
320
463
|
|
|
321
464
|
if (model.getTrackMovable(track_id)) {
|
|
465
|
+
const customMoveUp = model.getTrackOnMoveUp(track_id);
|
|
466
|
+
const customMoveDown = model.getTrackOnMoveDown(track_id);
|
|
467
|
+
const moveUpDisabled =
|
|
468
|
+
movingDisabled || model.isTrackMoveUpDisabled(track_id);
|
|
469
|
+
const moveDownDisabled =
|
|
470
|
+
movingDisabled || model.isTrackMoveDownDisabled(track_id);
|
|
322
471
|
$dropdown.append(
|
|
323
472
|
OncoprintTrackOptionsView.$makeDropdownOption(
|
|
324
473
|
'Move up',
|
|
325
474
|
'normal',
|
|
326
|
-
|
|
475
|
+
moveUpDisabled,
|
|
327
476
|
function(evt) {
|
|
328
477
|
evt.stopPropagation();
|
|
329
|
-
|
|
478
|
+
if (customMoveUp) customMoveUp();
|
|
479
|
+
else self.moveUpCallback(track_id);
|
|
330
480
|
}
|
|
331
481
|
)
|
|
332
482
|
);
|
|
@@ -334,10 +484,11 @@ export default class OncoprintTrackOptionsView {
|
|
|
334
484
|
OncoprintTrackOptionsView.$makeDropdownOption(
|
|
335
485
|
'Move down',
|
|
336
486
|
'normal',
|
|
337
|
-
|
|
487
|
+
moveDownDisabled,
|
|
338
488
|
function(evt) {
|
|
339
489
|
evt.stopPropagation();
|
|
340
|
-
|
|
490
|
+
if (customMoveDown) customMoveDown();
|
|
491
|
+
else self.moveDownCallback(track_id);
|
|
341
492
|
}
|
|
342
493
|
)
|
|
343
494
|
);
|
|
@@ -509,6 +660,15 @@ export default class OncoprintTrackOptionsView {
|
|
|
509
660
|
$dropdown.append(
|
|
510
661
|
OncoprintTrackOptionsView.$makeDropdownSeparator()
|
|
511
662
|
);
|
|
663
|
+
} else if (option.children && option.children.length) {
|
|
664
|
+
$dropdown.append(
|
|
665
|
+
OncoprintTrackOptionsView.$makeDropdownSubmenuOption(
|
|
666
|
+
option.label || '',
|
|
667
|
+
option.children,
|
|
668
|
+
track_id,
|
|
669
|
+
() => self.hideTrackMenu(track_id)
|
|
670
|
+
)
|
|
671
|
+
);
|
|
512
672
|
} else {
|
|
513
673
|
$dropdown.append(
|
|
514
674
|
OncoprintTrackOptionsView.$makeDropdownOption(
|
|
@@ -630,9 +630,13 @@ export default class OncoprintWebGLCellView {
|
|
|
630
630
|
self.ctx.viewportWidth,
|
|
631
631
|
self.ctx.viewportHeight,
|
|
632
632
|
0,
|
|
633
|
-
-
|
|
633
|
+
-1000,
|
|
634
634
|
1000
|
|
635
|
-
); // y axis inverted so that y increases down like SVG
|
|
635
|
+
); // y axis inverted so that y increases down like SVG.
|
|
636
|
+
// z_index in packed vertex positions equals shape-list index, which
|
|
637
|
+
// for stacked-bar rule sets is one-per-category. A wide near plane
|
|
638
|
+
// (-1000) keeps shapes with z>5 from being frustum-clipped, which
|
|
639
|
+
// would shorten bars for tracks with 7+ categories.
|
|
636
640
|
self.pMatrix = pMatrix;
|
|
637
641
|
})(this);
|
|
638
642
|
}
|