@genome-spy/core 0.74.0 → 0.76.0
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/bundle/{esm-CgfVIRJ-.js → esm-BimDEpBb.js} +1 -1
- package/dist/bundle/{esm-DtE8VqAv.js → esm-Bvlm1uVk.js} +1 -1
- package/dist/bundle/{esm-sIoQYZ21.js → esm-CngqBe45.js} +17 -17
- package/dist/bundle/{esm-DQiq2Zhd.js → esm-D_euN86T.js} +43 -43
- package/dist/bundle/index.es.js +6064 -5756
- package/dist/bundle/index.js +104 -103
- package/dist/schema.json +572 -12
- package/dist/src/config/defaults/markDefaults.d.ts.map +1 -1
- package/dist/src/config/defaults/markDefaults.js +1 -12
- package/dist/src/config/defaults/scaleDefaults.d.ts.map +1 -1
- package/dist/src/config/defaults/scaleDefaults.js +1 -0
- package/dist/src/config/markConfig.d.ts.map +1 -1
- package/dist/src/config/markConfig.js +16 -8
- package/dist/src/config/themes.d.ts.map +1 -1
- package/dist/src/config/themes.js +15 -2
- package/dist/src/data/sources/dataUtils.d.ts +25 -0
- package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
- package/dist/src/data/sources/dataUtils.js +23 -0
- package/dist/src/data/sources/inlineSource.js +2 -2
- package/dist/src/data/sources/lazy/registerBuiltInLazySources.js +2 -2
- package/dist/src/data/sources/lazy/registerCoreLazySources.d.ts +2 -0
- package/dist/src/data/sources/lazy/registerCoreLazySources.d.ts.map +1 -0
- package/dist/src/data/sources/lazy/registerCoreLazySources.js +2 -0
- package/dist/src/data/sources/lazy/tabixSource.d.ts +7 -0
- package/dist/src/data/sources/lazy/tabixSource.d.ts.map +1 -1
- package/dist/src/data/sources/lazy/tabixSource.js +18 -0
- package/dist/src/data/sources/lazy/tabixTsvSource.d.ts +37 -0
- package/dist/src/data/sources/lazy/tabixTsvSource.d.ts.map +1 -0
- package/dist/src/data/sources/lazy/tabixTsvSource.js +163 -0
- package/dist/src/data/sources/urlSource.d.ts.map +1 -1
- package/dist/src/data/sources/urlSource.js +8 -3
- package/dist/src/encoder/encoder.d.ts +2 -2
- package/dist/src/encoder/encoder.d.ts.map +1 -1
- package/dist/src/genome/scaleLocus.d.ts.map +1 -1
- package/dist/src/genome/scaleLocus.js +8 -3
- package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
- package/dist/src/genomeSpy/interactionController.js +91 -51
- package/dist/src/genomeSpyBase.d.ts.map +1 -1
- package/dist/src/genomeSpyBase.js +4 -1
- package/dist/src/gl/dataToVertices.d.ts +12 -14
- package/dist/src/gl/dataToVertices.d.ts.map +1 -1
- package/dist/src/gl/dataToVertices.js +116 -95
- package/dist/src/gl/glslScaleGenerator.d.ts +3 -0
- package/dist/src/gl/glslScaleGenerator.d.ts.map +1 -1
- package/dist/src/gl/glslScaleGenerator.js +10 -8
- package/dist/src/gl/vertexRangeIndex.d.ts +23 -0
- package/dist/src/gl/vertexRangeIndex.d.ts.map +1 -0
- package/dist/src/gl/vertexRangeIndex.js +150 -0
- package/dist/src/gl/webGLHelper.d.ts +5 -2
- package/dist/src/gl/webGLHelper.d.ts.map +1 -1
- package/dist/src/gl/webGLHelper.js +20 -3
- package/dist/src/marks/__snapshots__/shaderSnapshot.test.js.snap +1082 -0
- package/dist/src/marks/link.vertex.glsl.js +1 -1
- package/dist/src/marks/mark.d.ts +1 -1
- package/dist/src/minimal.d.ts.map +1 -1
- package/dist/src/minimal.js +5 -4
- package/dist/src/paramRuntime/expressionCompiler.d.ts +2 -1
- package/dist/src/paramRuntime/expressionCompiler.d.ts.map +1 -1
- package/dist/src/paramRuntime/expressionCompiler.js +3 -2
- package/dist/src/paramRuntime/expressionRef.d.ts +4 -1
- package/dist/src/paramRuntime/expressionRef.d.ts.map +1 -1
- package/dist/src/paramRuntime/expressionRef.js +10 -3
- package/dist/src/paramRuntime/graphRuntime.d.ts.map +1 -1
- package/dist/src/paramRuntime/graphRuntime.js +15 -6
- package/dist/src/paramRuntime/paramRuntime.d.ts +8 -2
- package/dist/src/paramRuntime/paramRuntime.d.ts.map +1 -1
- package/dist/src/paramRuntime/paramRuntime.js +10 -5
- package/dist/src/paramRuntime/types.d.ts +1 -0
- package/dist/src/paramRuntime/types.d.ts.map +1 -1
- package/dist/src/paramRuntime/types.js +1 -0
- package/dist/src/paramRuntime/viewParamRuntime.d.ts +5 -4
- package/dist/src/paramRuntime/viewParamRuntime.d.ts.map +1 -1
- package/dist/src/paramRuntime/viewParamRuntime.js +17 -6
- package/dist/src/scale/scale.d.ts.map +1 -1
- package/dist/src/scale/scale.js +11 -2
- package/dist/src/scales/domainPlanner.d.ts +57 -11
- package/dist/src/scales/domainPlanner.d.ts.map +1 -1
- package/dist/src/scales/domainPlanner.js +183 -84
- package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
- package/dist/src/scales/scaleInstanceManager.js +7 -2
- package/dist/src/scales/scalePropsResolver.d.ts +3 -3
- package/dist/src/scales/scalePropsResolver.d.ts.map +1 -1
- package/dist/src/scales/scalePropsResolver.js +28 -5
- package/dist/src/scales/scaleResolution.d.ts +12 -1
- package/dist/src/scales/scaleResolution.d.ts.map +1 -1
- package/dist/src/scales/scaleResolution.js +180 -21
- package/dist/src/scales/selectionDomainUtils.d.ts +10 -0
- package/dist/src/scales/selectionDomainUtils.d.ts.map +1 -1
- package/dist/src/scales/selectionDomainUtils.js +32 -3
- package/dist/src/screenshotExport.d.ts +23 -0
- package/dist/src/screenshotExport.d.ts.map +1 -0
- package/dist/src/screenshotExport.js +44 -0
- package/dist/src/screenshotHarness.d.ts.map +1 -1
- package/dist/src/screenshotHarness.js +26 -24
- package/dist/src/spec/axis.d.ts +2 -2
- package/dist/src/spec/channel.d.ts +34 -4
- package/dist/src/spec/data.d.ts +52 -0
- package/dist/src/spec/parameter.d.ts +6 -0
- package/dist/src/spec/scale.d.ts +13 -1
- package/dist/src/spec/transform.d.ts +6 -0
- package/dist/src/utils/expression.d.ts +16 -8
- package/dist/src/utils/expression.d.ts.map +1 -1
- package/dist/src/utils/expression.js +291 -11
- package/dist/src/view/axisGridView.d.ts.map +1 -1
- package/dist/src/view/axisGridView.js +2 -1
- package/dist/src/view/axisView.d.ts.map +1 -1
- package/dist/src/view/axisView.js +2 -1
- package/dist/src/view/facetView.d.ts.map +1 -1
- package/dist/src/view/facetView.js +2 -1
- package/dist/src/view/flowBuilder.d.ts +1 -1
- package/dist/src/view/flowBuilder.d.ts.map +1 -1
- package/dist/src/view/flowBuilder.js +11 -7
- package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
- package/dist/src/view/gridView/gridChild.js +9 -1
- package/dist/src/view/gridView/gridView.d.ts.map +1 -1
- package/dist/src/view/gridView/gridView.js +198 -32
- package/dist/src/view/gridView/scrollbar.d.ts.map +1 -1
- package/dist/src/view/gridView/scrollbar.js +5 -1
- package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
- package/dist/src/view/gridView/selectionRect.js +5 -1
- package/dist/src/view/gridView/separatorView.d.ts.map +1 -1
- package/dist/src/view/gridView/separatorView.js +5 -1
- package/dist/src/view/resolutionPlanner.d.ts +9 -0
- package/dist/src/view/resolutionPlanner.d.ts.map +1 -0
- package/dist/src/view/resolutionPlanner.js +302 -0
- package/dist/src/view/testUtils.d.ts +30 -3
- package/dist/src/view/testUtils.d.ts.map +1 -1
- package/dist/src/view/testUtils.js +51 -2
- package/dist/src/view/unitView.d.ts +1 -1
- package/dist/src/view/unitView.d.ts.map +1 -1
- package/dist/src/view/unitView.js +5 -152
- package/dist/src/view/view.d.ts.map +1 -1
- package/dist/src/view/view.js +2 -1
- package/dist/src/view/viewSelectors.d.ts +38 -10
- package/dist/src/view/viewSelectors.d.ts.map +1 -1
- package/dist/src/view/viewSelectors.js +67 -2
- package/dist/src/view/viewUtilTypes.d.ts +15 -0
- package/dist/src/view/viewUtils.d.ts.map +1 -1
- package/dist/src/view/viewUtils.js +10 -0
- package/package.json +2 -2
- package/LICENSE +0 -21
- /package/dist/bundle/{esm-BDFRLEuD.js → esm-C49STiCR.js} +0 -0
- /package/dist/bundle/{esm-CGX-qz1d.js → esm-CuVa5T98.js} +0 -0
|
@@ -28,15 +28,17 @@ import {
|
|
|
28
28
|
isPrimaryPositionalChannel,
|
|
29
29
|
isSecondaryChannel,
|
|
30
30
|
} from "../encoder/encoder.js";
|
|
31
|
+
import { isExprRef } from "../paramRuntime/paramUtils.js";
|
|
31
32
|
import { NominalDomain } from "../utils/domainArray.js";
|
|
32
33
|
import { shallowArrayEquals } from "../utils/arrayUtils.js";
|
|
33
34
|
import createIndexer from "../utils/indexer.js";
|
|
34
35
|
import { getCachedOrCall, invalidate } from "../utils/propertyCacher.js";
|
|
35
36
|
import { resolveUrl } from "../utils/url.js";
|
|
37
|
+
import { orderResolutionMembers } from "./resolutionMemberOrder.js";
|
|
36
38
|
import {
|
|
37
39
|
findIntervalSelectionBindingOwners,
|
|
40
|
+
getIntervalSelection,
|
|
38
41
|
normalizeIntervalForSelection,
|
|
39
|
-
requireIntervalSelection,
|
|
40
42
|
} from "./selectionDomainUtils.js";
|
|
41
43
|
import { toExternalIndexLikeInterval } from "./indexLikeDomainUtils.js";
|
|
42
44
|
|
|
@@ -100,6 +102,9 @@ export default class ScaleResolution {
|
|
|
100
102
|
/** @type {Set<ScaleResolutionMember>} */
|
|
101
103
|
#dataDomainMembers = new Set();
|
|
102
104
|
|
|
105
|
+
/** @type {ScaleResolutionMember[] | undefined} */
|
|
106
|
+
#orderedMembers;
|
|
107
|
+
|
|
103
108
|
/**
|
|
104
109
|
* @type {Record<ScaleResolutionEventType, Set<ScaleResolutionListener>>}
|
|
105
110
|
*/
|
|
@@ -127,15 +132,28 @@ export default class ScaleResolution {
|
|
|
127
132
|
|
|
128
133
|
#selectionReverseSyncSuppressionDepth = 0;
|
|
129
134
|
|
|
135
|
+
/** @type {(() => void)[]} */
|
|
136
|
+
#configuredDomainExprUnsubscribers = [];
|
|
137
|
+
|
|
130
138
|
#ignoreSelectionInitial = false;
|
|
131
139
|
|
|
132
140
|
/** @type {[number, number] | null | undefined} */
|
|
133
141
|
#lastLinkedSelectionInterval = undefined;
|
|
134
142
|
|
|
143
|
+
/** @type {import("../view/view.js").default | undefined} */
|
|
144
|
+
#hostView;
|
|
145
|
+
|
|
146
|
+
#resolvingScaleProps = 0;
|
|
147
|
+
|
|
148
|
+
#memberRegistrationBatchDepth = 0;
|
|
149
|
+
|
|
150
|
+
#membersDirty = false;
|
|
151
|
+
|
|
135
152
|
/**
|
|
136
153
|
* @param {Channel} channel
|
|
154
|
+
* @param {import("../view/view.js").default} [hostView]
|
|
137
155
|
*/
|
|
138
|
-
constructor(channel) {
|
|
156
|
+
constructor(channel, hostView) {
|
|
139
157
|
this.channel = channel;
|
|
140
158
|
/** @type {import("../spec/channel.js").Type} Data type (quantitative, nominal, etc...) */
|
|
141
159
|
this.type = null;
|
|
@@ -143,8 +161,10 @@ export default class ScaleResolution {
|
|
|
143
161
|
/** @type {string} An optional unique identifier for the scale */
|
|
144
162
|
this.name = undefined;
|
|
145
163
|
|
|
164
|
+
this.#hostView = hostView;
|
|
165
|
+
|
|
146
166
|
this.#domainAggregator = new DomainPlanner({
|
|
147
|
-
|
|
167
|
+
getActiveMembers: () => this.#getActiveMembers(),
|
|
148
168
|
getAllMembers: () => this.#members,
|
|
149
169
|
getDataMembers: () =>
|
|
150
170
|
this.#getActiveMembers(this.#dataDomainMembers),
|
|
@@ -154,7 +174,7 @@ export default class ScaleResolution {
|
|
|
154
174
|
});
|
|
155
175
|
|
|
156
176
|
this.#scaleManager = new ScaleInstanceManager({
|
|
157
|
-
getParamRuntime: () => this.#
|
|
177
|
+
getParamRuntime: () => this.#resolutionView.paramRuntime,
|
|
158
178
|
onRangeChange: () => this.#notifyListeners("range"),
|
|
159
179
|
onDomainChange: () => this.#notifyListeners("domain"),
|
|
160
180
|
getGenomeStore: () => this.#viewContext.genomeStore,
|
|
@@ -182,6 +202,10 @@ export default class ScaleResolution {
|
|
|
182
202
|
return first.view;
|
|
183
203
|
}
|
|
184
204
|
|
|
205
|
+
get #resolutionView() {
|
|
206
|
+
return this.#hostView ?? this.#firstMemberView;
|
|
207
|
+
}
|
|
208
|
+
|
|
185
209
|
/**
|
|
186
210
|
* @param {Set<ScaleResolutionMember>} [members]
|
|
187
211
|
*/
|
|
@@ -206,7 +230,7 @@ export default class ScaleResolution {
|
|
|
206
230
|
}
|
|
207
231
|
|
|
208
232
|
get #viewContext() {
|
|
209
|
-
return this.#
|
|
233
|
+
return this.#resolutionView.context;
|
|
210
234
|
}
|
|
211
235
|
|
|
212
236
|
get zoomExtent() {
|
|
@@ -300,10 +324,13 @@ export default class ScaleResolution {
|
|
|
300
324
|
return;
|
|
301
325
|
}
|
|
302
326
|
|
|
303
|
-
const selection =
|
|
327
|
+
const selection = getIntervalSelection(
|
|
304
328
|
linkInfo.runtime.getValue(linkInfo.param),
|
|
305
329
|
linkInfo.param
|
|
306
330
|
);
|
|
331
|
+
if (!selection) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
307
334
|
|
|
308
335
|
const interval = this.#normalizeDomainIntervalForLinkedSelection(
|
|
309
336
|
this.getScale().domain()
|
|
@@ -372,9 +399,15 @@ export default class ScaleResolution {
|
|
|
372
399
|
* @returns {boolean}
|
|
373
400
|
*/
|
|
374
401
|
#hasConfiguredDomain() {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
402
|
+
for (const member of this.#members) {
|
|
403
|
+
if (
|
|
404
|
+
member.contributesToDomain &&
|
|
405
|
+
member.channelDef.scale?.domain !== undefined
|
|
406
|
+
) {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return false;
|
|
378
411
|
}
|
|
379
412
|
|
|
380
413
|
/**
|
|
@@ -382,10 +415,13 @@ export default class ScaleResolution {
|
|
|
382
415
|
* @returns {[number, number] | null}
|
|
383
416
|
*/
|
|
384
417
|
#getCurrentLinkedSelectionInterval(linkInfo) {
|
|
385
|
-
const selection =
|
|
418
|
+
const selection = getIntervalSelection(
|
|
386
419
|
linkInfo.runtime.getValue(linkInfo.param),
|
|
387
420
|
linkInfo.param
|
|
388
421
|
);
|
|
422
|
+
if (!selection) {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
389
425
|
const interval = selection.intervals[linkInfo.encoding];
|
|
390
426
|
return interval && interval.length === 2
|
|
391
427
|
? /** @type {[number, number]} */ (interval)
|
|
@@ -484,9 +520,53 @@ export default class ScaleResolution {
|
|
|
484
520
|
if (member.contributesToDomain) {
|
|
485
521
|
this.#dataDomainMembers.add(member);
|
|
486
522
|
}
|
|
523
|
+
return member;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
#syncMembers() {
|
|
527
|
+
this.#membersDirty = false;
|
|
528
|
+
this.#invalidateOrderedMembers();
|
|
487
529
|
this.#invalidateConfiguredDomain();
|
|
488
530
|
this.#refreshSelectionDomainParamSubscriptions();
|
|
489
|
-
|
|
531
|
+
this.#refreshConfiguredDomainExprSubscriptions();
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
#markMembersDirty() {
|
|
535
|
+
if (this.#memberRegistrationBatchDepth > 0) {
|
|
536
|
+
this.#membersDirty = true;
|
|
537
|
+
} else {
|
|
538
|
+
this.#syncMembers();
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Executes a group of member registrations without refreshing derived
|
|
544
|
+
* membership state until the callback completes.
|
|
545
|
+
*
|
|
546
|
+
* @template T
|
|
547
|
+
* @param {Iterable<ScaleResolution>} resolutions
|
|
548
|
+
* @param {() => T} callback
|
|
549
|
+
* @returns {T}
|
|
550
|
+
*/
|
|
551
|
+
static registerInBatch(resolutions, callback) {
|
|
552
|
+
const batchedResolutions = Array.from(resolutions);
|
|
553
|
+
for (const resolution of batchedResolutions) {
|
|
554
|
+
resolution.#memberRegistrationBatchDepth++;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
try {
|
|
558
|
+
return callback();
|
|
559
|
+
} finally {
|
|
560
|
+
for (const resolution of batchedResolutions) {
|
|
561
|
+
resolution.#memberRegistrationBatchDepth--;
|
|
562
|
+
if (
|
|
563
|
+
resolution.#memberRegistrationBatchDepth === 0 &&
|
|
564
|
+
resolution.#membersDirty
|
|
565
|
+
) {
|
|
566
|
+
resolution.#syncMembers();
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
490
570
|
}
|
|
491
571
|
|
|
492
572
|
/**
|
|
@@ -495,12 +575,12 @@ export default class ScaleResolution {
|
|
|
495
575
|
*/
|
|
496
576
|
registerMember(member) {
|
|
497
577
|
const registeredMember = this.#addMember(member);
|
|
578
|
+
this.#markMembersDirty();
|
|
498
579
|
return () => {
|
|
499
580
|
const removed = this.#members.delete(registeredMember);
|
|
500
581
|
if (removed) {
|
|
501
582
|
this.#dataDomainMembers.delete(registeredMember);
|
|
502
|
-
this.#
|
|
503
|
-
this.#refreshSelectionDomainParamSubscriptions();
|
|
583
|
+
this.#markMembersDirty();
|
|
504
584
|
}
|
|
505
585
|
return removed && this.#members.size === 0;
|
|
506
586
|
};
|
|
@@ -508,6 +588,7 @@ export default class ScaleResolution {
|
|
|
508
588
|
|
|
509
589
|
dispose() {
|
|
510
590
|
this.#clearSelectionDomainParamSubscriptions();
|
|
591
|
+
this.#clearConfiguredDomainExprSubscriptions();
|
|
511
592
|
this.#listeners.domain.clear();
|
|
512
593
|
this.#listeners.range.clear();
|
|
513
594
|
this.#scaleManager.dispose();
|
|
@@ -521,6 +602,13 @@ export default class ScaleResolution {
|
|
|
521
602
|
this.#lastLinkedSelectionInterval = undefined;
|
|
522
603
|
}
|
|
523
604
|
|
|
605
|
+
#clearConfiguredDomainExprSubscriptions() {
|
|
606
|
+
for (const unsubscribe of this.#configuredDomainExprUnsubscribers) {
|
|
607
|
+
unsubscribe();
|
|
608
|
+
}
|
|
609
|
+
this.#configuredDomainExprUnsubscribers = [];
|
|
610
|
+
}
|
|
611
|
+
|
|
524
612
|
#refreshSelectionDomainParamSubscriptions() {
|
|
525
613
|
this.#clearSelectionDomainParamSubscriptions();
|
|
526
614
|
|
|
@@ -552,6 +640,33 @@ export default class ScaleResolution {
|
|
|
552
640
|
);
|
|
553
641
|
}
|
|
554
642
|
|
|
643
|
+
#refreshConfiguredDomainExprSubscriptions() {
|
|
644
|
+
this.#clearConfiguredDomainExprSubscriptions();
|
|
645
|
+
|
|
646
|
+
if (this.#members.size === 0) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const listener = () => {
|
|
651
|
+
this.#invalidateConfiguredDomain();
|
|
652
|
+
this.reconfigureDomain();
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
for (const member of this.#members) {
|
|
656
|
+
if (!member.contributesToDomain) {
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
const domain = member.channelDef.scale?.domain;
|
|
660
|
+
if (!isExprRef(domain)) {
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
const expr = member.view.paramRuntime.createExpression(domain.expr);
|
|
665
|
+
const unsubscribe = expr.subscribe(listener);
|
|
666
|
+
this.#configuredDomainExprUnsubscribers.push(unsubscribe);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
555
670
|
#hasRenderedMember() {
|
|
556
671
|
for (const member of this.#members) {
|
|
557
672
|
if (member.view.hasRendered()) {
|
|
@@ -637,9 +752,9 @@ export default class ScaleResolution {
|
|
|
637
752
|
const props = resolveScalePropsBase({
|
|
638
753
|
channel: this.channel,
|
|
639
754
|
dataType: this.type,
|
|
640
|
-
|
|
755
|
+
orderedMembers: this.#getOrderedMembers(),
|
|
641
756
|
isExplicitDomain: this.isDomainDefinedExplicitly(),
|
|
642
|
-
configScopes: this.#
|
|
757
|
+
configScopes: this.#resolutionView.getConfigScopes(),
|
|
643
758
|
});
|
|
644
759
|
this.#validateLinkedSelectionConfiguration(props);
|
|
645
760
|
return props;
|
|
@@ -650,6 +765,26 @@ export default class ScaleResolution {
|
|
|
650
765
|
invalidate(this, "mergedScaleProps");
|
|
651
766
|
}
|
|
652
767
|
|
|
768
|
+
#invalidateOrderedMembers() {
|
|
769
|
+
this.#orderedMembers = undefined;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Returns the participating members in a stable order.
|
|
774
|
+
*
|
|
775
|
+
* The membership set changes rarely, so cache the sorted order separately
|
|
776
|
+
* from merged scale props. That keeps parameter-driven domain updates from
|
|
777
|
+
* re-running the same path-based sort work.
|
|
778
|
+
*
|
|
779
|
+
* @returns {ScaleResolutionMember[]}
|
|
780
|
+
*/
|
|
781
|
+
#getOrderedMembers() {
|
|
782
|
+
if (!this.#orderedMembers) {
|
|
783
|
+
this.#orderedMembers = orderResolutionMembers(this.#members);
|
|
784
|
+
}
|
|
785
|
+
return this.#orderedMembers;
|
|
786
|
+
}
|
|
787
|
+
|
|
653
788
|
#invalidateConfiguredDomain() {
|
|
654
789
|
this.#domainAggregator.invalidateConfiguredDomain();
|
|
655
790
|
this.#invalidateMergedScaleProps();
|
|
@@ -736,10 +871,18 @@ export default class ScaleResolution {
|
|
|
736
871
|
|
|
737
872
|
const resolvedProps = { ...props };
|
|
738
873
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
874
|
+
this.#resolvingScaleProps += 1;
|
|
875
|
+
let domain;
|
|
876
|
+
try {
|
|
877
|
+
domain = this.#getConfiguredOrDefaultDomain(
|
|
878
|
+
extractDataDomain,
|
|
879
|
+
resolvedProps.type === LOCUS
|
|
880
|
+
? resolvedProps.assembly
|
|
881
|
+
: undefined
|
|
882
|
+
);
|
|
883
|
+
} finally {
|
|
884
|
+
this.#resolvingScaleProps -= 1;
|
|
885
|
+
}
|
|
743
886
|
|
|
744
887
|
if (isDiscrete(resolvedProps.type)) {
|
|
745
888
|
const isExplicit = this.isDomainDefinedExplicitly();
|
|
@@ -833,7 +976,6 @@ export default class ScaleResolution {
|
|
|
833
976
|
*/
|
|
834
977
|
reconfigureDomain() {
|
|
835
978
|
this.#withSelectionReverseSyncSuppressed(() => {
|
|
836
|
-
this.#invalidateMergedScaleProps();
|
|
837
979
|
const state = this.#computeScaleState(true, true);
|
|
838
980
|
if (!state) {
|
|
839
981
|
return;
|
|
@@ -946,6 +1088,11 @@ export default class ScaleResolution {
|
|
|
946
1088
|
return;
|
|
947
1089
|
}
|
|
948
1090
|
|
|
1091
|
+
if (scale.props.domainTransition === false) {
|
|
1092
|
+
this.#notifyListeners("domain");
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
949
1096
|
const newDomain = scale.domain();
|
|
950
1097
|
const action = this.#interactionController.getDomainChangeAction(
|
|
951
1098
|
previousDomain,
|
|
@@ -1005,6 +1152,11 @@ export default class ScaleResolution {
|
|
|
1005
1152
|
* @returns {ScaleWithProps}
|
|
1006
1153
|
*/
|
|
1007
1154
|
getScale() {
|
|
1155
|
+
if (this.#resolvingScaleProps > 0) {
|
|
1156
|
+
throw new Error(
|
|
1157
|
+
`Scale resolution for channel "${this.channel}" cannot read its own scale while its domain is being resolved.`
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1008
1160
|
return this.#scaleManager.scale ?? this.initializeScale();
|
|
1009
1161
|
}
|
|
1010
1162
|
|
|
@@ -1025,6 +1177,13 @@ export default class ScaleResolution {
|
|
|
1025
1177
|
}
|
|
1026
1178
|
|
|
1027
1179
|
getDomain() {
|
|
1180
|
+
if (this.#resolvingScaleProps > 0) {
|
|
1181
|
+
throw new Error(
|
|
1182
|
+
`Scale resolution for channel "${this.channel}" cannot read its own domain while its domain is being resolved.`
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
// The underlying scale getter returns a fresh array. Treat this as a
|
|
1186
|
+
// read-only snapshot rather than a mutable backing store.
|
|
1028
1187
|
return this.getScale().domain();
|
|
1029
1188
|
}
|
|
1030
1189
|
|
|
@@ -1058,7 +1217,7 @@ export default class ScaleResolution {
|
|
|
1058
1217
|
return;
|
|
1059
1218
|
}
|
|
1060
1219
|
|
|
1061
|
-
const root = this.#
|
|
1220
|
+
const root = this.#resolutionView.getLayoutAncestors().at(-1);
|
|
1062
1221
|
const persist = root
|
|
1063
1222
|
? findIntervalSelectionBindingOwners(
|
|
1064
1223
|
root,
|
|
@@ -10,6 +10,16 @@ export function requireParamRuntime(paramRuntime: {
|
|
|
10
10
|
* @param {string} paramName
|
|
11
11
|
*/
|
|
12
12
|
export function requireIntervalSelection(selection: any, paramName: string): import("../types/selectionTypes.js").IntervalSelection;
|
|
13
|
+
/**
|
|
14
|
+
* Returns an interval selection when the value exists and is of the correct
|
|
15
|
+
* type. Missing values are treated as empty so selection-linked domains can
|
|
16
|
+
* initialize before a pushed outer selection has been seeded.
|
|
17
|
+
*
|
|
18
|
+
* @param {any} selection
|
|
19
|
+
* @param {string} paramName
|
|
20
|
+
* @returns {import("../types/selectionTypes.js").IntervalSelection | undefined}
|
|
21
|
+
*/
|
|
22
|
+
export function getIntervalSelection(selection: any, paramName: string): import("../types/selectionTypes.js").IntervalSelection | undefined;
|
|
13
23
|
/**
|
|
14
24
|
* Resolves the runtime-backed interval selection binding used by a linked
|
|
15
25
|
* domain. Matching is based on the actual resolved runtime slot instead of
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selectionDomainUtils.d.ts","sourceRoot":"","sources":["../../../src/scales/selectionDomainUtils.js"],"names":[],"mappings":"AAOA;;;GAGG;AACH,kDAHW;IAAE,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAA;CAAE,aAC9C,MAAM,OAUhB;AAED;;;GAGG;AACH,oDAHW,GAAG,aACH,MAAM,0DAgBhB;AAED;;;;;;;;GAQG;AACH,sDAJW,OAAO,iBAAiB,EAAE,OAAO,aACjC,MAAM,YACN,GAAG,GAAG,GAAG;;;EAiBnB;AAED;;;;;GAKG;AACH,yDALW,OAAO,iBAAiB,EAAE,OAAO,WACjC,GAAG,aACH,MAAM,YACN,GAAG,GAAG,GAAG;
|
|
1
|
+
{"version":3,"file":"selectionDomainUtils.d.ts","sourceRoot":"","sources":["../../../src/scales/selectionDomainUtils.js"],"names":[],"mappings":"AAOA;;;GAGG;AACH,kDAHW;IAAE,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAA;CAAE,aAC9C,MAAM,OAUhB;AAED;;;GAGG;AACH,oDAHW,GAAG,aACH,MAAM,0DAgBhB;AAED;;;;;;;;GAQG;AACH,gDAJW,GAAG,aACH,MAAM,GACJ,OAAO,4BAA4B,EAAE,iBAAiB,GAAG,SAAS,CAc9E;AAED;;;;;;;;GAQG;AACH,sDAJW,OAAO,iBAAiB,EAAE,OAAO,aACjC,MAAM,YACN,GAAG,GAAG,GAAG;;;EAiBnB;AAED;;;;;GAKG;AACH,yDALW,OAAO,iBAAiB,EAAE,OAAO,WACjC,GAAG,aACH,MAAM,YACN,GAAG,GAAG,GAAG;UAQG,OAAO,iBAAiB,EAAE,OAAO;WAAS,OAAO,sBAAsB,EAAE,kBAAkB;IAuBjH;AAED;;;;;GAKG;AACH,yDALW,OAAO,iBAAiB,EAAE,OAAO,WACjC,GAAG,aACH,MAAM,YACN,GAAG,GAAG,GAAG,WA+CnB;AAED;;;;;GAKG;AACH,wDALW,MAAM,EAAE,cACR,MAAM,EAAE,YACR;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3B,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAsCxC"}
|
|
@@ -39,6 +39,29 @@ export function requireIntervalSelection(selection, paramName) {
|
|
|
39
39
|
return selection;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Returns an interval selection when the value exists and is of the correct
|
|
44
|
+
* type. Missing values are treated as empty so selection-linked domains can
|
|
45
|
+
* initialize before a pushed outer selection has been seeded.
|
|
46
|
+
*
|
|
47
|
+
* @param {any} selection
|
|
48
|
+
* @param {string} paramName
|
|
49
|
+
* @returns {import("../types/selectionTypes.js").IntervalSelection | undefined}
|
|
50
|
+
*/
|
|
51
|
+
export function getIntervalSelection(selection, paramName) {
|
|
52
|
+
if (!selection) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!isIntervalSelection(selection)) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Selection domain parameter "${paramName}" must be an interval selection.`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return selection;
|
|
63
|
+
}
|
|
64
|
+
|
|
42
65
|
/**
|
|
43
66
|
* Resolves the runtime-backed interval selection binding used by a linked
|
|
44
67
|
* domain. Matching is based on the actual resolved runtime slot instead of
|
|
@@ -52,7 +75,7 @@ export function resolveIntervalSelectionBinding(view, paramName, encoding) {
|
|
|
52
75
|
const runtime = view.paramRuntime.findRuntimeForParam
|
|
53
76
|
? requireParamRuntime(view.paramRuntime, paramName)
|
|
54
77
|
: view.paramRuntime;
|
|
55
|
-
const selection =
|
|
78
|
+
const selection = getIntervalSelection(
|
|
56
79
|
runtime.getValue
|
|
57
80
|
? runtime.getValue(paramName)
|
|
58
81
|
: view.paramRuntime.findValue(paramName),
|
|
@@ -71,7 +94,12 @@ export function resolveIntervalSelectionBinding(view, paramName, encoding) {
|
|
|
71
94
|
* @param {string} paramName
|
|
72
95
|
* @param {"x" | "y"} encoding
|
|
73
96
|
*/
|
|
74
|
-
export function findIntervalSelectionBindingOwners(
|
|
97
|
+
export function findIntervalSelectionBindingOwners(
|
|
98
|
+
root,
|
|
99
|
+
runtime,
|
|
100
|
+
paramName,
|
|
101
|
+
encoding
|
|
102
|
+
) {
|
|
75
103
|
/** @type {{ view: import("../view/view.js").default, param: import("../spec/parameter.js").SelectionParameter }[]} */
|
|
76
104
|
const owners = [];
|
|
77
105
|
|
|
@@ -124,7 +152,8 @@ export function hasIntervalSelectionBindingInScope(
|
|
|
124
152
|
|
|
125
153
|
seen.add(candidateView);
|
|
126
154
|
|
|
127
|
-
const param =
|
|
155
|
+
const param =
|
|
156
|
+
candidateView.paramRuntime?.paramConfigs?.get(paramName);
|
|
128
157
|
if (!param || !isSelectionParameter(param)) {
|
|
129
158
|
continue;
|
|
130
159
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {{ width: number | undefined, height: number | undefined }} renderedBounds
|
|
3
|
+
* @param {{ width: number, height: number }} logicalSize
|
|
4
|
+
*/
|
|
5
|
+
export function resolveExportSize(renderedBounds: {
|
|
6
|
+
width: number | undefined;
|
|
7
|
+
height: number | undefined;
|
|
8
|
+
}, logicalSize: {
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
}): {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Returns the smallest DPR that yields at least 400 physical pixels vertically.
|
|
17
|
+
*
|
|
18
|
+
* Fractional DPRs are rounded upward to the nearest 0.5.
|
|
19
|
+
*
|
|
20
|
+
* @param {number} logicalHeight
|
|
21
|
+
*/
|
|
22
|
+
export function resolveCaptureDevicePixelRatio(logicalHeight: number): number;
|
|
23
|
+
//# sourceMappingURL=screenshotExport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshotExport.d.ts","sourceRoot":"","sources":["../../src/screenshotExport.js"],"names":[],"mappings":"AAEA;;;GAGG;AACH,kDAHW;IAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,eACzD;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;;;EAiB3C;AAED;;;;;;GAMG;AACH,8DAFW,MAAM,UAehB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const MIN_VERTICAL_RESOLUTION = 400;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {{ width: number | undefined, height: number | undefined }} renderedBounds
|
|
5
|
+
* @param {{ width: number, height: number }} logicalSize
|
|
6
|
+
*/
|
|
7
|
+
export function resolveExportSize(renderedBounds, logicalSize) {
|
|
8
|
+
return {
|
|
9
|
+
width:
|
|
10
|
+
Number.isFinite(renderedBounds.width) && renderedBounds.width > 0
|
|
11
|
+
? Math.ceil(renderedBounds.width)
|
|
12
|
+
: Number.isFinite(logicalSize.width) && logicalSize.width > 0
|
|
13
|
+
? logicalSize.width
|
|
14
|
+
: 500,
|
|
15
|
+
height:
|
|
16
|
+
Number.isFinite(renderedBounds.height) && renderedBounds.height > 0
|
|
17
|
+
? Math.ceil(renderedBounds.height)
|
|
18
|
+
: Number.isFinite(logicalSize.height) && logicalSize.height > 0
|
|
19
|
+
? logicalSize.height
|
|
20
|
+
: 280,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns the smallest DPR that yields at least 400 physical pixels vertically.
|
|
26
|
+
*
|
|
27
|
+
* Fractional DPRs are rounded upward to the nearest 0.5.
|
|
28
|
+
*
|
|
29
|
+
* @param {number} logicalHeight
|
|
30
|
+
*/
|
|
31
|
+
export function resolveCaptureDevicePixelRatio(logicalHeight) {
|
|
32
|
+
if (!Number.isFinite(logicalHeight) || logicalHeight <= 0) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Expected a positive logical height, got ${logicalHeight}.`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (logicalHeight >= MIN_VERTICAL_RESOLUTION) {
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const requiredDevicePixelRatio = MIN_VERTICAL_RESOLUTION / logicalHeight;
|
|
43
|
+
return Math.ceil(requiredDevicePixelRatio * 2) / 2;
|
|
44
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshotHarness.d.ts","sourceRoot":"","sources":["../../src/screenshotHarness.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"screenshotHarness.d.ts","sourceRoot":"","sources":["../../src/screenshotHarness.js"],"names":[],"mappings":"8BAYa;IACR,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,OAAO,CAAC;QAAE,WAAW,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC5F;+BAIS,MAAM,GAAG,OAAO,UAAU,GAAG;IAAE,qBAAqB,EAAE,eAAe,CAAA;CAAE"}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { embed, loadSpec } from "./index.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import {
|
|
3
|
+
resolveCaptureDevicePixelRatio,
|
|
4
|
+
resolveExportSize,
|
|
5
|
+
} from "./screenshotExport.js";
|
|
6
|
+
|
|
7
|
+
const DEFAULT_CONTAINER_WIDTH = 500;
|
|
8
|
+
const DEFAULT_CONTAINER_HEIGHT = 300;
|
|
9
|
+
const DEFAULT_LAZY_READY_TIMEOUT_MS = 10_000;
|
|
6
10
|
const READY_DELAY_MS = 100;
|
|
7
11
|
|
|
8
12
|
/**
|
|
@@ -89,29 +93,41 @@ async function initializeHarness(url) {
|
|
|
89
93
|
api.getRenderedBounds(),
|
|
90
94
|
api.getLogicalCanvasSize()
|
|
91
95
|
);
|
|
96
|
+
const devicePixelRatio = resolveCaptureDevicePixelRatio(
|
|
97
|
+
logicalSize.height
|
|
98
|
+
);
|
|
92
99
|
|
|
93
100
|
screenshotWindow.__genomeSpyScreenshot = {
|
|
94
101
|
status: "ready",
|
|
95
|
-
detail: `Ready (${logicalSize.width}x${logicalSize.height}, DPR
|
|
102
|
+
detail: `Ready (${logicalSize.width}x${logicalSize.height}, DPR ${formatDevicePixelRatio(
|
|
103
|
+
devicePixelRatio
|
|
104
|
+
)})`,
|
|
96
105
|
error: "",
|
|
97
106
|
async capture() {
|
|
98
107
|
const currentSize = resolveExportSize(
|
|
99
108
|
api.getRenderedBounds(),
|
|
100
109
|
api.getLogicalCanvasSize()
|
|
101
110
|
);
|
|
111
|
+
const currentDevicePixelRatio = resolveCaptureDevicePixelRatio(
|
|
112
|
+
currentSize.height
|
|
113
|
+
);
|
|
102
114
|
return {
|
|
103
115
|
logicalSize: currentSize,
|
|
104
116
|
dataUrl: api.exportCanvas(
|
|
105
117
|
currentSize.width,
|
|
106
118
|
currentSize.height,
|
|
107
|
-
|
|
119
|
+
currentDevicePixelRatio,
|
|
108
120
|
"white"
|
|
109
121
|
),
|
|
110
122
|
};
|
|
111
123
|
},
|
|
112
124
|
};
|
|
113
125
|
|
|
114
|
-
setStatus(
|
|
126
|
+
setStatus(
|
|
127
|
+
`Ready (${logicalSize.width}x${logicalSize.height}, DPR ${formatDevicePixelRatio(
|
|
128
|
+
devicePixelRatio
|
|
129
|
+
)})`
|
|
130
|
+
);
|
|
115
131
|
} catch (error) {
|
|
116
132
|
setFailure(error instanceof Error ? error.message : String(error));
|
|
117
133
|
}
|
|
@@ -184,24 +200,10 @@ function parseTimeoutMs(value, fallback) {
|
|
|
184
200
|
}
|
|
185
201
|
|
|
186
202
|
/**
|
|
187
|
-
* @param {
|
|
188
|
-
* @param {{ width: number, height: number }} logicalSize
|
|
203
|
+
* @param {number} value
|
|
189
204
|
*/
|
|
190
|
-
function
|
|
191
|
-
return
|
|
192
|
-
width:
|
|
193
|
-
Number.isFinite(renderedBounds.width) && renderedBounds.width > 0
|
|
194
|
-
? Math.ceil(renderedBounds.width)
|
|
195
|
-
: Number.isFinite(logicalSize.width) && logicalSize.width > 0
|
|
196
|
-
? logicalSize.width
|
|
197
|
-
: DEFAULT_CONTAINER_WIDTH,
|
|
198
|
-
height:
|
|
199
|
-
Number.isFinite(renderedBounds.height) && renderedBounds.height > 0
|
|
200
|
-
? Math.ceil(renderedBounds.height)
|
|
201
|
-
: Number.isFinite(logicalSize.height) && logicalSize.height > 0
|
|
202
|
-
? logicalSize.height
|
|
203
|
-
: DEFAULT_CONTAINER_HEIGHT,
|
|
204
|
-
};
|
|
205
|
+
function formatDevicePixelRatio(value) {
|
|
206
|
+
return value.toFixed(3).replace(/\.?0+$/, "");
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
/**
|
package/dist/src/spec/axis.d.ts
CHANGED
|
@@ -113,9 +113,9 @@ export interface Axis extends BaseAxis, ZIndexProps {
|
|
|
113
113
|
format?: string;
|
|
114
114
|
|
|
115
115
|
/**
|
|
116
|
-
* A title for the axis (none by default).
|
|
116
|
+
* A title for the axis (none by default). Set to `null` to remove it.
|
|
117
117
|
*/
|
|
118
|
-
title?: string;
|
|
118
|
+
title?: string | null;
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
121
|
* The orthogonal offset in pixels by which to displace the axis from its position along the edge of the chart.
|