@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.
Files changed (143) hide show
  1. package/dist/bundle/{esm-CgfVIRJ-.js → esm-BimDEpBb.js} +1 -1
  2. package/dist/bundle/{esm-DtE8VqAv.js → esm-Bvlm1uVk.js} +1 -1
  3. package/dist/bundle/{esm-sIoQYZ21.js → esm-CngqBe45.js} +17 -17
  4. package/dist/bundle/{esm-DQiq2Zhd.js → esm-D_euN86T.js} +43 -43
  5. package/dist/bundle/index.es.js +6064 -5756
  6. package/dist/bundle/index.js +104 -103
  7. package/dist/schema.json +572 -12
  8. package/dist/src/config/defaults/markDefaults.d.ts.map +1 -1
  9. package/dist/src/config/defaults/markDefaults.js +1 -12
  10. package/dist/src/config/defaults/scaleDefaults.d.ts.map +1 -1
  11. package/dist/src/config/defaults/scaleDefaults.js +1 -0
  12. package/dist/src/config/markConfig.d.ts.map +1 -1
  13. package/dist/src/config/markConfig.js +16 -8
  14. package/dist/src/config/themes.d.ts.map +1 -1
  15. package/dist/src/config/themes.js +15 -2
  16. package/dist/src/data/sources/dataUtils.d.ts +25 -0
  17. package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
  18. package/dist/src/data/sources/dataUtils.js +23 -0
  19. package/dist/src/data/sources/inlineSource.js +2 -2
  20. package/dist/src/data/sources/lazy/registerBuiltInLazySources.js +2 -2
  21. package/dist/src/data/sources/lazy/registerCoreLazySources.d.ts +2 -0
  22. package/dist/src/data/sources/lazy/registerCoreLazySources.d.ts.map +1 -0
  23. package/dist/src/data/sources/lazy/registerCoreLazySources.js +2 -0
  24. package/dist/src/data/sources/lazy/tabixSource.d.ts +7 -0
  25. package/dist/src/data/sources/lazy/tabixSource.d.ts.map +1 -1
  26. package/dist/src/data/sources/lazy/tabixSource.js +18 -0
  27. package/dist/src/data/sources/lazy/tabixTsvSource.d.ts +37 -0
  28. package/dist/src/data/sources/lazy/tabixTsvSource.d.ts.map +1 -0
  29. package/dist/src/data/sources/lazy/tabixTsvSource.js +163 -0
  30. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  31. package/dist/src/data/sources/urlSource.js +8 -3
  32. package/dist/src/encoder/encoder.d.ts +2 -2
  33. package/dist/src/encoder/encoder.d.ts.map +1 -1
  34. package/dist/src/genome/scaleLocus.d.ts.map +1 -1
  35. package/dist/src/genome/scaleLocus.js +8 -3
  36. package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
  37. package/dist/src/genomeSpy/interactionController.js +91 -51
  38. package/dist/src/genomeSpyBase.d.ts.map +1 -1
  39. package/dist/src/genomeSpyBase.js +4 -1
  40. package/dist/src/gl/dataToVertices.d.ts +12 -14
  41. package/dist/src/gl/dataToVertices.d.ts.map +1 -1
  42. package/dist/src/gl/dataToVertices.js +116 -95
  43. package/dist/src/gl/glslScaleGenerator.d.ts +3 -0
  44. package/dist/src/gl/glslScaleGenerator.d.ts.map +1 -1
  45. package/dist/src/gl/glslScaleGenerator.js +10 -8
  46. package/dist/src/gl/vertexRangeIndex.d.ts +23 -0
  47. package/dist/src/gl/vertexRangeIndex.d.ts.map +1 -0
  48. package/dist/src/gl/vertexRangeIndex.js +150 -0
  49. package/dist/src/gl/webGLHelper.d.ts +5 -2
  50. package/dist/src/gl/webGLHelper.d.ts.map +1 -1
  51. package/dist/src/gl/webGLHelper.js +20 -3
  52. package/dist/src/marks/__snapshots__/shaderSnapshot.test.js.snap +1082 -0
  53. package/dist/src/marks/link.vertex.glsl.js +1 -1
  54. package/dist/src/marks/mark.d.ts +1 -1
  55. package/dist/src/minimal.d.ts.map +1 -1
  56. package/dist/src/minimal.js +5 -4
  57. package/dist/src/paramRuntime/expressionCompiler.d.ts +2 -1
  58. package/dist/src/paramRuntime/expressionCompiler.d.ts.map +1 -1
  59. package/dist/src/paramRuntime/expressionCompiler.js +3 -2
  60. package/dist/src/paramRuntime/expressionRef.d.ts +4 -1
  61. package/dist/src/paramRuntime/expressionRef.d.ts.map +1 -1
  62. package/dist/src/paramRuntime/expressionRef.js +10 -3
  63. package/dist/src/paramRuntime/graphRuntime.d.ts.map +1 -1
  64. package/dist/src/paramRuntime/graphRuntime.js +15 -6
  65. package/dist/src/paramRuntime/paramRuntime.d.ts +8 -2
  66. package/dist/src/paramRuntime/paramRuntime.d.ts.map +1 -1
  67. package/dist/src/paramRuntime/paramRuntime.js +10 -5
  68. package/dist/src/paramRuntime/types.d.ts +1 -0
  69. package/dist/src/paramRuntime/types.d.ts.map +1 -1
  70. package/dist/src/paramRuntime/types.js +1 -0
  71. package/dist/src/paramRuntime/viewParamRuntime.d.ts +5 -4
  72. package/dist/src/paramRuntime/viewParamRuntime.d.ts.map +1 -1
  73. package/dist/src/paramRuntime/viewParamRuntime.js +17 -6
  74. package/dist/src/scale/scale.d.ts.map +1 -1
  75. package/dist/src/scale/scale.js +11 -2
  76. package/dist/src/scales/domainPlanner.d.ts +57 -11
  77. package/dist/src/scales/domainPlanner.d.ts.map +1 -1
  78. package/dist/src/scales/domainPlanner.js +183 -84
  79. package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
  80. package/dist/src/scales/scaleInstanceManager.js +7 -2
  81. package/dist/src/scales/scalePropsResolver.d.ts +3 -3
  82. package/dist/src/scales/scalePropsResolver.d.ts.map +1 -1
  83. package/dist/src/scales/scalePropsResolver.js +28 -5
  84. package/dist/src/scales/scaleResolution.d.ts +12 -1
  85. package/dist/src/scales/scaleResolution.d.ts.map +1 -1
  86. package/dist/src/scales/scaleResolution.js +180 -21
  87. package/dist/src/scales/selectionDomainUtils.d.ts +10 -0
  88. package/dist/src/scales/selectionDomainUtils.d.ts.map +1 -1
  89. package/dist/src/scales/selectionDomainUtils.js +32 -3
  90. package/dist/src/screenshotExport.d.ts +23 -0
  91. package/dist/src/screenshotExport.d.ts.map +1 -0
  92. package/dist/src/screenshotExport.js +44 -0
  93. package/dist/src/screenshotHarness.d.ts.map +1 -1
  94. package/dist/src/screenshotHarness.js +26 -24
  95. package/dist/src/spec/axis.d.ts +2 -2
  96. package/dist/src/spec/channel.d.ts +34 -4
  97. package/dist/src/spec/data.d.ts +52 -0
  98. package/dist/src/spec/parameter.d.ts +6 -0
  99. package/dist/src/spec/scale.d.ts +13 -1
  100. package/dist/src/spec/transform.d.ts +6 -0
  101. package/dist/src/utils/expression.d.ts +16 -8
  102. package/dist/src/utils/expression.d.ts.map +1 -1
  103. package/dist/src/utils/expression.js +291 -11
  104. package/dist/src/view/axisGridView.d.ts.map +1 -1
  105. package/dist/src/view/axisGridView.js +2 -1
  106. package/dist/src/view/axisView.d.ts.map +1 -1
  107. package/dist/src/view/axisView.js +2 -1
  108. package/dist/src/view/facetView.d.ts.map +1 -1
  109. package/dist/src/view/facetView.js +2 -1
  110. package/dist/src/view/flowBuilder.d.ts +1 -1
  111. package/dist/src/view/flowBuilder.d.ts.map +1 -1
  112. package/dist/src/view/flowBuilder.js +11 -7
  113. package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
  114. package/dist/src/view/gridView/gridChild.js +9 -1
  115. package/dist/src/view/gridView/gridView.d.ts.map +1 -1
  116. package/dist/src/view/gridView/gridView.js +198 -32
  117. package/dist/src/view/gridView/scrollbar.d.ts.map +1 -1
  118. package/dist/src/view/gridView/scrollbar.js +5 -1
  119. package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
  120. package/dist/src/view/gridView/selectionRect.js +5 -1
  121. package/dist/src/view/gridView/separatorView.d.ts.map +1 -1
  122. package/dist/src/view/gridView/separatorView.js +5 -1
  123. package/dist/src/view/resolutionPlanner.d.ts +9 -0
  124. package/dist/src/view/resolutionPlanner.d.ts.map +1 -0
  125. package/dist/src/view/resolutionPlanner.js +302 -0
  126. package/dist/src/view/testUtils.d.ts +30 -3
  127. package/dist/src/view/testUtils.d.ts.map +1 -1
  128. package/dist/src/view/testUtils.js +51 -2
  129. package/dist/src/view/unitView.d.ts +1 -1
  130. package/dist/src/view/unitView.d.ts.map +1 -1
  131. package/dist/src/view/unitView.js +5 -152
  132. package/dist/src/view/view.d.ts.map +1 -1
  133. package/dist/src/view/view.js +2 -1
  134. package/dist/src/view/viewSelectors.d.ts +38 -10
  135. package/dist/src/view/viewSelectors.d.ts.map +1 -1
  136. package/dist/src/view/viewSelectors.js +67 -2
  137. package/dist/src/view/viewUtilTypes.d.ts +15 -0
  138. package/dist/src/view/viewUtils.d.ts.map +1 -1
  139. package/dist/src/view/viewUtils.js +10 -0
  140. package/package.json +2 -2
  141. package/LICENSE +0 -21
  142. /package/dist/bundle/{esm-BDFRLEuD.js → esm-C49STiCR.js} +0 -0
  143. /package/dist/bundle/{esm-CGX-qz1d.js → esm-CuVa5T98.js} +0 -0
@@ -11,6 +11,7 @@ import {
11
11
  resolveIntervalSelectionBinding,
12
12
  } from "./selectionDomainUtils.js";
13
13
  import createDomain from "../utils/domainArray.js";
14
+ import { isExprRef } from "../paramRuntime/paramUtils.js";
14
15
  import { getAccessorDomainKey, isScaleAccessor } from "../encoder/accessor.js";
15
16
  import { getEncoderAccessors, getPrimaryChannel } from "../encoder/encoder.js";
16
17
  import {
@@ -18,12 +19,45 @@ import {
18
19
  isChromosomalLocusInterval,
19
20
  } from "../genome/genome.js";
20
21
 
22
+ /*
23
+ * Domain planning decides what domain a shared scale should use before the
24
+ * scale instance is configured.
25
+ *
26
+ * "Planning" means collecting the participating members, separating literal
27
+ * configured domains from selection-driven domains, validating that the shared
28
+ * scale is not asked to mix incompatible sources, and producing the final union
29
+ * that will be applied to the scale.
30
+ */
31
+
21
32
  /**
22
33
  * @typedef {import("../utils/domainArray.js").DomainArray} DomainArray
23
34
  * @typedef {import("../spec/scale.js").ComplexDomain} ComplexDomain
24
35
  * @typedef {import("../spec/scale.js").ScalarDomain} ScalarDomain
25
36
  * @typedef {import("../spec/scale.js").SelectionDomainRef} SelectionDomainRef
37
+ * @typedef {import("../spec/parameter.js").ExprRef} ExprRef
26
38
  * @typedef {import("./scaleResolution.js").ScaleResolutionMember} ScaleResolutionMember
39
+ * @typedef {() => Set<ScaleResolutionMember>} ScaleMembersGetter
40
+ * @typedef {(interval: ScalarDomain | ComplexDomain) => number[]} FromComplexInterval
41
+ * @typedef {(assembly: import("../spec/scale.js").Scale["assembly"] | undefined) => number[]} GetLocusExtent
42
+ * @typedef {{
43
+ * domains: DomainArray[],
44
+ * selectionRef: SelectionDomainLinkInfo | undefined,
45
+ * selectionRuntime: any,
46
+ * selectionDescription: string | undefined,
47
+ * hasLiteralDomain: boolean,
48
+ * }} ConfiguredDomainResolutionState
49
+ * @typedef {{
50
+ * kind: "literal",
51
+ * domain: DomainArray,
52
+ * } | {
53
+ * kind: "selection",
54
+ * domain: DomainArray | undefined,
55
+ * description: string,
56
+ * param: string,
57
+ * encoding: "x" | "y",
58
+ * hasInitial: boolean,
59
+ * runtime: any,
60
+ * }} ConfiguredDomainMemberResolution
27
61
  * @typedef {{
28
62
  * param: string,
29
63
  * encoding: "x" | "y",
@@ -33,22 +67,22 @@ import {
33
67
  */
34
68
 
35
69
  export default class DomainPlanner {
36
- /** @type {() => Set<ScaleResolutionMember>} */
37
- #getMembers;
70
+ /** @type {ScaleMembersGetter} */
71
+ #getActiveMembers;
38
72
 
39
- /** @type {() => Set<ScaleResolutionMember>} */
73
+ /** @type {ScaleMembersGetter} */
40
74
  #getAllMembers;
41
75
 
42
- /** @type {() => Set<ScaleResolutionMember>} */
76
+ /** @type {ScaleMembersGetter} */
43
77
  #getDataMembers;
44
78
 
45
79
  /** @type {() => import("../spec/channel.js").Type} */
46
80
  #getType;
47
81
 
48
- /** @type {(assembly: import("../spec/scale.js").Scale["assembly"] | undefined) => number[]} */
82
+ /** @type {GetLocusExtent} */
49
83
  #getLocusExtent;
50
84
 
51
- /** @type {(interval: ScalarDomain | ComplexDomain) => number[]} */
85
+ /** @type {FromComplexInterval} */
52
86
  #fromComplexInterval;
53
87
 
54
88
  /** @type {any[]} */
@@ -67,24 +101,24 @@ export default class DomainPlanner {
67
101
 
68
102
  /**
69
103
  * @param {object} options
70
- * @param {() => Set<ScaleResolutionMember>} options.getMembers
71
- * @param {() => Set<ScaleResolutionMember>} [options.getAllMembers]
72
- * @param {() => Set<ScaleResolutionMember>} [options.getDataMembers]
104
+ * @param {ScaleMembersGetter} options.getActiveMembers Active shared-scale members used for configured domain planning.
105
+ * @param {ScaleMembersGetter} [options.getAllMembers] All members, including inactive ones, used for conflict validation.
106
+ * @param {ScaleMembersGetter} [options.getDataMembers] Members used for data-domain extraction; defaults to `getActiveMembers`.
73
107
  * @param {() => import("../spec/channel.js").Type} options.getType
74
- * @param {(assembly: import("../spec/scale.js").Scale["assembly"] | undefined) => number[]} options.getLocusExtent
75
- * @param {(interval: ScalarDomain | ComplexDomain) => number[]} options.fromComplexInterval
108
+ * @param {GetLocusExtent} options.getLocusExtent
109
+ * @param {FromComplexInterval} options.fromComplexInterval
76
110
  */
77
111
  constructor({
78
- getMembers,
112
+ getActiveMembers,
79
113
  getAllMembers,
80
114
  getDataMembers,
81
115
  getType,
82
116
  getLocusExtent,
83
117
  fromComplexInterval,
84
118
  }) {
85
- this.#getMembers = getMembers;
86
- this.#getAllMembers = getAllMembers ?? getMembers;
87
- this.#getDataMembers = getDataMembers ?? getMembers;
119
+ this.#getActiveMembers = getActiveMembers;
120
+ this.#getAllMembers = getAllMembers ?? getActiveMembers;
121
+ this.#getDataMembers = getDataMembers ?? getActiveMembers;
88
122
  this.#getType = getType;
89
123
  this.#getLocusExtent = getLocusExtent;
90
124
  this.#fromComplexInterval = fromComplexInterval;
@@ -147,10 +181,13 @@ export default class DomainPlanner {
147
181
  * @returns {any[]}
148
182
  */
149
183
  getDefaultDomain(extractDataDomain = false, locusAssembly) {
184
+ const type = this.#getType();
150
185
  return resolveDefaultDomain(
151
- this.#getType(),
186
+ type,
152
187
  this.#getLocusExtent,
153
- extractDataDomain ? this.getDataDomain() : undefined,
188
+ extractDataDomain && type !== LOCUS
189
+ ? this.getDataDomain()
190
+ : undefined,
154
191
  locusAssembly
155
192
  );
156
193
  }
@@ -194,7 +231,7 @@ export default class DomainPlanner {
194
231
  }
195
232
 
196
233
  const configuredDomain = resolveConfiguredDomain(
197
- this.#getMembers(),
234
+ this.#getActiveMembers(),
198
235
  this.#fromComplexInterval,
199
236
  includeSelectionInitial
200
237
  );
@@ -298,91 +335,153 @@ function resolveConfiguredDomain(
298
335
  .filter((member) => member.contributesToDomain)
299
336
  .filter((member) => member.channelDef.scale?.domain);
300
337
 
301
- /** @type {DomainArray[]} */
302
- const domains = [];
303
-
304
- /** @type {any} */
305
- let selectionRefRuntime = undefined;
306
- /** @type {string | undefined} */
307
- let selectionRefDescription = undefined;
308
- /** @type {SelectionDomainLinkInfo | undefined} */
309
- let selectionRef = undefined;
310
- let hasLiteralDomain = false;
338
+ /** @type {ConfiguredDomainResolutionState} */
339
+ const state = {
340
+ domains: [],
341
+ selectionRef: undefined,
342
+ selectionRuntime: undefined,
343
+ selectionDescription: undefined,
344
+ hasLiteralDomain: false,
345
+ };
311
346
 
312
347
  for (const member of domainMembers) {
313
- const domainDef = member.channelDef.scale.domain;
314
- if (isSelectionDomainRef(domainDef)) {
315
- if (hasLiteralDomain) {
316
- throw new Error(
317
- "Cannot mix selection-driven and literal configured domains on a shared scale."
318
- );
319
- }
348
+ const resolved = resolveConfiguredDomainMember(
349
+ member,
350
+ fromComplexInterval,
351
+ includeSelectionInitial
352
+ );
353
+
354
+ if (resolved.kind === "selection") {
355
+ mergeSelectionConfiguredDomain(state, resolved);
356
+ continue;
357
+ }
320
358
 
321
- const resolved = resolveSelectionDomain(
359
+ mergeLiteralConfiguredDomain(state, resolved);
360
+ }
361
+
362
+ return finishConfiguredDomainResolution(state);
363
+ }
364
+
365
+ /**
366
+ * @param {ScaleResolutionMember} member
367
+ * @param {(interval: ScalarDomain | ComplexDomain) => number[]} fromComplexInterval
368
+ * @param {boolean} includeSelectionInitial
369
+ * @returns {ConfiguredDomainMemberResolution}
370
+ */
371
+ function resolveConfiguredDomainMember(
372
+ member,
373
+ fromComplexInterval,
374
+ includeSelectionInitial
375
+ ) {
376
+ const domainDef = member.channelDef.scale.domain;
377
+ if (isSelectionDomainRef(domainDef)) {
378
+ return {
379
+ kind: "selection",
380
+ ...resolveSelectionDomain(
322
381
  member,
323
382
  domainDef,
324
383
  fromComplexInterval,
325
384
  includeSelectionInitial
326
- );
385
+ ),
386
+ };
387
+ }
327
388
 
328
- if (
329
- selectionRef &&
330
- (selectionRef.runtime !== resolved.runtime ||
331
- selectionRef.param !== resolved.param ||
332
- selectionRef.encoding !== resolved.encoding)
333
- ) {
334
- throw new Error(
335
- "Conflicting selection domain references on a shared scale: " +
336
- selectionRefDescription +
337
- " vs " +
338
- resolved.description +
339
- "."
340
- );
341
- }
389
+ if (isExprRef(domainDef)) {
390
+ return {
391
+ kind: "literal",
392
+ domain: resolveConfiguredIntervalDomain(
393
+ member.channelDef.type,
394
+ member.view.paramRuntime.createExpression(domainDef.expr)(),
395
+ fromComplexInterval
396
+ ),
397
+ };
398
+ }
342
399
 
343
- selectionRefRuntime = resolved.runtime;
344
- selectionRefDescription = resolved.description;
345
- selectionRef = {
346
- param: resolved.param,
347
- encoding: resolved.encoding,
348
- hasInitial:
349
- (selectionRef?.hasInitial ?? false) || resolved.hasInitial,
350
- runtime: resolved.runtime,
351
- };
352
-
353
- if (resolved.domain) {
354
- domains.push(resolved.domain);
355
- }
356
- continue;
357
- }
400
+ return {
401
+ kind: "literal",
402
+ domain: resolveConfiguredIntervalDomain(
403
+ member.channelDef.type,
404
+ domainDef,
405
+ fromComplexInterval
406
+ ),
407
+ };
408
+ }
358
409
 
359
- if (selectionRefRuntime) {
360
- throw new Error(
361
- "Cannot mix literal configured domains with selection-driven domains on a shared scale."
362
- );
363
- }
410
+ /**
411
+ * @param {ConfiguredDomainResolutionState} state
412
+ * @param {Extract<ConfiguredDomainMemberResolution, { kind: "selection" }>} resolved
413
+ */
414
+ function mergeSelectionConfiguredDomain(state, resolved) {
415
+ if (state.hasLiteralDomain) {
416
+ throw new Error(
417
+ "Cannot mix selection-driven and literal configured domains on a shared scale."
418
+ );
419
+ }
364
420
 
365
- hasLiteralDomain = true;
366
- domains.push(
367
- resolveConfiguredIntervalDomain(
368
- member.channelDef.type,
369
- domainDef,
370
- fromComplexInterval
371
- )
421
+ if (
422
+ state.selectionRef &&
423
+ (state.selectionRef.runtime !== resolved.runtime ||
424
+ state.selectionRef.param !== resolved.param ||
425
+ state.selectionRef.encoding !== resolved.encoding)
426
+ ) {
427
+ throw new Error(
428
+ "Conflicting selection domain references on a shared scale: " +
429
+ state.selectionDescription +
430
+ " vs " +
431
+ resolved.description +
432
+ "."
433
+ );
434
+ }
435
+
436
+ state.selectionRuntime = resolved.runtime;
437
+ state.selectionDescription = resolved.description;
438
+ state.selectionRef = {
439
+ param: resolved.param,
440
+ encoding: resolved.encoding,
441
+ hasInitial:
442
+ (state.selectionRef?.hasInitial ?? false) || resolved.hasInitial,
443
+ runtime: resolved.runtime,
444
+ };
445
+
446
+ if (resolved.domain) {
447
+ state.domains.push(resolved.domain);
448
+ }
449
+ }
450
+
451
+ /**
452
+ * @param {ConfiguredDomainResolutionState} state
453
+ * @param {Extract<ConfiguredDomainMemberResolution, { kind: "literal" }>} resolved
454
+ */
455
+ function mergeLiteralConfiguredDomain(state, resolved) {
456
+ if (state.selectionRuntime) {
457
+ throw new Error(
458
+ "Cannot mix literal configured domains with selection-driven domains on a shared scale."
372
459
  );
373
460
  }
374
461
 
375
- if (domains.length > 0) {
462
+ state.hasLiteralDomain = true;
463
+ state.domains.push(resolved.domain);
464
+ }
465
+
466
+ /**
467
+ * @param {ConfiguredDomainResolutionState} state
468
+ * @returns {{
469
+ * domain: DomainArray | undefined,
470
+ * selectionRef: SelectionDomainLinkInfo | undefined,
471
+ * }}
472
+ */
473
+ function finishConfiguredDomainResolution(state) {
474
+ if (state.domains.length > 0) {
376
475
  return {
377
- domain: domains.reduce((acc, curr) => acc.extendAll(curr)),
378
- selectionRef,
476
+ domain: state.domains.reduce((acc, curr) => acc.extendAll(curr)),
477
+ selectionRef: state.selectionRef,
379
478
  };
380
479
  }
381
480
 
382
- if (selectionRefRuntime) {
481
+ if (state.selectionRuntime) {
383
482
  // Selection refs are still the source of truth even when the
384
483
  // selection interval currently resolves to no domain.
385
- return { domain: undefined, selectionRef };
484
+ return { domain: undefined, selectionRef: state.selectionRef };
386
485
  }
387
486
 
388
487
  return { domain: undefined, selectionRef: undefined };
@@ -422,7 +521,7 @@ function resolveSelectionDomain(
422
521
  resolvedChannel
423
522
  );
424
523
  const hasInitial = domainRef.initial !== undefined;
425
- const interval = binding.selection.intervals[resolvedChannel];
524
+ const interval = binding.selection?.intervals[resolvedChannel];
426
525
  const description = paramName + "." + resolvedChannel;
427
526
  if (!interval || interval.length !== 2) {
428
527
  const initialDomain = includeSelectionInitial
@@ -1 +1 @@
1
- {"version":3,"file":"scaleInstanceManager.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleInstanceManager.js"],"names":[],"mappings":"AAMA;IA0BI;;;;;;OAMG;IACH,iFALG;QAAkH,eAAe,EAAzH,MAAM;YAAE,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,0BAA0B,EAAE,eAAe,CAAA;SAAE;QAC5E,aAAa,EAAjC,MAAM,IAAI;QACW,cAAc,GAAnC,MAAM,IAAI;QAC6D,cAAc,GAArF,MAAM,OAAO,0BAA0B,EAAE,OAAO,GAAG,SAAS;KAA0B,EAYhG;IAED;eA1CkC,OAAO,kBAAkB,EAAE,KAAK;MA4CjE;IAED;;;OAGG;IACH,0BAHW,OAAO,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,GAC1C,OAAO,qBAAqB,EAAE,OAAO,CAajD;IAED;;;OAGG;IACH,mBAHW,OAAO,kBAAkB,EAAE,KAAK;eAhET,OAAO,kBAAkB,EAAE,KAAK;MAqFjE;IAcD;;OAEG;IACH,wBAFW,OAAO,kBAAkB,EAAE,KAAK,QAc1C;IAED;;;OAGG;IACH,4CAHW,MAAM,IAAI,GACR,IAAI,CAShB;IAmFD,gBAGC;;CACJ"}
1
+ {"version":3,"file":"scaleInstanceManager.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleInstanceManager.js"],"names":[],"mappings":"AAMA;IA0BI;;;;;;OAMG;IACH,iFALG;QAAkH,eAAe,EAAzH,MAAM;YAAE,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,0BAA0B,EAAE,eAAe,CAAA;SAAE;QAC5E,aAAa,EAAjC,MAAM,IAAI;QACW,cAAc,GAAnC,MAAM,IAAI;QAC6D,cAAc,GAArF,MAAM,OAAO,0BAA0B,EAAE,OAAO,GAAG,SAAS;KAA0B,EAYhG;IAED;eA1CkC,OAAO,kBAAkB,EAAE,KAAK;MA4CjE;IAED;;;OAGG;IACH,0BAHW,OAAO,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,GAC1C,OAAO,qBAAqB,EAAE,OAAO,CAajD;IAED;;;OAGG;IACH,mBAHW,OAAO,kBAAkB,EAAE,KAAK;eAhET,OAAO,kBAAkB,EAAE,KAAK;MAqFjE;IAcD;;OAEG;IACH,wBAFW,OAAO,kBAAkB,EAAE,KAAK,QAc1C;IAED;;;OAGG;IACH,4CAHW,MAAM,IAAI,GACR,IAAI,CAShB;IAwFD,gBAGC;;CACJ"}
@@ -147,10 +147,12 @@ export default class ScaleInstanceManager {
147
147
  const {
148
148
  assembly: _assembly,
149
149
  domainIndexer: _domainIndexer,
150
+ __rangeExprScope: _rangeExprScope,
150
151
  ...rest
151
152
  } = propsAny;
152
153
  void _assembly;
153
154
  void _domainIndexer;
155
+ void _rangeExprScope;
154
156
  return rest;
155
157
  }
156
158
 
@@ -168,11 +170,14 @@ export default class ScaleInstanceManager {
168
170
  this.#rangeExprRefListeners.forEach((fn) => fn.invalidate());
169
171
  this.#rangeExprRefListeners.clear();
170
172
 
173
+ const rangeExprScope = /** @type {any} */ (props).__rangeExprScope;
174
+ const paramRuntime =
175
+ rangeExprScope?.paramRuntime ?? this.#getParamRuntime();
176
+
171
177
  const resolved = resolveRange({
172
178
  range: props.range,
173
179
  reverse: props.reverse,
174
- createExpression: (expr) =>
175
- this.#getParamRuntime().createExpression(expr),
180
+ createExpression: (expr) => paramRuntime.createExpression(expr),
176
181
  registerExpr: (fn) => this.#rangeExprRefListeners.add(fn),
177
182
  });
178
183
 
@@ -7,15 +7,15 @@
7
7
  * @param {object} options
8
8
  * @param {Channel} options.channel
9
9
  * @param {import("../spec/channel.js").Type} options.dataType
10
- * @param {Set<ScaleResolutionMember>} options.members
10
+ * @param {ScaleResolutionMember[]} options.orderedMembers
11
11
  * @param {boolean} options.isExplicitDomain
12
12
  * @param {import("../spec/config.js").GenomeSpyConfig[]} options.configScopes
13
13
  * @returns {Scale}
14
14
  */
15
- export function resolveScalePropsBase({ channel, dataType, members, isExplicitDomain, configScopes, }: {
15
+ export function resolveScalePropsBase({ channel, dataType, orderedMembers, isExplicitDomain, configScopes, }: {
16
16
  channel: Channel;
17
17
  dataType: import("../spec/channel.js").Type;
18
- members: Set<ScaleResolutionMember>;
18
+ orderedMembers: ScaleResolutionMember[];
19
19
  isExplicitDomain: boolean;
20
20
  configScopes: import("../spec/config.js").GenomeSpyConfig[];
21
21
  }): Scale;
@@ -1 +1 @@
1
- {"version":3,"file":"scalePropsResolver.d.ts","sourceRoot":"","sources":["../../../src/scales/scalePropsResolver.js"],"names":[],"mappings":"AAcA;;;;GAIG;AAEH;;;;;;;;GAQG;AACH,uGAPG;IAAyB,OAAO,EAAxB,OAAO;IACoC,QAAQ,EAAnD,OAAO,oBAAoB,EAAE,IAAI;IACG,OAAO,EAA3C,GAAG,CAAC,qBAAqB,CAAC;IACT,gBAAgB,EAAjC,OAAO;IACgD,YAAY,EAAnE,OAAO,mBAAmB,EAAE,eAAe,EAAE;CACrD,GAAU,KAAK,CAsHjB;sBAlIY,OAAO,oBAAoB,EAAE,OAAO;oBACpC,OAAO,kBAAkB,EAAE,KAAK;oCAChC,OAAO,sBAAsB,EAAE,qBAAqB"}
1
+ {"version":3,"file":"scalePropsResolver.d.ts","sourceRoot":"","sources":["../../../src/scales/scalePropsResolver.js"],"names":[],"mappings":"AAcA;;;;GAIG;AAEH;;;;;;;;GAQG;AACH,8GAPG;IAAyB,OAAO,EAAxB,OAAO;IACoC,QAAQ,EAAnD,OAAO,oBAAoB,EAAE,IAAI;IACA,cAAc,EAA/C,qBAAqB,EAAE;IACN,gBAAgB,EAAjC,OAAO;IACgD,YAAY,EAAnE,OAAO,mBAAmB,EAAE,eAAe,EAAE;CACrD,GAAU,KAAK,CA6IjB;sBAzJY,OAAO,oBAAoB,EAAE,OAAO;oBACpC,OAAO,kBAAkB,EAAE,KAAK;oCAChC,OAAO,sBAAsB,EAAE,qBAAqB"}
@@ -1,5 +1,6 @@
1
1
  import { isDiscrete } from "vega-scale";
2
2
  import { isColorChannel } from "../encoder/encoder.js";
3
+ import { isExprRef } from "../paramRuntime/paramUtils.js";
3
4
 
4
5
  import mergeObjects from "../utils/mergeObjects.js";
5
6
  import {
@@ -10,7 +11,6 @@ import {
10
11
  } from "../config/scaleConfig.js";
11
12
  import { applyLockedProperties, getDefaultScaleType } from "./scaleRules.js";
12
13
  import { INDEX, LOCUS } from "./scaleResolutionConstants.js";
13
- import { orderResolutionMembers } from "./resolutionMemberOrder.js";
14
14
 
15
15
  /**
16
16
  * @typedef {import("../spec/channel.js").Channel} Channel
@@ -22,7 +22,7 @@ import { orderResolutionMembers } from "./resolutionMemberOrder.js";
22
22
  * @param {object} options
23
23
  * @param {Channel} options.channel
24
24
  * @param {import("../spec/channel.js").Type} options.dataType
25
- * @param {Set<ScaleResolutionMember>} options.members
25
+ * @param {ScaleResolutionMember[]} options.orderedMembers
26
26
  * @param {boolean} options.isExplicitDomain
27
27
  * @param {import("../spec/config.js").GenomeSpyConfig[]} options.configScopes
28
28
  * @returns {Scale}
@@ -30,11 +30,13 @@ import { orderResolutionMembers } from "./resolutionMemberOrder.js";
30
30
  export function resolveScalePropsBase({
31
31
  channel,
32
32
  dataType,
33
- members,
33
+ orderedMembers,
34
34
  isExplicitDomain,
35
35
  configScopes,
36
36
  }) {
37
- const markTypes = [...members]
37
+ const memberList = orderedMembers;
38
+
39
+ const markTypes = memberList
38
40
  .map((member) =>
39
41
  typeof member.view.getMarkType == "function"
40
42
  ? member.view.getMarkType()
@@ -42,7 +44,7 @@ export function resolveScalePropsBase({
42
44
  )
43
45
  .filter((markType) => !!markType);
44
46
 
45
- const propArray = orderResolutionMembers(members)
47
+ const propArray = memberList
46
48
  .map((member) => member.channelDef.scale)
47
49
  .filter((props) => props !== undefined);
48
50
 
@@ -129,6 +131,27 @@ export function resolveScalePropsBase({
129
131
  // TODO: Props should be set more intelligently
130
132
  }
131
133
 
134
+ if (props.domainTransition === undefined) {
135
+ const hasExprDrivenDomain = memberList.some((member) =>
136
+ isExprRef(member.channelDef.scale?.domain)
137
+ );
138
+ props.domainTransition = !hasExprDrivenDomain;
139
+ }
140
+
141
+ if (
142
+ Array.isArray(props.range) &&
143
+ props.range.some(isExprRef) &&
144
+ memberList.length > 0
145
+ ) {
146
+ const rangeOwner = memberList.find(
147
+ (member) => member.channelDef.scale?.range !== undefined
148
+ )?.view;
149
+ if (rangeOwner) {
150
+ /** @type {any} */
151
+ (props).__rangeExprScope = rangeOwner;
152
+ }
153
+ }
154
+
132
155
  // By default, index and locus scales are zoomable, others are not.
133
156
  // Config can override this baseline via scale.zoom.
134
157
  if (!("zoom" in props)) {
@@ -27,10 +27,21 @@
27
27
  * @implements {ScaleResolutionApi}
28
28
  */
29
29
  export default class ScaleResolution implements ScaleResolutionApi {
30
+ /**
31
+ * Executes a group of member registrations without refreshing derived
32
+ * membership state until the callback completes.
33
+ *
34
+ * @template T
35
+ * @param {Iterable<ScaleResolution>} resolutions
36
+ * @param {() => T} callback
37
+ * @returns {T}
38
+ */
39
+ static registerInBatch<T>(resolutions: Iterable<ScaleResolution>, callback: () => T): T;
30
40
  /**
31
41
  * @param {Channel} channel
42
+ * @param {import("../view/view.js").default} [hostView]
32
43
  */
33
- constructor(channel: import("../spec/channel.js").Channel);
44
+ constructor(channel: import("../spec/channel.js").Channel, hostView?: import("../view/view.js").default);
34
45
  channel: import("../spec/channel.js").Channel;
35
46
  /** @type {import("../spec/channel.js").Type} Data type (quantitative, nominal, etc...) */
36
47
  type: import("../spec/channel.js").Type;
@@ -1 +1 @@
1
- {"version":3,"file":"scaleResolution.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleResolution.js"],"names":[],"mappings":"AAkDA;;;;;;;;GAQG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AACH;IAwDI;;OAEG;IACH,2DAkCC;IAjCG,8CAAsB;IACtB,0FAA0F;IAC1F,MADW,OAAO,oBAAoB,EAAE,IAAI,CAC5B;IAEhB,iEAAiE;IACjE,MADW,MAAM,CACI;IAoEzB,2BASC;IAwBD;;;;;;;OAOG;IACH,4KAEC;IAED;;;OAGG;IACH,+KAEC;IAiCD,sCA0CC;IA0JD;;;OAGG;IACH,uBAHW,qBAAqB,GACnB,MAAM,OAAO,CAazB;IAED,gBAKC;IAkDD;;;;OAIG;IACH,0CAJW,OAAO,sBAAsB,EAAE,OAAO,aACtC,QAAQ,CAAC,OAAO,qBAAqB,EAAE,aAAa,CAAC,GACnD,MAAM,IAAI,CAkCtB;IAED;;OAEG;IACH,qCAEC;IAED,+BAiBC;IAmDD;;;;;;;;;;;OAWG;IACH,0BALa;QACR,QAAQ,EAAE,OAAO,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;QACnE,oBAAoB,EAAE,OAAO,CAAA;KAC9B,CAeH;IAED;;;;;;;OAOG;IACH,wBAFa,OAAO,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS,CAShE;IAwFD;;;;;OAKG;IACH,oBAYC;IAED;;;;;OAKG;IACH,0BA2BC;IA+HD;;OAEG;IACH;eAj4BkC,OAAO,kBAAkB,EAAE,KAAK;MAw4BjE;IAED;;;;;;OAMG;IACH;eAj5BkC,OAAO,kBAAkB,EAAE,KAAK;MAm5BjE;IAED;;;;OAIG;IACH;eA15BkC,OAAO,kBAAkB,EAAE,KAAK;MAm6BjE;IAED,mBAEC;IAED;;;;OAIG;IACH,+DAEC;IAED;;OAEG;IACH,oBAFa,mFAA6B,CASzC;IAED;;OAEG;IACH;;;;MAqBC;IAED;;;;OAIG;IACH,oBAEC;IAED;;OAEG;IACH,sBASC;IAED;;;;;;;OAOG;IACH,kBALW,MAAM,eACN,MAAM,OACN,MAAM,GACJ,OAAO,CAInB;IAED;;;;;;OAMG;IACH,eAJW,mFAA6B,aAC7B,OAAO,GAAG,MAAM,iBAK1B;IAED;;;;OAIG;IACH,qBAEC;IAED;;;;;OAKG;IACH,uBAEC;IAED;;;;;;;OAOG;IACH,wBAoBC;IAED;;;;;OAKG;IACH,uBAFW,MAAM,yDAUhB;IAED;;OAEG;IACH,iBAFW,MAAM,yDAIhB;IAED;;;OAGG;IACH,qBAHW,MAAM,+CAAmB,GACvB,MAAM,CAIlB;IAED;;;OAGG;IACH,8BAHW,kFAA4B,GAC1B,MAAM,EAAE,CAOpB;;CACJ;kCA1oC+B,CAAC,SAApB,6CAAkB;;;;UAGrB,OAAO,qBAAqB,EAAE,OAAO;aACrC,CAAC;gBACD,OAAO,oBAAoB,EAAE,mBAAmB;yBAChD,OAAO;;sBAlCV,+BAA+B;sBAA/B,+BAA+B;wBAA/B,+BAA+B;wBAA/B,+BAA+B;6BAA/B,+BAA+B"}
1
+ {"version":3,"file":"scaleResolution.d.ts","sourceRoot":"","sources":["../../../src/scales/scaleResolution.js"],"names":[],"mappings":"AAoDA;;;;;;;;GAQG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AACH;IA6cI;;;;;;;;OAQG;IACH,uBALa,CAAC,eACH,QAAQ,CAAC,eAAe,CAAC,YACzB,MAAM,CAAC,GACL,CAAC,CAqBb;IAlaD;;;OAGG;IACH,sEAFW,OAAO,iBAAiB,EAAE,OAAO,EAsC3C;IAnCG,8CAAsB;IACtB,0FAA0F;IAC1F,MADW,OAAO,oBAAoB,EAAE,IAAI,CAC5B;IAEhB,iEAAiE;IACjE,MADW,MAAM,CACI;IA0EzB,2BASC;IAwBD;;;;;;;OAOG;IACH,4KAEC;IAED;;;OAGG;IACH,+KAEC;IAiCD,sCA6CC;IA+MD;;;OAGG;IACH,uBAHW,qBAAqB,GACnB,MAAM,OAAO,CAazB;IAED,gBAMC;IAoFD;;;;OAIG;IACH,0CAJW,OAAO,sBAAsB,EAAE,OAAO,aACtC,QAAQ,CAAC,OAAO,qBAAqB,EAAE,aAAa,CAAC,GACnD,MAAM,IAAI,CAkCtB;IAED;;OAEG;IACH,qCAEC;IAED,+BAiBC;IAuED;;;;;;;;;;;OAWG;IACH,0BALa;QACR,QAAQ,EAAE,OAAO,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;QACnE,oBAAoB,EAAE,OAAO,CAAA;KAC9B,CAeH;IAED;;;;;;;OAOG;IACH,wBAFa,OAAO,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS,CAShE;IAgGD;;;;;OAKG;IACH,oBAYC;IAED;;;;;OAKG;IACH,0BA0BC;IAoID;;OAEG;IACH;eAlhCkC,OAAO,kBAAkB,EAAE,KAAK;MAyhCjE;IAED;;;;;;OAMG;IACH;eAliCkC,OAAO,kBAAkB,EAAE,KAAK;MAyiCjE;IAED;;;;OAIG;IACH;eAhjCkC,OAAO,kBAAkB,EAAE,KAAK;MAyjCjE;IAED,mBASC;IAED;;;;OAIG;IACH,+DAEC;IAED;;OAEG;IACH,oBAFa,mFAA6B,CASzC;IAED;;OAEG;IACH;;;;MAqBC;IAED;;;;OAIG;IACH,oBAEC;IAED;;OAEG;IACH,sBASC;IAED;;;;;;;OAOG;IACH,kBALW,MAAM,eACN,MAAM,OACN,MAAM,GACJ,OAAO,CAInB;IAED;;;;;;OAMG;IACH,eAJW,mFAA6B,aAC7B,OAAO,GAAG,MAAM,iBAK1B;IAED;;;;OAIG;IACH,qBAEC;IAED;;;;;OAKG;IACH,uBAEC;IAED;;;;;;;OAOG;IACH,wBAoBC;IAED;;;;;OAKG;IACH,uBAFW,MAAM,yDAUhB;IAED;;OAEG;IACH,iBAFW,MAAM,yDAIhB;IAED;;;OAGG;IACH,qBAHW,MAAM,+CAAmB,GACvB,MAAM,CAIlB;IAED;;;OAGG;IACH,8BAHW,kFAA4B,GAC1B,MAAM,EAAE,CAOpB;;CACJ;kCAvyC+B,CAAC,SAApB,6CAAkB;;;;UAGrB,OAAO,qBAAqB,EAAE,OAAO;aACrC,CAAC;gBACD,OAAO,oBAAoB,EAAE,mBAAmB;yBAChD,OAAO;;sBApCV,+BAA+B;sBAA/B,+BAA+B;wBAA/B,+BAA+B;wBAA/B,+BAA+B;6BAA/B,+BAA+B"}