@genome-spy/core 0.74.0 → 0.75.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/index.es.js +4660 -4468
- package/dist/bundle/index.js +81 -81
- package/dist/schema.json +220 -12
- 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/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/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/marks/mark.d.ts +1 -1
- 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 +1 -0
- 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 +182 -83
- 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 +171 -18
- 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 +4 -4
- package/dist/src/spec/data.d.ts +12 -0
- package/dist/src/spec/scale.d.ts +13 -1
- 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/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/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/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/package.json +2 -2
|
@@ -28,11 +28,13 @@ 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,
|
|
38
40
|
normalizeIntervalForSelection,
|
|
@@ -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() {
|
|
@@ -372,9 +396,15 @@ export default class ScaleResolution {
|
|
|
372
396
|
* @returns {boolean}
|
|
373
397
|
*/
|
|
374
398
|
#hasConfiguredDomain() {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
399
|
+
for (const member of this.#members) {
|
|
400
|
+
if (
|
|
401
|
+
member.contributesToDomain &&
|
|
402
|
+
member.channelDef.scale?.domain !== undefined
|
|
403
|
+
) {
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return false;
|
|
378
408
|
}
|
|
379
409
|
|
|
380
410
|
/**
|
|
@@ -484,9 +514,53 @@ export default class ScaleResolution {
|
|
|
484
514
|
if (member.contributesToDomain) {
|
|
485
515
|
this.#dataDomainMembers.add(member);
|
|
486
516
|
}
|
|
517
|
+
return member;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
#syncMembers() {
|
|
521
|
+
this.#membersDirty = false;
|
|
522
|
+
this.#invalidateOrderedMembers();
|
|
487
523
|
this.#invalidateConfiguredDomain();
|
|
488
524
|
this.#refreshSelectionDomainParamSubscriptions();
|
|
489
|
-
|
|
525
|
+
this.#refreshConfiguredDomainExprSubscriptions();
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
#markMembersDirty() {
|
|
529
|
+
if (this.#memberRegistrationBatchDepth > 0) {
|
|
530
|
+
this.#membersDirty = true;
|
|
531
|
+
} else {
|
|
532
|
+
this.#syncMembers();
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Executes a group of member registrations without refreshing derived
|
|
538
|
+
* membership state until the callback completes.
|
|
539
|
+
*
|
|
540
|
+
* @template T
|
|
541
|
+
* @param {Iterable<ScaleResolution>} resolutions
|
|
542
|
+
* @param {() => T} callback
|
|
543
|
+
* @returns {T}
|
|
544
|
+
*/
|
|
545
|
+
static registerInBatch(resolutions, callback) {
|
|
546
|
+
const batchedResolutions = Array.from(resolutions);
|
|
547
|
+
for (const resolution of batchedResolutions) {
|
|
548
|
+
resolution.#memberRegistrationBatchDepth++;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
try {
|
|
552
|
+
return callback();
|
|
553
|
+
} finally {
|
|
554
|
+
for (const resolution of batchedResolutions) {
|
|
555
|
+
resolution.#memberRegistrationBatchDepth--;
|
|
556
|
+
if (
|
|
557
|
+
resolution.#memberRegistrationBatchDepth === 0 &&
|
|
558
|
+
resolution.#membersDirty
|
|
559
|
+
) {
|
|
560
|
+
resolution.#syncMembers();
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
490
564
|
}
|
|
491
565
|
|
|
492
566
|
/**
|
|
@@ -495,12 +569,12 @@ export default class ScaleResolution {
|
|
|
495
569
|
*/
|
|
496
570
|
registerMember(member) {
|
|
497
571
|
const registeredMember = this.#addMember(member);
|
|
572
|
+
this.#markMembersDirty();
|
|
498
573
|
return () => {
|
|
499
574
|
const removed = this.#members.delete(registeredMember);
|
|
500
575
|
if (removed) {
|
|
501
576
|
this.#dataDomainMembers.delete(registeredMember);
|
|
502
|
-
this.#
|
|
503
|
-
this.#refreshSelectionDomainParamSubscriptions();
|
|
577
|
+
this.#markMembersDirty();
|
|
504
578
|
}
|
|
505
579
|
return removed && this.#members.size === 0;
|
|
506
580
|
};
|
|
@@ -508,6 +582,7 @@ export default class ScaleResolution {
|
|
|
508
582
|
|
|
509
583
|
dispose() {
|
|
510
584
|
this.#clearSelectionDomainParamSubscriptions();
|
|
585
|
+
this.#clearConfiguredDomainExprSubscriptions();
|
|
511
586
|
this.#listeners.domain.clear();
|
|
512
587
|
this.#listeners.range.clear();
|
|
513
588
|
this.#scaleManager.dispose();
|
|
@@ -521,6 +596,13 @@ export default class ScaleResolution {
|
|
|
521
596
|
this.#lastLinkedSelectionInterval = undefined;
|
|
522
597
|
}
|
|
523
598
|
|
|
599
|
+
#clearConfiguredDomainExprSubscriptions() {
|
|
600
|
+
for (const unsubscribe of this.#configuredDomainExprUnsubscribers) {
|
|
601
|
+
unsubscribe();
|
|
602
|
+
}
|
|
603
|
+
this.#configuredDomainExprUnsubscribers = [];
|
|
604
|
+
}
|
|
605
|
+
|
|
524
606
|
#refreshSelectionDomainParamSubscriptions() {
|
|
525
607
|
this.#clearSelectionDomainParamSubscriptions();
|
|
526
608
|
|
|
@@ -552,6 +634,33 @@ export default class ScaleResolution {
|
|
|
552
634
|
);
|
|
553
635
|
}
|
|
554
636
|
|
|
637
|
+
#refreshConfiguredDomainExprSubscriptions() {
|
|
638
|
+
this.#clearConfiguredDomainExprSubscriptions();
|
|
639
|
+
|
|
640
|
+
if (this.#members.size === 0) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const listener = () => {
|
|
645
|
+
this.#invalidateConfiguredDomain();
|
|
646
|
+
this.reconfigureDomain();
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
for (const member of this.#members) {
|
|
650
|
+
if (!member.contributesToDomain) {
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
const domain = member.channelDef.scale?.domain;
|
|
654
|
+
if (!isExprRef(domain)) {
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
const expr = member.view.paramRuntime.createExpression(domain.expr);
|
|
659
|
+
const unsubscribe = expr.subscribe(listener);
|
|
660
|
+
this.#configuredDomainExprUnsubscribers.push(unsubscribe);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
555
664
|
#hasRenderedMember() {
|
|
556
665
|
for (const member of this.#members) {
|
|
557
666
|
if (member.view.hasRendered()) {
|
|
@@ -637,9 +746,9 @@ export default class ScaleResolution {
|
|
|
637
746
|
const props = resolveScalePropsBase({
|
|
638
747
|
channel: this.channel,
|
|
639
748
|
dataType: this.type,
|
|
640
|
-
|
|
749
|
+
orderedMembers: this.#getOrderedMembers(),
|
|
641
750
|
isExplicitDomain: this.isDomainDefinedExplicitly(),
|
|
642
|
-
configScopes: this.#
|
|
751
|
+
configScopes: this.#resolutionView.getConfigScopes(),
|
|
643
752
|
});
|
|
644
753
|
this.#validateLinkedSelectionConfiguration(props);
|
|
645
754
|
return props;
|
|
@@ -650,6 +759,26 @@ export default class ScaleResolution {
|
|
|
650
759
|
invalidate(this, "mergedScaleProps");
|
|
651
760
|
}
|
|
652
761
|
|
|
762
|
+
#invalidateOrderedMembers() {
|
|
763
|
+
this.#orderedMembers = undefined;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Returns the participating members in a stable order.
|
|
768
|
+
*
|
|
769
|
+
* The membership set changes rarely, so cache the sorted order separately
|
|
770
|
+
* from merged scale props. That keeps parameter-driven domain updates from
|
|
771
|
+
* re-running the same path-based sort work.
|
|
772
|
+
*
|
|
773
|
+
* @returns {ScaleResolutionMember[]}
|
|
774
|
+
*/
|
|
775
|
+
#getOrderedMembers() {
|
|
776
|
+
if (!this.#orderedMembers) {
|
|
777
|
+
this.#orderedMembers = orderResolutionMembers(this.#members);
|
|
778
|
+
}
|
|
779
|
+
return this.#orderedMembers;
|
|
780
|
+
}
|
|
781
|
+
|
|
653
782
|
#invalidateConfiguredDomain() {
|
|
654
783
|
this.#domainAggregator.invalidateConfiguredDomain();
|
|
655
784
|
this.#invalidateMergedScaleProps();
|
|
@@ -736,10 +865,18 @@ export default class ScaleResolution {
|
|
|
736
865
|
|
|
737
866
|
const resolvedProps = { ...props };
|
|
738
867
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
868
|
+
this.#resolvingScaleProps += 1;
|
|
869
|
+
let domain;
|
|
870
|
+
try {
|
|
871
|
+
domain = this.#getConfiguredOrDefaultDomain(
|
|
872
|
+
extractDataDomain,
|
|
873
|
+
resolvedProps.type === LOCUS
|
|
874
|
+
? resolvedProps.assembly
|
|
875
|
+
: undefined
|
|
876
|
+
);
|
|
877
|
+
} finally {
|
|
878
|
+
this.#resolvingScaleProps -= 1;
|
|
879
|
+
}
|
|
743
880
|
|
|
744
881
|
if (isDiscrete(resolvedProps.type)) {
|
|
745
882
|
const isExplicit = this.isDomainDefinedExplicitly();
|
|
@@ -833,7 +970,6 @@ export default class ScaleResolution {
|
|
|
833
970
|
*/
|
|
834
971
|
reconfigureDomain() {
|
|
835
972
|
this.#withSelectionReverseSyncSuppressed(() => {
|
|
836
|
-
this.#invalidateMergedScaleProps();
|
|
837
973
|
const state = this.#computeScaleState(true, true);
|
|
838
974
|
if (!state) {
|
|
839
975
|
return;
|
|
@@ -946,6 +1082,11 @@ export default class ScaleResolution {
|
|
|
946
1082
|
return;
|
|
947
1083
|
}
|
|
948
1084
|
|
|
1085
|
+
if (scale.props.domainTransition === false) {
|
|
1086
|
+
this.#notifyListeners("domain");
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
949
1090
|
const newDomain = scale.domain();
|
|
950
1091
|
const action = this.#interactionController.getDomainChangeAction(
|
|
951
1092
|
previousDomain,
|
|
@@ -1005,6 +1146,11 @@ export default class ScaleResolution {
|
|
|
1005
1146
|
* @returns {ScaleWithProps}
|
|
1006
1147
|
*/
|
|
1007
1148
|
getScale() {
|
|
1149
|
+
if (this.#resolvingScaleProps > 0) {
|
|
1150
|
+
throw new Error(
|
|
1151
|
+
`Scale resolution for channel "${this.channel}" cannot read its own scale while its domain is being resolved.`
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1008
1154
|
return this.#scaleManager.scale ?? this.initializeScale();
|
|
1009
1155
|
}
|
|
1010
1156
|
|
|
@@ -1025,6 +1171,13 @@ export default class ScaleResolution {
|
|
|
1025
1171
|
}
|
|
1026
1172
|
|
|
1027
1173
|
getDomain() {
|
|
1174
|
+
if (this.#resolvingScaleProps > 0) {
|
|
1175
|
+
throw new Error(
|
|
1176
|
+
`Scale resolution for channel "${this.channel}" cannot read its own domain while its domain is being resolved.`
|
|
1177
|
+
);
|
|
1178
|
+
}
|
|
1179
|
+
// The underlying scale getter returns a fresh array. Treat this as a
|
|
1180
|
+
// read-only snapshot rather than a mutable backing store.
|
|
1028
1181
|
return this.getScale().domain();
|
|
1029
1182
|
}
|
|
1030
1183
|
|
|
@@ -1058,7 +1211,7 @@ export default class ScaleResolution {
|
|
|
1058
1211
|
return;
|
|
1059
1212
|
}
|
|
1060
1213
|
|
|
1061
|
-
const root = this.#
|
|
1214
|
+
const root = this.#resolutionView.getLayoutAncestors().at(-1);
|
|
1062
1215
|
const persist = root
|
|
1063
1216
|
? findIntervalSelectionBindingOwners(
|
|
1064
1217
|
root,
|
|
@@ -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.
|
|
@@ -434,17 +434,17 @@ export interface Encoding {
|
|
|
434
434
|
*
|
|
435
435
|
* The `value` of this channel can be a number between zero and one.
|
|
436
436
|
*/
|
|
437
|
-
x2?: Position2Def;
|
|
437
|
+
x2?: Position2Def | null;
|
|
438
438
|
|
|
439
439
|
/**
|
|
440
440
|
* Y2 coordinates of the marks.
|
|
441
441
|
*
|
|
442
442
|
* The `value` of this channel can be a number between zero and one.
|
|
443
443
|
*/
|
|
444
|
-
y2?: Position2Def;
|
|
444
|
+
y2?: Position2Def | null;
|
|
445
445
|
|
|
446
|
-
dx?: NumericMarkPropDef; // TODO: Not a mark property. Fix types.
|
|
447
|
-
dy?: NumericMarkPropDef;
|
|
446
|
+
dx?: NumericMarkPropDef | MarkPropExprDef; // TODO: Not a mark property. Fix types.
|
|
447
|
+
dy?: NumericMarkPropDef | MarkPropExprDef;
|
|
448
448
|
|
|
449
449
|
/**
|
|
450
450
|
* Color of the marks – either fill or stroke color based on the `filled` property of mark definition.
|
package/dist/src/spec/data.d.ts
CHANGED
|
@@ -50,6 +50,12 @@ export interface DataFormatBase {
|
|
|
50
50
|
|
|
51
51
|
export interface CsvDataFormat extends DataFormatBase {
|
|
52
52
|
type?: "csv" | "tsv";
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Optional ordered list of field names for headerless CSV or TSV input.
|
|
56
|
+
* When provided, the first row is interpreted as data rather than a header row.
|
|
57
|
+
*/
|
|
58
|
+
columns?: string[];
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
export interface DsvDataFormat extends DataFormatBase {
|
|
@@ -62,6 +68,12 @@ export interface DsvDataFormat extends DataFormatBase {
|
|
|
62
68
|
* @maxLength 1
|
|
63
69
|
*/
|
|
64
70
|
delimiter: string;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Optional ordered list of field names for headerless delimiter-separated input.
|
|
74
|
+
* When provided, the first row is interpreted as data rather than a header row.
|
|
75
|
+
*/
|
|
76
|
+
columns?: string[];
|
|
65
77
|
}
|
|
66
78
|
|
|
67
79
|
export interface JsonDataFormat extends DataFormatBase {
|
package/dist/src/spec/scale.d.ts
CHANGED
|
@@ -68,7 +68,7 @@ export interface Scale {
|
|
|
68
68
|
*
|
|
69
69
|
* For _ordinal_ and _nominal_ fields, `domain` can be an array that lists valid input values.
|
|
70
70
|
*/
|
|
71
|
-
domain?: ScalarDomain | ComplexDomain | SelectionDomainRef;
|
|
71
|
+
domain?: ScalarDomain | ComplexDomain | SelectionDomainRef | ExprRef;
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
74
|
* Inserts a single mid-point value into a two-element domain. The mid-point value must lie between the domain minimum and maximum values. This property can be useful for setting a midpoint for [diverging color scales](https://vega.github.io/vega-lite/docs/scale.html#piecewise). The domainMid property is only intended for use with scales supporting continuous, piecewise domains.
|
|
@@ -92,6 +92,18 @@ export interface Scale {
|
|
|
92
92
|
*/
|
|
93
93
|
reverse?: boolean;
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Controls whether domain updates are applied immediately or with a smooth
|
|
97
|
+
* transition.
|
|
98
|
+
*
|
|
99
|
+
* Set this to `false` to apply domain updates immediately. The default is
|
|
100
|
+
* `true`, except for domains defined by an `ExprRef`, which default to
|
|
101
|
+
* `false` unless overridden.
|
|
102
|
+
*
|
|
103
|
+
* __Default value:__ `true`, except `false` for ExprRef-driven domains.
|
|
104
|
+
*/
|
|
105
|
+
domainTransition?: boolean | Record<string, unknown>;
|
|
106
|
+
|
|
95
107
|
/**
|
|
96
108
|
* The range of the scale. One of:
|
|
97
109
|
*
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @typedef { object } ExpressionProps
|
|
3
|
-
* @prop { string[] } fields
|
|
4
|
-
* @prop { string[] } globals
|
|
5
|
-
* @prop { string } code
|
|
6
|
-
*
|
|
7
|
-
* @typedef { ((datum?: import("../data/flowNode.js").Datum) => any) & ExpressionProps } ExpressionFunction
|
|
8
|
-
*
|
|
9
2
|
* @param {string} expr
|
|
3
|
+
* @param {Record<string, any>} globalObject
|
|
4
|
+
* @param {ExpressionCompileContext} context
|
|
5
|
+
*
|
|
10
6
|
* @returns {ExpressionFunction}
|
|
11
7
|
*/
|
|
12
|
-
export default function createFunction(expr: string, globalObject?:
|
|
8
|
+
export default function createFunction(expr: string, globalObject?: Record<string, any>, context?: ExpressionCompileContext): ExpressionFunction;
|
|
13
9
|
/**
|
|
14
10
|
* @param {string} expr
|
|
15
11
|
* @returns {(event: UIEvent | import("./interactionEvent.js").WheelLikeEvent) => boolean}
|
|
@@ -19,6 +15,18 @@ export type ExpressionProps = {
|
|
|
19
15
|
fields: string[];
|
|
20
16
|
globals: string[];
|
|
21
17
|
code: string;
|
|
18
|
+
scaleDependencies?: import("../paramRuntime/types.js").ParamRef<any>[];
|
|
22
19
|
};
|
|
23
20
|
export type ExpressionFunction = ((datum?: import("../data/flowNode.js").Datum) => any) & ExpressionProps;
|
|
21
|
+
export type ExpressionCompileContext = {
|
|
22
|
+
resolveScaleResolution?: (channel: string) => import("../scales/scaleResolution.js").default | undefined;
|
|
23
|
+
};
|
|
24
|
+
export type ScaleHelperCompileContext = ExpressionCompileContext & {
|
|
25
|
+
globalvar: string;
|
|
26
|
+
globalObject: Record<string, any>;
|
|
27
|
+
getScaleHelper: (kind: "scale" | "invert" | "domain" | "range", channel: string, resolution: import("../scales/scaleResolution.js").default) => {
|
|
28
|
+
codeName: string;
|
|
29
|
+
dependency: import("../paramRuntime/types.js").ParamRef<any>;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
24
32
|
//# sourceMappingURL=expression.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expression.d.ts","sourceRoot":"","sources":["../../../src/utils/expression.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"expression.d.ts","sourceRoot":"","sources":["../../../src/utils/expression.js"],"names":[],"mappings":"AAkVA;;;;;;GAMG;AACH,6CANW,MAAM,iBACN,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,YACnB,wBAAwB,GAEtB,kBAAkB,CAuG9B;AAQD;;;GAGG;AACH,gDAHW,MAAM,GACJ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,uBAAuB,EAAE,cAAc,KAAK,OAAO,CA4BxF;;YAjVU,MAAM,EAAE;aACR,MAAM,EAAE;UACR,MAAM;wBACN,OAAO,0BAA0B,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE;;iCAE/C,CAAC,CAAC,KAAK,CAAC,EAAE,OAAO,qBAAqB,EAAE,KAAK,KAAK,GAAG,CAAC,GAAG,eAAe;;6BAG5E,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,8BAA8B,EAAE,OAAO,GAAG,SAAS;;wCAE5E,wBAAwB,GAAG;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAClC,cAAc,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,8BAA8B,EAAE,OAAO,KAAK;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,OAAO,0BAA0B,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAA;KAAE,CAAA;CACnO"}
|