@genome-spy/core 0.77.0 → 0.78.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 (48) hide show
  1. package/dist/bundle/{esm-CscjKVDc.js → esm-DAnOffpD.js} +1 -1
  2. package/dist/bundle/{esm-0dYHNV_D.js → esm-DNtC3H80.js} +1 -1
  3. package/dist/bundle/{esm-CRMf_I9V.js → esm-DVOHLB1e.js} +1 -1
  4. package/dist/bundle/esm-NIYEaYkc.js +1221 -0
  5. package/dist/bundle/index.es.js +2816 -2703
  6. package/dist/bundle/index.js +57 -54
  7. package/dist/schema.json +825 -112
  8. package/dist/src/genomeSpy/headlessBootstrap.d.ts.map +1 -1
  9. package/dist/src/genomeSpy/headlessBootstrap.js +2 -0
  10. package/dist/src/genomeSpy/interactionController.d.ts +4 -1
  11. package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
  12. package/dist/src/genomeSpy/interactionController.js +57 -13
  13. package/dist/src/genomeSpyBase.d.ts.map +1 -1
  14. package/dist/src/genomeSpyBase.js +5 -1
  15. package/dist/src/scales/domainExpressions.d.ts +21 -0
  16. package/dist/src/scales/domainExpressions.d.ts.map +1 -0
  17. package/dist/src/scales/domainExpressions.js +43 -0
  18. package/dist/src/scales/domainPlanner.d.ts +12 -1
  19. package/dist/src/scales/domainPlanner.d.ts.map +1 -1
  20. package/dist/src/scales/domainPlanner.js +55 -36
  21. package/dist/src/scales/scaleInstanceManager.d.ts +1 -0
  22. package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
  23. package/dist/src/scales/scaleInstanceManager.js +5 -0
  24. package/dist/src/scales/scalePropsResolver.d.ts +6 -1
  25. package/dist/src/scales/scalePropsResolver.d.ts.map +1 -1
  26. package/dist/src/scales/scalePropsResolver.js +35 -10
  27. package/dist/src/scales/scaleResolution.d.ts +16 -0
  28. package/dist/src/scales/scaleResolution.d.ts.map +1 -1
  29. package/dist/src/scales/scaleResolution.js +136 -16
  30. package/dist/src/scales/scaleRules.d.ts +10 -0
  31. package/dist/src/scales/scaleRules.d.ts.map +1 -1
  32. package/dist/src/scales/scaleRules.js +38 -1
  33. package/dist/src/scales/viewLevelScaleConfig.d.ts +45 -0
  34. package/dist/src/scales/viewLevelScaleConfig.d.ts.map +1 -0
  35. package/dist/src/scales/viewLevelScaleConfig.js +138 -0
  36. package/dist/src/spec/scale.d.ts +19 -6
  37. package/dist/src/spec/view.d.ts +11 -0
  38. package/dist/src/styles/genome-spy.css +4 -1
  39. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  40. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  41. package/dist/src/styles/genome-spy.css.js +4 -1
  42. package/dist/src/utils/ui/tooltip.d.ts +4 -0
  43. package/dist/src/utils/ui/tooltip.d.ts.map +1 -1
  44. package/dist/src/utils/ui/tooltip.js +35 -10
  45. package/dist/src/view/containerMutationHelper.d.ts.map +1 -1
  46. package/dist/src/view/containerMutationHelper.js +11 -3
  47. package/package.json +2 -2
  48. package/dist/bundle/esm-C49STiCR.js +0 -1248
@@ -1 +1 @@
1
- {"version":3,"file":"headlessBootstrap.d.ts","sourceRoot":"","sources":["../../../src/genomeSpy/headlessBootstrap.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"headlessBootstrap.d.ts","sourceRoot":"","sources":["../../../src/genomeSpy/headlessBootstrap.js"],"names":[],"mappings":"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"}
@@ -14,6 +14,7 @@ import { resolveBaseConfig } from "../config/resolveConfig.js";
14
14
  import { createViewContext } from "./viewContextFactory.js";
15
15
  import { ViewFactory, VIEW_ROOT_NAME } from "../view/viewFactory.js";
16
16
  import { ensureAssembliesForView } from "../genome/assemblyPreflight.js";
17
+ import { attachViewLevelScaleConfigs } from "../scales/viewLevelScaleConfig.js";
17
18
  import {
18
19
  configureViewHierarchy,
19
20
  configureViewOpacity,
@@ -196,6 +197,7 @@ export async function createHeadlessEngine(spec, options = {}) {
196
197
  VIEW_ROOT_NAME
197
198
  );
198
199
 
200
+ attachViewLevelScaleConfigs(view);
199
201
  await ensureAssembliesForView(view, context.genomeStore);
200
202
  prepareViewHierarchy(view);
201
203
 
@@ -30,7 +30,10 @@ export default class InteractionController {
30
30
  * @param {MouseEvent} [mouseEvent]
31
31
  */
32
32
  resumeHoverTracking(mouseEvent?: MouseEvent): void;
33
- registerInteractionEvents(): void;
33
+ /**
34
+ * @returns {() => void}
35
+ */
36
+ registerInteractionEvents(): () => void;
34
37
  /**
35
38
  * This method should be called in a mouseMove handler. If not called, the
36
39
  * tooltip will be hidden.
@@ -1 +1 @@
1
- {"version":3,"file":"interactionController.d.ts","sourceRoot":"","sources":["../../../src/genomeSpy/interactionController.js"],"names":[],"mappings":"AAWA;IAoCI;;;;;;;;;;OAUG;IACH,mIATG;QAAmD,QAAQ,EAAnD,OAAO,iBAAiB,EAAE,OAAO;QACe,QAAQ,EAAxD,OAAO,sBAAsB,EAAE,OAAO;QACY,OAAO,EAAzD,OAAO,wBAAwB,EAAE,OAAO;QACQ,QAAQ,EAAxD,OAAO,sBAAsB,EAAE,OAAO;QACM,SAAS,EAArD,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI;QAC6C,eAAe,EAA9F,MAAM,CAAC,MAAM,EAAE,OAAO,8BAA8B,EAAE,cAAc,CAAC;QACjD,wBAAwB,EAA5C,MAAM,IAAI;QACY,mBAAmB,EAAzC,MAAM,MAAM;KACtB,EAiCA;IAED;cA3DkB,OAAO,kBAAkB,EAAE,OAAO;eAAS,OAAO,qBAAqB,EAAE,KAAK;kBAAY,MAAM;MA6DjH;IAED,6BAQC;IAED;;OAEG;IACH,iCAFW,UAAU,QA8CpB;IAED,kCAycC;IA+JD;;;;;;;OAOG;IACH,cAFa,CAAC,SAFH,CAAC,cACD,CAAS,IAAC,EAAD,CAAC,KAAE,OAAO,CAAC,MAAM,GAAG,WAAW,GAAG,OAAO,KAAK,EAAE,cAAc,CAAC,QAgBlF;;CACJ;8BAGY;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC"}
1
+ {"version":3,"file":"interactionController.d.ts","sourceRoot":"","sources":["../../../src/genomeSpy/interactionController.js"],"names":[],"mappings":"AAWA;IA2CI;;;;;;;;;;OAUG;IACH,mIATG;QAAmD,QAAQ,EAAnD,OAAO,iBAAiB,EAAE,OAAO;QACe,QAAQ,EAAxD,OAAO,sBAAsB,EAAE,OAAO;QACY,OAAO,EAAzD,OAAO,wBAAwB,EAAE,OAAO;QACQ,QAAQ,EAAxD,OAAO,sBAAsB,EAAE,OAAO;QACM,SAAS,EAArD,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI;QAC6C,eAAe,EAA9F,MAAM,CAAC,MAAM,EAAE,OAAO,8BAA8B,EAAE,cAAc,CAAC;QACjD,wBAAwB,EAA5C,MAAM,IAAI;QACY,mBAAmB,EAAzC,MAAM,MAAM;KACtB,EAiCA;IAED;cAlEkB,OAAO,kBAAkB,EAAE,OAAO;eAAS,OAAO,qBAAqB,EAAE,KAAK;kBAAY,MAAM;MAoEjH;IAED,6BAQC;IAED;;OAEG;IACH,iCAFW,UAAU,QA8CpB;IAED;;OAEG;IACH,6BAFa,MAAM,IAAI,CA6etB;IA+JD;;;;;;;OAOG;IACH,cAFa,CAAC,SAFH,CAAC,cACD,CAAS,IAAC,EAAD,CAAC,KAAE,OAAO,CAAC,MAAM,GAAG,WAAW,GAAG,OAAO,KAAK,EAAE,cAAc,CAAC,QAgBlF;;CACJ;8BAGY;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC"}
@@ -45,6 +45,13 @@ export default class InteractionController {
45
45
  #suppressTooltipUntilMouseMove = false;
46
46
  #hoverTrackingSuspensionCount = 0;
47
47
  #postRenderHoverRefreshRequested = false;
48
+
49
+ #dismissStickyTooltip() {
50
+ this.#tooltip.sticky = false;
51
+ this.#suppressTooltipUntilMouseMove = true;
52
+ this.#tooltip.clear();
53
+ }
54
+
48
55
  /**
49
56
  * @param {object} options
50
57
  * @param {import("../view/view.js").default} options.viewRoot
@@ -153,14 +160,49 @@ export default class InteractionController {
153
160
  this.#cursorManager.clear();
154
161
  }
155
162
 
163
+ /**
164
+ * @returns {() => void}
165
+ */
156
166
  registerInteractionEvents() {
157
167
  const canvas = this.#glHelper.canvas;
168
+ /** @type {Array<() => void>} */
169
+ const removers = [];
170
+
171
+ /**
172
+ * @param {EventTarget} target
173
+ * @param {string} type
174
+ * @param {EventListener} listener
175
+ * @param {AddEventListenerOptions} [options]
176
+ */
177
+ const addListener = (target, type, listener, options) => {
178
+ target.addEventListener(type, listener, options);
179
+ removers.push(() =>
180
+ target.removeEventListener(type, listener, options)
181
+ );
182
+ };
158
183
 
159
184
  let lastWheelEvent = performance.now();
160
185
  let longPressTriggered = false;
161
186
  /** @type {{ pointerCount: 1 | 2, centerX: number, centerY: number, distance: number } | undefined} */
162
187
  let previousTouchGesture;
163
188
 
189
+ if (globalThis.document?.addEventListener) {
190
+ addListener(
191
+ document,
192
+ "mousedown",
193
+ (/** @type {MouseEvent} */ event) => {
194
+ if (
195
+ this.#tooltip.sticky &&
196
+ !event.composedPath().includes(canvas) &&
197
+ !this.#tooltip.containsEvent(event)
198
+ ) {
199
+ this.#dismissStickyTooltip();
200
+ }
201
+ },
202
+ { capture: true }
203
+ );
204
+ }
205
+
164
206
  /**
165
207
  * @param {Point} point
166
208
  * @param {import("../utils/interactionEvent.js").InteractionUiEvent} uiEvent
@@ -343,15 +385,13 @@ export default class InteractionController {
343
385
  }
344
386
  };
345
387
 
346
- canvas.addEventListener("mousedown", (/** @type {MouseEvent} */ e) => {
388
+ addListener(canvas, "mousedown", (/** @type {MouseEvent} */ e) => {
347
389
  this.#mouseDownCoords = Point.fromMouseEvent(e);
348
390
  this.#longPressPending = false;
349
391
  const hasModifier = e.shiftKey || e.ctrlKey || e.metaKey;
350
392
 
351
393
  if (this.#tooltip.sticky) {
352
- this.#tooltip.sticky = false;
353
- this.#suppressTooltipUntilMouseMove = true;
354
- this.#tooltip.clear();
394
+ this.#dismissStickyTooltip();
355
395
  // Dismiss the sticky tooltip before routing the press so the
356
396
  // click only affects the tooltip state.
357
397
  longPressTriggered = true;
@@ -427,7 +467,7 @@ export default class InteractionController {
427
467
  "mousemove",
428
468
  "contextmenu",
429
469
  "dblclick",
430
- ].forEach((type) => canvas.addEventListener(type, listener));
470
+ ].forEach((type) => addListener(canvas, type, listener));
431
471
 
432
472
  /**
433
473
  * @param {number} clientX
@@ -574,25 +614,23 @@ export default class InteractionController {
574
614
  previousTouchGesture = readTouchGesture(touchEvent.touches);
575
615
  };
576
616
 
577
- canvas.addEventListener("touchstart", handleTouchStartOrMove, {
617
+ addListener(canvas, "touchstart", handleTouchStartOrMove, {
578
618
  passive: false,
579
619
  });
580
- canvas.addEventListener("touchmove", handleTouchStartOrMove, {
620
+ addListener(canvas, "touchmove", handleTouchStartOrMove, {
581
621
  passive: false,
582
622
  });
583
- canvas.addEventListener("touchend", handleTouchEndOrCancel, {
623
+ addListener(canvas, "touchend", handleTouchEndOrCancel, {
584
624
  passive: false,
585
625
  });
586
- canvas.addEventListener("touchcancel", handleTouchEndOrCancel, {
626
+ addListener(canvas, "touchcancel", handleTouchEndOrCancel, {
587
627
  passive: false,
588
628
  });
589
629
 
590
630
  // Prevent text selections etc while dragging
591
- canvas.addEventListener("dragstart", (event) =>
592
- event.stopPropagation()
593
- );
631
+ addListener(canvas, "dragstart", (event) => event.stopPropagation());
594
632
 
595
- canvas.addEventListener("mouseout", (event) => {
633
+ addListener(canvas, "mouseout", (event) => {
596
634
  if (this.#isInteractionFrozen()) {
597
635
  return;
598
636
  }
@@ -610,6 +648,12 @@ export default class InteractionController {
610
648
  this.#tooltip.clear();
611
649
  this.#currentHover = null;
612
650
  });
651
+
652
+ return () => {
653
+ for (const remove of removers) {
654
+ remove();
655
+ }
656
+ };
613
657
  }
614
658
 
615
659
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"genomeSpyBase.d.ts","sourceRoot":"","sources":["../../src/genomeSpyBase.js"],"names":[],"mappings":"AA+CA;;;GAGG;AAEH;IAoBI;;;;;OAKG;IAEH;;;;;OAKG;IACH,uBAJW,WAAW,qDAEX,OAAO,qBAAqB,EAAE,YAAY,EA0CpD;IAvCG,uBAA0B;IAC1B,oDAAsB;IAItB,sCAAsC;IACtC,wCAAgB;IAEhB,yBAAoC;IAEpC,4CAA4C;IAC5C,oBADW,CAAC,CAAS,IAAM,EAAN,MAAM,KAAE,MAAM,EAAE,CAAC,EAAE,CACZ;IAE5B,mBAAoD;IAEpD,0BAA0B;IAC1B,aADW,WAAW,CACM;IAE5B;;;;;OAKG;IACH,yBAFU,CAAC,IAAI,qEAAM,KAAK,OAAO,CAE8B;IAE/D,oFAAoF;IACpF,iBADW,MAAM,CAAC,MAAM,EAAE,OAAO,6BAA6B,EAAE,cAAc,CAAC,CAK9E;IAED,mBAAmB;IACnB,8EAAyB;IAIzB,YAAkC;IAatC;;;OAGG;IACH,oCAFW,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,EAAE,QAIjC;IAED;;OAEG;IACH,+BAFW,MAAM,YAShB;IAED;;;;OAIG;IACH,sBAHW,MAAM,QACN,GAAG,EAAE,QAYf;IAED;;;;OAIG;IACH,eAFW,MAAM,oFAIhB;IAED;;;OAGG;IACH,uBAHW,MAAM,YACN,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,QAI9B;IAED;;;OAGG;IACH,0BAHW,MAAM,YACN,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,QAI9B;IAED;;;;;OAKG;IACH,gBAHW,kBAAkB,YAClB,GAAG,QAMb;IAmEG,iDAAsB;IAQ1B;;OAEG;IACH,gBAqBC;IA0ND;;;OAGG;IACH,UAFa,OAAO,CAAC,OAAO,CAAC,CAyC5B;IAED,2CAiBC;IAED;;;;;OAKG;IACH,8BAFW,WAAW,iBAerB;IAED;;;;;;;OAOG;IACH,cAFa,CAAC,SAFH,CAAC,cACD,CAAS,IAAC,EAAD,CAAC,KAAE,OAAO,CAAC,MAAM,GAAG,WAAW,GAAG,OAAO,KAAK,EAAE,cAAc,CAAC,QAKlF;IAED;;;;;;;;OAQG;IACH,4BANW,MAAM,kBACN,MAAM,qBACN,MAAM,eACN,MAAM,UAuBhB;IAED;;;MAEC;IAED;eACwB,MAAM,GAAG,SAAS;gBAAU,MAAM,GAAG,SAAS;MAcrE;IAED,sBAGC;IAED,kBAEC;IAED,iCAEC;IAED,oEAYC;IAED,uFAWC;;CACJ;;;;iCAzpBY,eAAe,GAAG,QAAQ,GAAG,gBAAgB,GAAG,kBAAkB;4BAvBnC,uBAAuB;qBAP9C,qBAAqB;wBAElB,yBAAyB;qBAL5B,oBAAoB"}
1
+ {"version":3,"file":"genomeSpyBase.d.ts","sourceRoot":"","sources":["../../src/genomeSpyBase.js"],"names":[],"mappings":"AAgDA;;;GAGG;AAEH;IAoBI;;;;;OAKG;IAEH;;;;;OAKG;IACH,uBAJW,WAAW,qDAEX,OAAO,qBAAqB,EAAE,YAAY,EA0CpD;IAvCG,uBAA0B;IAC1B,oDAAsB;IAItB,sCAAsC;IACtC,wCAAgB;IAEhB,yBAAoC;IAEpC,4CAA4C;IAC5C,oBADW,CAAC,CAAS,IAAM,EAAN,MAAM,KAAE,MAAM,EAAE,CAAC,EAAE,CACZ;IAE5B,mBAAoD;IAEpD,0BAA0B;IAC1B,aADW,WAAW,CACM;IAE5B;;;;;OAKG;IACH,yBAFU,CAAC,IAAI,qEAAM,KAAK,OAAO,CAE8B;IAE/D,oFAAoF;IACpF,iBADW,MAAM,CAAC,MAAM,EAAE,OAAO,6BAA6B,EAAE,cAAc,CAAC,CAK9E;IAED,mBAAmB;IACnB,8EAAyB;IAIzB,YAAkC;IAatC;;;OAGG;IACH,oCAFW,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,EAAE,QAIjC;IAED;;OAEG;IACH,+BAFW,MAAM,YAShB;IAED;;;;OAIG;IACH,sBAHW,MAAM,QACN,GAAG,EAAE,QAYf;IAED;;;;OAIG;IACH,eAFW,MAAM,oFAIhB;IAED;;;OAGG;IACH,uBAHW,MAAM,YACN,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,QAI9B;IAED;;;OAGG;IACH,0BAHW,MAAM,YACN,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,QAI9B;IAED;;;;;OAKG;IACH,gBAHW,kBAAkB,YAClB,GAAG,QAMb;IAmEG,iDAAsB;IAQ1B;;OAEG;IACH,gBAqBC;IA2ND;;;OAGG;IACH,UAFa,OAAO,CAAC,OAAO,CAAC,CA2C5B;IAED,2CAiBC;IAED;;;;;OAKG;IACH,8BAFW,WAAW,iBAerB;IAED;;;;;;;OAOG;IACH,cAFa,CAAC,SAFH,CAAC,cACD,CAAS,IAAC,EAAD,CAAC,KAAE,OAAO,CAAC,MAAM,GAAG,WAAW,GAAG,OAAO,KAAK,EAAE,cAAc,CAAC,QAKlF;IAED;;;;;;;;OAQG;IACH,4BANW,MAAM,kBACN,MAAM,qBACN,MAAM,eACN,MAAM,UAuBhB;IAED;;;MAEC;IAED;eACwB,MAAM,GAAG,SAAS;gBAAU,MAAM,GAAG,SAAS;MAcrE;IAED,sBAGC;IAED,kBAEC;IAED,iCAEC;IAED,oEAYC;IAED,uFAWC;;CACJ;;;;iCA5pBY,eAAe,GAAG,QAAQ,GAAG,gBAAgB,GAAG,kBAAkB;4BAxBnC,uBAAuB;qBAP9C,qBAAqB;wBAElB,yBAAyB;qBAL5B,oBAAoB"}
@@ -34,6 +34,7 @@ import { validateSelectorConstraints } from "./view/viewSelectors.js";
34
34
  import { resolveEmbedParam } from "./paramRuntime/embedParamApi.js";
35
35
  import SingleAxisWindowedSource from "./data/sources/lazy/singleAxisWindowedSource.js";
36
36
  import { ensureAssembliesForView } from "./genome/assemblyPreflight.js";
37
+ import { attachViewLevelScaleConfigs } from "./scales/viewLevelScaleConfig.js";
37
38
  import { resolveRootGenomeConfig } from "./genome/rootGenomeConfig.js";
38
39
  import { awaitSubtreeLazyReady } from "./view/dataReadiness.js";
39
40
  import { INTERNAL_DEFAULT_CONFIG } from "./config/defaultConfig.js";
@@ -439,6 +440,7 @@ export default class GenomeSpy {
439
440
  // Reminder: assemblies must be ensured after view creation (imports and
440
441
  // inheritance resolved), but before any code path that may touch scales
441
442
  // (e.g. step-based sizes, dynamic opacity, encoder initialization).
443
+ attachViewLevelScaleConfigs(this.viewRoot);
442
444
  await ensureAssembliesForView(this.viewRoot, this.genomeStore);
443
445
 
444
446
  this.#loadingStatusRegistry.set(this.viewRoot, "loading");
@@ -535,7 +537,9 @@ export default class GenomeSpy {
535
537
 
536
538
  await this.#prepareViewsAndData();
537
539
 
538
- this.#interactionController.registerInteractionEvents();
540
+ this.#destructionCallbacks.push(
541
+ this.#interactionController.registerInteractionEvents()
542
+ );
539
543
 
540
544
  this.computeLayout();
541
545
  this.animator.requestRender();
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Resolves a configured domain value recursively.
3
+ *
4
+ * This supports top-level expression refs as well as arrays containing mixed
5
+ * constants and expression refs.
6
+ *
7
+ * @param {any} value
8
+ * @param {{ createExpression: (expr: string) => () => any }} paramRuntime
9
+ * @returns {any}
10
+ */
11
+ export function resolveConfiguredDomainValue(value: any, paramRuntime: {
12
+ createExpression: (expr: string) => () => any;
13
+ }): any;
14
+ /**
15
+ * Collects all expression refs nested inside a configured domain value.
16
+ *
17
+ * @param {any} value
18
+ * @returns {import("../spec/parameter.js").ExprRef[]}
19
+ */
20
+ export function collectConfiguredDomainExprRefs(value: any): import("../spec/parameter.js").ExprRef[];
21
+ //# sourceMappingURL=domainExpressions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domainExpressions.d.ts","sourceRoot":"","sources":["../../../src/scales/domainExpressions.js"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,oDAJW,GAAG,gBACH;IAAE,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,CAAA;CAAE,GAC/C,GAAG,CAcf;AAED;;;;;GAKG;AACH,uDAHW,GAAG,GACD,OAAO,sBAAsB,EAAE,OAAO,EAAE,CAYpD"}
@@ -0,0 +1,43 @@
1
+ import { isExprRef } from "../paramRuntime/paramUtils.js";
2
+
3
+ /**
4
+ * Resolves a configured domain value recursively.
5
+ *
6
+ * This supports top-level expression refs as well as arrays containing mixed
7
+ * constants and expression refs.
8
+ *
9
+ * @param {any} value
10
+ * @param {{ createExpression: (expr: string) => () => any }} paramRuntime
11
+ * @returns {any}
12
+ */
13
+ export function resolveConfiguredDomainValue(value, paramRuntime) {
14
+ if (isExprRef(value)) {
15
+ return paramRuntime.createExpression(value.expr)();
16
+ }
17
+
18
+ if (Array.isArray(value)) {
19
+ return value.map((item) =>
20
+ resolveConfiguredDomainValue(item, paramRuntime)
21
+ );
22
+ }
23
+
24
+ return value;
25
+ }
26
+
27
+ /**
28
+ * Collects all expression refs nested inside a configured domain value.
29
+ *
30
+ * @param {any} value
31
+ * @returns {import("../spec/parameter.js").ExprRef[]}
32
+ */
33
+ export function collectConfiguredDomainExprRefs(value) {
34
+ if (isExprRef(value)) {
35
+ return [value];
36
+ }
37
+
38
+ if (Array.isArray(value)) {
39
+ return value.flatMap((item) => collectConfiguredDomainExprRefs(item));
40
+ }
41
+
42
+ return [];
43
+ }
@@ -10,7 +10,9 @@ export function isSelectionDomainRef(domain: any): domain is SelectionDomainRef;
10
10
  * @typedef {import("../spec/scale.js").SelectionDomainRef} SelectionDomainRef
11
11
  * @typedef {import("../spec/parameter.js").ExprRef} ExprRef
12
12
  * @typedef {import("./scaleResolution.js").ScaleResolutionMember} ScaleResolutionMember
13
+ * @typedef {{ view: import("../view/view.js").default, channel: import("../spec/channel.js").ChannelWithScale, type: import("../spec/channel.js").Type, domain: import("../spec/scale.js").Scale["domain"] }} ConfiguredDomainSource
13
14
  * @typedef {() => Set<ScaleResolutionMember>} ScaleMembersGetter
15
+ * @typedef {() => ConfiguredDomainSource | undefined} ViewLevelConfiguredDomainGetter
14
16
  * @typedef {(interval: ScalarDomain | ComplexDomain) => number[]} FromComplexInterval
15
17
  * @typedef {(assembly: import("../spec/scale.js").Scale["assembly"] | undefined) => number[]} GetLocusExtent
16
18
  * @typedef {{
@@ -45,14 +47,16 @@ export default class DomainPlanner {
45
47
  * @param {ScaleMembersGetter} options.getActiveMembers Active shared-scale members used for configured domain planning.
46
48
  * @param {ScaleMembersGetter} [options.getAllMembers] All members, including inactive ones, used for conflict validation.
47
49
  * @param {ScaleMembersGetter} [options.getDataMembers] Members used for data-domain extraction; defaults to `getActiveMembers`.
50
+ * @param {ViewLevelConfiguredDomainGetter} [options.getViewLevelConfiguredDomain] View-level configured domain source.
48
51
  * @param {() => import("../spec/channel.js").Type} options.getType
49
52
  * @param {GetLocusExtent} options.getLocusExtent
50
53
  * @param {FromComplexInterval} options.fromComplexInterval
51
54
  */
52
- constructor({ getActiveMembers, getAllMembers, getDataMembers, getType, getLocusExtent, fromComplexInterval, }: {
55
+ constructor({ getActiveMembers, getAllMembers, getDataMembers, getViewLevelConfiguredDomain, getType, getLocusExtent, fromComplexInterval, }: {
53
56
  getActiveMembers: ScaleMembersGetter;
54
57
  getAllMembers?: ScaleMembersGetter;
55
58
  getDataMembers?: ScaleMembersGetter;
59
+ getViewLevelConfiguredDomain?: ViewLevelConfiguredDomainGetter;
56
60
  getType: () => import("../spec/channel.js").Type;
57
61
  getLocusExtent: GetLocusExtent;
58
62
  fromComplexInterval: FromComplexInterval;
@@ -126,7 +130,14 @@ export type ScalarDomain = import("../spec/scale.js").ScalarDomain;
126
130
  export type SelectionDomainRef = import("../spec/scale.js").SelectionDomainRef;
127
131
  export type ExprRef = import("../spec/parameter.js").ExprRef;
128
132
  export type ScaleResolutionMember = import("./scaleResolution.js").ScaleResolutionMember;
133
+ export type ConfiguredDomainSource = {
134
+ view: import("../view/view.js").default;
135
+ channel: import("../spec/channel.js").ChannelWithScale;
136
+ type: import("../spec/channel.js").Type;
137
+ domain: import("../spec/scale.js").Scale["domain"];
138
+ };
129
139
  export type ScaleMembersGetter = () => Set<ScaleResolutionMember>;
140
+ export type ViewLevelConfiguredDomainGetter = () => ConfiguredDomainSource | undefined;
130
141
  export type FromComplexInterval = (interval: ScalarDomain | ComplexDomain) => number[];
131
142
  export type GetLocusExtent = (assembly: import("../spec/scale.js").Scale["assembly"] | undefined) => number[];
132
143
  export type ConfiguredDomainResolutionState = {
@@ -1 +1 @@
1
- {"version":3,"file":"domainPlanner.d.ts","sourceRoot":"","sources":["../../../src/scales/domainPlanner.js"],"names":[],"mappings":"AAooBA;;;GAGG;AACH,6CAHW,GAAG,GACD,MAAM,IAAI,kBAAkB,CASxC;AAhnBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH;IAiCI;;;;;;;;OAQG;IACH,gHAPG;QAAoC,gBAAgB,EAA5C,kBAAkB;QACW,aAAa,GAA1C,kBAAkB;QACW,cAAc,GAA3C,kBAAkB;QAC+B,OAAO,EAAxD,MAAM,OAAO,oBAAoB,EAAE,IAAI;QACf,cAAc,EAAtC,cAAc;QACe,mBAAmB,EAAhD,mBAAmB;KAC7B,EAeA;IAED;;OAEG;IACH,6BAFa,GAAG,EAAE,CAIjB;IAED;;OAEG;IACH,8BAFW;QAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;KAAE,WAI/C;IAED,wCAGC;IAED;;OAEG;IACH,2CAFa,uBAAuB,GAAG,SAAS,CAS/C;IAED;;;MAUC;IAED,mCAIC;IAED;;;;;;OAMG;IACH,qCAJW,OAAO,kBACP,OAAO,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,GAC1C,GAAG,EAAE,CAYjB;IAED;;;;;;;OAOG;IACH,iDALW,OAAO,kBACP,OAAO,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,YAC5C;QAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;KAAE,GACnC,GAAG,EAAE,CAYjB;IAED;;;;;OAKG;IACH,8BAHW;QAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;KAAE,GACpC,WAAW,CA8BtB;IAED;;;;OAIG;IACH,iBAFY,WAAW,GAAG,SAAS,CAQlC;IAED;;;;;OAKG;IACH,4BALW,OAAO,qBAAqB,EAAE,SAAS,wBACvC,OAAO,mBACP,GAAG,EAAE,GACH,OAAO,CAgBnB;;CAkCJ;0BA7RY,OAAO,yBAAyB,EAAE,WAAW;4BAC7C,OAAO,kBAAkB,EAAE,aAAa;2BACxC,OAAO,kBAAkB,EAAE,YAAY;iCACvC,OAAO,kBAAkB,EAAE,kBAAkB;sBAC7C,OAAO,sBAAsB,EAAE,OAAO;oCACtC,OAAO,sBAAsB,EAAE,qBAAqB;iCACpD,MAAM,GAAG,CAAC,qBAAqB,CAAC;kCAChC,CAAC,QAAQ,EAAE,YAAY,GAAG,aAAa,KAAK,MAAM,EAAE;6BACpD,CAAC,QAAQ,EAAE,OAAO,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,GAAG,SAAS,KAAK,MAAM,EAAE;8CAChF;IACR,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,YAAY,EAAE,uBAAuB,GAAG,SAAS,CAAC;IAClD,gBAAgB,EAAE,GAAG,CAAC;IACtB,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,gBAAgB,EAAE,OAAO,CAAC;CAC3B;+CACS;IACR,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;CACrB,GAAG;IACF,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,GAAG,GAAG,GAAG,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,GAAG,CAAC;CACd;sCACS;IACR,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,GAAG,GAAG,GAAG,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,GAAG,CAAC;CACd"}
1
+ {"version":3,"file":"domainPlanner.d.ts","sourceRoot":"","sources":["../../../src/scales/domainPlanner.js"],"names":[],"mappings":"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"}
@@ -11,7 +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
+ import { resolveConfiguredDomainValue } from "./domainExpressions.js";
15
15
  import { getAccessorDomainKey, isScaleAccessor } from "../encoder/accessor.js";
16
16
  import { getEncoderAccessors, getPrimaryChannel } from "../encoder/encoder.js";
17
17
  import {
@@ -36,7 +36,9 @@ import {
36
36
  * @typedef {import("../spec/scale.js").SelectionDomainRef} SelectionDomainRef
37
37
  * @typedef {import("../spec/parameter.js").ExprRef} ExprRef
38
38
  * @typedef {import("./scaleResolution.js").ScaleResolutionMember} ScaleResolutionMember
39
+ * @typedef {{ view: import("../view/view.js").default, channel: import("../spec/channel.js").ChannelWithScale, type: import("../spec/channel.js").Type, domain: import("../spec/scale.js").Scale["domain"] }} ConfiguredDomainSource
39
40
  * @typedef {() => Set<ScaleResolutionMember>} ScaleMembersGetter
41
+ * @typedef {() => ConfiguredDomainSource | undefined} ViewLevelConfiguredDomainGetter
40
42
  * @typedef {(interval: ScalarDomain | ComplexDomain) => number[]} FromComplexInterval
41
43
  * @typedef {(assembly: import("../spec/scale.js").Scale["assembly"] | undefined) => number[]} GetLocusExtent
42
44
  * @typedef {{
@@ -76,6 +78,9 @@ export default class DomainPlanner {
76
78
  /** @type {ScaleMembersGetter} */
77
79
  #getDataMembers;
78
80
 
81
+ /** @type {ViewLevelConfiguredDomainGetter | undefined} */
82
+ #getViewLevelConfiguredDomain;
83
+
79
84
  /** @type {() => import("../spec/channel.js").Type} */
80
85
  #getType;
81
86
 
@@ -104,6 +109,7 @@ export default class DomainPlanner {
104
109
  * @param {ScaleMembersGetter} options.getActiveMembers Active shared-scale members used for configured domain planning.
105
110
  * @param {ScaleMembersGetter} [options.getAllMembers] All members, including inactive ones, used for conflict validation.
106
111
  * @param {ScaleMembersGetter} [options.getDataMembers] Members used for data-domain extraction; defaults to `getActiveMembers`.
112
+ * @param {ViewLevelConfiguredDomainGetter} [options.getViewLevelConfiguredDomain] View-level configured domain source.
107
113
  * @param {() => import("../spec/channel.js").Type} options.getType
108
114
  * @param {GetLocusExtent} options.getLocusExtent
109
115
  * @param {FromComplexInterval} options.fromComplexInterval
@@ -112,6 +118,7 @@ export default class DomainPlanner {
112
118
  getActiveMembers,
113
119
  getAllMembers,
114
120
  getDataMembers,
121
+ getViewLevelConfiguredDomain,
115
122
  getType,
116
123
  getLocusExtent,
117
124
  fromComplexInterval,
@@ -119,6 +126,7 @@ export default class DomainPlanner {
119
126
  this.#getActiveMembers = getActiveMembers;
120
127
  this.#getAllMembers = getAllMembers ?? getActiveMembers;
121
128
  this.#getDataMembers = getDataMembers ?? getActiveMembers;
129
+ this.#getViewLevelConfiguredDomain = getViewLevelConfiguredDomain;
122
130
  this.#getType = getType;
123
131
  this.#getLocusExtent = getLocusExtent;
124
132
  this.#fromComplexInterval = fromComplexInterval;
@@ -232,6 +240,7 @@ export default class DomainPlanner {
232
240
 
233
241
  const configuredDomain = resolveConfiguredDomain(
234
242
  this.#getActiveMembers(),
243
+ this.#getViewLevelConfiguredDomain?.(),
235
244
  this.#fromComplexInterval,
236
245
  includeSelectionInitial
237
246
  );
@@ -319,6 +328,7 @@ export default class DomainPlanner {
319
328
 
320
329
  /**
321
330
  * @param {Set<ScaleResolutionMember>} members
331
+ * @param {ConfiguredDomainSource | undefined} viewLevelDomain
322
332
  * @param {(interval: ScalarDomain | ComplexDomain) => number[]} fromComplexInterval
323
333
  * @param {boolean} includeSelectionInitial
324
334
  * @returns {{
@@ -328,6 +338,7 @@ export default class DomainPlanner {
328
338
  */
329
339
  function resolveConfiguredDomain(
330
340
  members,
341
+ viewLevelDomain,
331
342
  fromComplexInterval,
332
343
  includeSelectionInitial
333
344
  ) {
@@ -344,41 +355,62 @@ function resolveConfiguredDomain(
344
355
  hasLiteralDomain: false,
345
356
  };
346
357
 
347
- for (const member of domainMembers) {
348
- const resolved = resolveConfiguredDomainMember(
349
- member,
358
+ if (viewLevelDomain?.domain !== undefined) {
359
+ const resolved = resolveConfiguredDomainSource(
360
+ viewLevelDomain,
350
361
  fromComplexInterval,
351
362
  includeSelectionInitial
352
363
  );
364
+ mergeConfiguredDomainResolution(state, resolved);
365
+ }
353
366
 
354
- if (resolved.kind === "selection") {
355
- mergeSelectionConfiguredDomain(state, resolved);
356
- continue;
357
- }
367
+ for (const member of domainMembers) {
368
+ const resolved = resolveConfiguredDomainSource(
369
+ {
370
+ view: member.view,
371
+ channel: member.channel,
372
+ type: member.channelDef.type,
373
+ domain: member.channelDef.scale.domain,
374
+ },
375
+ fromComplexInterval,
376
+ includeSelectionInitial
377
+ );
358
378
 
359
- mergeLiteralConfiguredDomain(state, resolved);
379
+ mergeConfiguredDomainResolution(state, resolved);
360
380
  }
361
381
 
362
382
  return finishConfiguredDomainResolution(state);
363
383
  }
364
384
 
365
385
  /**
366
- * @param {ScaleResolutionMember} member
386
+ * @param {ConfiguredDomainResolutionState} state
387
+ * @param {ConfiguredDomainMemberResolution} resolved
388
+ */
389
+ function mergeConfiguredDomainResolution(state, resolved) {
390
+ if (resolved.kind === "selection") {
391
+ mergeSelectionConfiguredDomain(state, resolved);
392
+ } else {
393
+ mergeLiteralConfiguredDomain(state, resolved);
394
+ }
395
+ }
396
+
397
+ /**
398
+ * @param {ConfiguredDomainSource} source
367
399
  * @param {(interval: ScalarDomain | ComplexDomain) => number[]} fromComplexInterval
368
400
  * @param {boolean} includeSelectionInitial
369
401
  * @returns {ConfiguredDomainMemberResolution}
370
402
  */
371
- function resolveConfiguredDomainMember(
372
- member,
403
+ function resolveConfiguredDomainSource(
404
+ source,
373
405
  fromComplexInterval,
374
406
  includeSelectionInitial
375
407
  ) {
376
- const domainDef = member.channelDef.scale.domain;
408
+ const domainDef = source.domain;
377
409
  if (isSelectionDomainRef(domainDef)) {
378
410
  return {
379
411
  kind: "selection",
380
412
  ...resolveSelectionDomain(
381
- member,
413
+ source,
382
414
  domainDef,
383
415
  fromComplexInterval,
384
416
  includeSelectionInitial
@@ -386,22 +418,11 @@ function resolveConfiguredDomainMember(
386
418
  };
387
419
  }
388
420
 
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
- }
399
-
400
421
  return {
401
422
  kind: "literal",
402
423
  domain: resolveConfiguredIntervalDomain(
403
- member.channelDef.type,
404
- domainDef,
424
+ source.type,
425
+ resolveConfiguredDomainValue(domainDef, source.view?.paramRuntime),
405
426
  fromComplexInterval
406
427
  ),
407
428
  };
@@ -488,7 +509,7 @@ function finishConfiguredDomainResolution(state) {
488
509
  }
489
510
 
490
511
  /**
491
- * @param {ScaleResolutionMember} member
512
+ * @param {ConfiguredDomainSource} source
492
513
  * @param {SelectionDomainRef} domainRef
493
514
  * @param {(interval: ScalarDomain | ComplexDomain) => number[]} fromComplexInterval
494
515
  * @param {boolean} includeSelectionInitial
@@ -502,7 +523,7 @@ function finishConfiguredDomainResolution(state) {
502
523
  * }}
503
524
  */
504
525
  function resolveSelectionDomain(
505
- member,
526
+ source,
506
527
  domainRef,
507
528
  fromComplexInterval,
508
529
  includeSelectionInitial
@@ -510,13 +531,13 @@ function resolveSelectionDomain(
510
531
  const paramName = domainRef.param;
511
532
 
512
533
  const resolvedChannel = resolveSelectionDomainChannel(
513
- member.channel,
534
+ source.channel,
514
535
  domainRef,
515
536
  paramName
516
537
  );
517
538
 
518
539
  const binding = resolveIntervalSelectionBinding(
519
- member.view,
540
+ source.view,
520
541
  paramName,
521
542
  resolvedChannel
522
543
  );
@@ -527,7 +548,7 @@ function resolveSelectionDomain(
527
548
  const initialDomain = includeSelectionInitial
528
549
  ? domainRef.initial
529
550
  ? resolveConfiguredIntervalDomain(
530
- member.channelDef.type,
551
+ source.type,
531
552
  domainRef.initial,
532
553
  fromComplexInterval
533
554
  )
@@ -545,10 +566,7 @@ function resolveSelectionDomain(
545
566
 
546
567
  return {
547
568
  // Selection intervals already use internal scale-domain coordinates.
548
- domain: createDomain(
549
- member.channelDef.type,
550
- fromComplexInterval(interval)
551
- ),
569
+ domain: createDomain(source.type, fromComplexInterval(interval)),
552
570
  description,
553
571
  param: paramName,
554
572
  encoding: resolvedChannel,
@@ -565,6 +583,7 @@ function resolveSelectionDomain(
565
583
  */
566
584
  function resolveConfiguredIntervalDomain(type, interval, fromComplexInterval) {
567
585
  const numericDomain = fromComplexInterval(interval);
586
+ // TODO: support Vega-Lite-style `unionWith` domains.
568
587
  const internalDomain =
569
588
  type === LOCUS &&
570
589
  isChromosomalLocusInterval(interval) &&
@@ -17,6 +17,7 @@ export default class ScaleInstanceManager {
17
17
  get scale(): import("../types/encoder.js").VegaScale & {
18
18
  props: import("../spec/scale.js").Scale;
19
19
  };
20
+ resetScale(): void;
20
21
  /**
21
22
  * @param {import("../spec/scale.js").Scale["assembly"]} [assembly]
22
23
  * @returns {import("../genome/genome.js").default}
@@ -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;IAwFD,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,mBAGC;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;eArET,OAAO,kBAAkB,EAAE,KAAK;MA0FjE;IAcD;;OAEG;IACH,wBAFW,OAAO,kBAAkB,EAAE,KAAK,QAc1C;IAED;;;OAGG;IACH,4CAHW,MAAM,IAAI,GACR,IAAI,CAShB;IAwFD,gBAGC;;CACJ"}
@@ -53,6 +53,11 @@ export default class ScaleInstanceManager {
53
53
  return this.#scale;
54
54
  }
55
55
 
56
+ resetScale() {
57
+ this.dispose();
58
+ this.#scale = undefined;
59
+ }
60
+
56
61
  /**
57
62
  * @param {import("../spec/scale.js").Scale["assembly"]} [assembly]
58
63
  * @returns {import("../genome/genome.js").default}
@@ -8,14 +8,19 @@
8
8
  * @param {Channel} options.channel
9
9
  * @param {import("../spec/channel.js").Type} options.dataType
10
10
  * @param {ScaleResolutionMember[]} options.orderedMembers
11
+ * @param {{ view: import("../view/view.js").default, config: Scale } | undefined} [options.viewLevelScaleConfig]
11
12
  * @param {boolean} options.isExplicitDomain
12
13
  * @param {import("../spec/config.js").GenomeSpyConfig[]} options.configScopes
13
14
  * @returns {Scale}
14
15
  */
15
- export function resolveScalePropsBase({ channel, dataType, orderedMembers, isExplicitDomain, configScopes, }: {
16
+ export function resolveScalePropsBase({ channel, dataType, orderedMembers, viewLevelScaleConfig, isExplicitDomain, configScopes, }: {
16
17
  channel: Channel;
17
18
  dataType: import("../spec/channel.js").Type;
18
19
  orderedMembers: ScaleResolutionMember[];
20
+ viewLevelScaleConfig?: {
21
+ view: import("../view/view.js").default;
22
+ config: Scale;
23
+ } | undefined;
19
24
  isExplicitDomain: boolean;
20
25
  configScopes: import("../spec/config.js").GenomeSpyConfig[];
21
26
  }): Scale;
@@ -1 +1 @@
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
+ {"version":3,"file":"scalePropsResolver.d.ts","sourceRoot":"","sources":["../../../src/scales/scalePropsResolver.js"],"names":[],"mappings":"AAmBA;;;;GAIG;AAEH;;;;;;;;;GASG;AACH,oIARG;IAAyB,OAAO,EAAxB,OAAO;IACoC,QAAQ,EAAnD,OAAO,oBAAoB,EAAE,IAAI;IACA,cAAc,EAA/C,qBAAqB,EAAE;IAC0D,oBAAoB,GAArG;QAAE,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,KAAK,CAAA;KAAE,GAAG,SAAS;IACrD,gBAAgB,EAAjC,OAAO;IACgD,YAAY,EAAnE,OAAO,mBAAmB,EAAE,eAAe,EAAE;CACrD,GAAU,KAAK,CAgKjB;sBA7KY,OAAO,oBAAoB,EAAE,OAAO;oBACpC,OAAO,kBAAkB,EAAE,KAAK;oCAChC,OAAO,sBAAsB,EAAE,qBAAqB"}