@genome-spy/core 0.72.0 → 0.73.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 (103) hide show
  1. package/LICENSE +1 -1
  2. package/dist/bundle/index.es.js +6779 -5393
  3. package/dist/bundle/index.js +133 -121
  4. package/dist/schema.json +281 -17
  5. package/dist/src/data/formats/bed.d.ts +8 -0
  6. package/dist/src/data/formats/bed.d.ts.map +1 -0
  7. package/dist/src/data/formats/bed.js +53 -0
  8. package/dist/src/data/formats/bedpe.d.ts +8 -0
  9. package/dist/src/data/formats/bedpe.d.ts.map +1 -0
  10. package/dist/src/data/formats/bedpe.js +160 -0
  11. package/dist/src/data/sources/dataUtils.d.ts +16 -0
  12. package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
  13. package/dist/src/data/sources/dataUtils.js +53 -3
  14. package/dist/src/data/sources/urlSource.d.ts +4 -0
  15. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  16. package/dist/src/data/sources/urlSource.js +133 -14
  17. package/dist/src/genome/assemblyPreflight.d.ts +31 -0
  18. package/dist/src/genome/assemblyPreflight.d.ts.map +1 -0
  19. package/dist/src/genome/assemblyPreflight.js +99 -0
  20. package/dist/src/genome/genome.d.ts +2 -2
  21. package/dist/src/genome/genome.d.ts.map +1 -1
  22. package/dist/src/genome/genome.js +4 -0
  23. package/dist/src/genome/genomeStore.d.ts +34 -3
  24. package/dist/src/genome/genomeStore.d.ts.map +1 -1
  25. package/dist/src/genome/genomeStore.js +409 -18
  26. package/dist/src/genome/rootGenomeConfig.d.ts +26 -0
  27. package/dist/src/genome/rootGenomeConfig.d.ts.map +1 -0
  28. package/dist/src/genome/rootGenomeConfig.js +94 -0
  29. package/dist/src/genomeSpy/interactionController.d.ts +5 -1
  30. package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
  31. package/dist/src/genomeSpy/interactionController.js +244 -29
  32. package/dist/src/genomeSpy/renderCoordinator.js +1 -1
  33. package/dist/src/genomeSpy.d.ts +13 -3
  34. package/dist/src/genomeSpy.d.ts.map +1 -1
  35. package/dist/src/genomeSpy.js +81 -7
  36. package/dist/src/gl/canvasSizeHelper.d.ts +74 -0
  37. package/dist/src/gl/canvasSizeHelper.d.ts.map +1 -0
  38. package/dist/src/gl/canvasSizeHelper.js +203 -0
  39. package/dist/src/gl/webGLHelper.d.ts +25 -11
  40. package/dist/src/gl/webGLHelper.d.ts.map +1 -1
  41. package/dist/src/gl/webGLHelper.js +59 -33
  42. package/dist/src/index.d.ts.map +1 -1
  43. package/dist/src/index.js +5 -2
  44. package/dist/src/marks/link.d.ts.map +1 -1
  45. package/dist/src/marks/link.js +5 -3
  46. package/dist/src/marks/mark.d.ts.map +1 -1
  47. package/dist/src/marks/mark.js +6 -1
  48. package/dist/src/scales/domainPlanner.d.ts +34 -3
  49. package/dist/src/scales/domainPlanner.d.ts.map +1 -1
  50. package/dist/src/scales/domainPlanner.js +247 -26
  51. package/dist/src/scales/scaleInstanceManager.d.ts +2 -1
  52. package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
  53. package/dist/src/scales/scaleInstanceManager.js +10 -11
  54. package/dist/src/scales/scaleInteractionController.d.ts.map +1 -1
  55. package/dist/src/scales/scaleInteractionController.js +16 -14
  56. package/dist/src/scales/scaleResolution.d.ts +16 -0
  57. package/dist/src/scales/scaleResolution.d.ts.map +1 -1
  58. package/dist/src/scales/scaleResolution.js +314 -54
  59. package/dist/src/scales/scaleResolutionTestUtils.d.ts +21 -0
  60. package/dist/src/scales/scaleResolutionTestUtils.d.ts.map +1 -0
  61. package/dist/src/scales/scaleResolutionTestUtils.js +33 -0
  62. package/dist/src/scales/selectionDomainUtils.d.ts +22 -0
  63. package/dist/src/scales/selectionDomainUtils.d.ts.map +1 -0
  64. package/dist/src/scales/selectionDomainUtils.js +79 -0
  65. package/dist/src/scales/zoomDomainUtils.d.ts +18 -0
  66. package/dist/src/scales/zoomDomainUtils.d.ts.map +1 -0
  67. package/dist/src/scales/zoomDomainUtils.js +69 -0
  68. package/dist/src/screenshotHarness.d.ts +16 -0
  69. package/dist/src/screenshotHarness.d.ts.map +1 -0
  70. package/dist/src/screenshotHarness.js +242 -0
  71. package/dist/src/singlePageApp.js +1 -1
  72. package/dist/src/spec/data.d.ts +23 -3
  73. package/dist/src/spec/genome.d.ts +22 -2
  74. package/dist/src/spec/parameter.d.ts +39 -2
  75. package/dist/src/spec/root.d.ts +20 -1
  76. package/dist/src/spec/scale.d.ts +41 -5
  77. package/dist/src/styles/genome-spy.css +8 -0
  78. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  79. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  80. package/dist/src/styles/genome-spy.css.js +8 -0
  81. package/dist/src/tooltip/dataTooltipHandler.js +59 -10
  82. package/dist/src/types/embedApi.d.ts +19 -0
  83. package/dist/src/utils/inferSpecBaseUrl.d.ts +14 -0
  84. package/dist/src/utils/inferSpecBaseUrl.d.ts.map +1 -0
  85. package/dist/src/utils/inferSpecBaseUrl.js +73 -0
  86. package/dist/src/utils/interactionEvent.d.ts +53 -3
  87. package/dist/src/utils/interactionEvent.d.ts.map +1 -1
  88. package/dist/src/utils/interactionEvent.js +62 -1
  89. package/dist/src/view/containerMutationHelper.d.ts.map +1 -1
  90. package/dist/src/view/containerMutationHelper.js +8 -0
  91. package/dist/src/view/dataReadiness.d.ts +2 -2
  92. package/dist/src/view/dataReadiness.d.ts.map +1 -1
  93. package/dist/src/view/dataReadiness.js +63 -58
  94. package/dist/src/view/facetView.js +1 -1
  95. package/dist/src/view/gridView/gridChild.d.ts +7 -0
  96. package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
  97. package/dist/src/view/gridView/gridChild.js +180 -11
  98. package/dist/src/view/gridView/gridView.d.ts.map +1 -1
  99. package/dist/src/view/gridView/gridView.js +60 -17
  100. package/dist/src/view/zoom.d.ts +14 -2
  101. package/dist/src/view/zoom.d.ts.map +1 -1
  102. package/dist/src/view/zoom.js +373 -76
  103. package/package.json +4 -2
@@ -21,6 +21,7 @@ overflow: hidden;
21
21
 
22
22
  canvas {
23
23
  display: block;
24
+ touch-action: none;
24
25
  transform: scale(1, 1);
25
26
  opacity: 1;
26
27
  transition:
@@ -163,6 +164,13 @@ margin-left: 0.4em;
163
164
  box-shadow: 0px 0px 3px 1px white;
164
165
  }
165
166
 
167
+ .color-legend-unmapped {
168
+ background-color: transparent;
169
+ border: 1px solid black;
170
+ box-sizing: border-box;
171
+ box-shadow: none;
172
+ }
173
+
166
174
  .attributes {
167
175
  .hovered {
168
176
  background-color: #e0e0e0;
@@ -1,4 +1,5 @@
1
1
  import { html } from "lit";
2
+ import { splitAccessPath } from "vega-util";
2
3
  import formatObject from "../utils/formatObject.js";
3
4
  import { flattenDatumRows } from "./flattenDatumRows.js";
4
5
  import createTooltipContext from "./tooltipContext.js";
@@ -7,23 +8,71 @@ import createTooltipContext from "./tooltipContext.js";
7
8
  * @type {import("./tooltipHandler.js").TooltipHandler}
8
9
  */
9
10
  export default async function dataTooltipHandler(datum, mark, params, context) {
11
+ /**
12
+ * @param {string} fieldPath
13
+ * @returns {string}
14
+ */
15
+ // Tooltip rows are flattened using dot notation. Normalize encoded field
16
+ // access paths (for example bracket syntax) to the same shape.
17
+ const normalizeFieldPath = (fieldPath) =>
18
+ splitAccessPath(fieldPath).join(".");
19
+
20
+ /**
21
+ * @param {unknown} value
22
+ * @returns {boolean}
23
+ */
24
+ // Treat NaN like missing in tooltip semantics to avoid showing an
25
+ // "unmapped" marker for invalid numeric values.
26
+ const hasValue = (value) =>
27
+ value !== null &&
28
+ value !== undefined &&
29
+ !(typeof value === "number" && Number.isNaN(value));
30
+
10
31
  /**
11
32
  * @param {string} key
33
+ * @param {any} value
12
34
  * @param {object} datum
13
35
  */
14
- const legend = (key, datum) => {
36
+ const legend = (key, value, datum) => {
15
37
  for (const [channel, encoder] of Object.entries(mark.encoders)) {
16
- if (encoder?.dataAccessor?.fields.includes(key)) {
38
+ const fields = encoder?.dataAccessor?.fields;
39
+ if (
40
+ fields &&
41
+ fields.some(
42
+ (fieldPath) =>
43
+ fieldPath === key ||
44
+ normalizeFieldPath(fieldPath) === key
45
+ )
46
+ ) {
17
47
  switch (channel) {
18
48
  case "color":
19
49
  case "fill":
20
- case "stroke":
21
- return html`
22
- <span
23
- class="color-legend"
24
- style=${`background-color: ${encoder(datum)}`}
25
- ></span>
26
- `;
50
+ case "stroke": {
51
+ const encodedColor = encoder(datum);
52
+ if (
53
+ encodedColor !== null &&
54
+ encodedColor !== undefined
55
+ ) {
56
+ return html`
57
+ <span
58
+ class="color-legend"
59
+ style=${"background-color: " +
60
+ String(encodedColor)}
61
+ ></span>
62
+ `;
63
+ } else if (hasValue(value)) {
64
+ // The field has a value but the scale did not map it
65
+ // (typically outside/absent domain): show an empty,
66
+ // black-stroked swatch to signal the mismatch.
67
+ return html`
68
+ <span
69
+ class="color-legend color-legend-unmapped"
70
+ ></span>
71
+ `;
72
+ } else {
73
+ return "";
74
+ }
75
+ }
27
76
  default:
28
77
  }
29
78
  }
@@ -47,7 +96,7 @@ export default async function dataTooltipHandler(datum, mark, params, context) {
47
96
 
48
97
  const tableContents = orderedRows.map((row) => {
49
98
  const value = formatObject(row.value);
50
- const valueLegend = legend(row.key, datum);
99
+ const valueLegend = legend(row.key, row.value, datum);
51
100
  return html`
52
101
  <tr>
53
102
  <th>${row.key}</th>
@@ -75,6 +75,12 @@ export interface EmbedResult {
75
75
  */
76
76
  getScaleResolutionByName: (name: string) => ScaleResolutionApi;
77
77
 
78
+ /**
79
+ * Waits until lazy data sources have loaded data for the current visible
80
+ * positional domain.
81
+ */
82
+ awaitVisibleLazyData: (signal?: AbortSignal) => Promise<void>;
83
+
78
84
  /**
79
85
  * Updates a named dataset
80
86
  *
@@ -83,6 +89,19 @@ export interface EmbedResult {
83
89
  */
84
90
  updateNamedData: (name: string, data?: any[]) => void;
85
91
 
92
+ /**
93
+ * Returns the bounds reached by the last rendered layout in CSS pixels.
94
+ */
95
+ getRenderedBounds: () => {
96
+ width: number | undefined;
97
+ height: number | undefined;
98
+ };
99
+
100
+ /**
101
+ * Returns the current logical canvas size in CSS pixels.
102
+ */
103
+ getLogicalCanvasSize: () => { width: number; height: number };
104
+
86
105
  /**
87
106
  * Returns a PNG data URL of the current canvas.
88
107
  *
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @param {string} url
3
+ */
4
+ export function getCuratedExampleBaseUrl(url: string): string;
5
+ /**
6
+ * Infers the effective base URL for a spec loaded from the given URL or path.
7
+ *
8
+ * Curated shared examples resolve against the examples root so they can use
9
+ * tidy `data/...` and `shared/...` paths regardless of their subdirectory.
10
+ *
11
+ * @param {string} url
12
+ */
13
+ export default function inferSpecBaseUrl(url: string): string;
14
+ //# sourceMappingURL=inferSpecBaseUrl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inferSpecBaseUrl.d.ts","sourceRoot":"","sources":["../../../src/utils/inferSpecBaseUrl.js"],"names":[],"mappings":"AAuBA;;GAEG;AACH,8CAFW,MAAM,UAahB;AAED;;;;;;;GAOG;AACH,8CAFW,MAAM,UAYhB"}
@@ -0,0 +1,73 @@
1
+ const CURATED_EXAMPLE_BASES = [
2
+ ["/docs/examples/", "/docs/examples/"],
3
+ ["/examples/core/", "/examples/"],
4
+ ["/examples/docs/", "/examples/"],
5
+ ["/examples/app/", "/examples/"],
6
+ ];
7
+
8
+ const EXTERNAL_URL_RE = /^(?:[a-z]+:)?\/\//i;
9
+ const DUMMY_ORIGIN = "https://example.invalid";
10
+
11
+ /**
12
+ * @param {string} url
13
+ */
14
+ function getUrlKind(url) {
15
+ if (EXTERNAL_URL_RE.test(url)) {
16
+ return "external";
17
+ } else if (url.startsWith("/")) {
18
+ return "root";
19
+ } else {
20
+ return "relative";
21
+ }
22
+ }
23
+
24
+ /**
25
+ * @param {string} url
26
+ */
27
+ export function getCuratedExampleBaseUrl(url) {
28
+ const parsed = new URL(url, DUMMY_ORIGIN);
29
+ const match = CURATED_EXAMPLE_BASES.find(([prefix]) =>
30
+ parsed.pathname.startsWith(prefix)
31
+ );
32
+
33
+ if (!match) {
34
+ return undefined;
35
+ }
36
+
37
+ return formatOutputUrl(match[1], parsed, getUrlKind(url));
38
+ }
39
+
40
+ /**
41
+ * Infers the effective base URL for a spec loaded from the given URL or path.
42
+ *
43
+ * Curated shared examples resolve against the examples root so they can use
44
+ * tidy `data/...` and `shared/...` paths regardless of their subdirectory.
45
+ *
46
+ * @param {string} url
47
+ */
48
+ export default function inferSpecBaseUrl(url) {
49
+ const curatedBaseUrl = getCuratedExampleBaseUrl(url);
50
+ if (curatedBaseUrl) {
51
+ return curatedBaseUrl;
52
+ }
53
+
54
+ const parsed = new URL(url, DUMMY_ORIGIN);
55
+ const directoryUrl = new URL("./", parsed);
56
+
57
+ return formatOutputUrl(directoryUrl.pathname, parsed, getUrlKind(url));
58
+ }
59
+
60
+ /**
61
+ * @param {string} pathname
62
+ * @param {URL} parsed
63
+ * @param {"external" | "root" | "relative"} kind
64
+ */
65
+ function formatOutputUrl(pathname, parsed, kind) {
66
+ if (kind === "external") {
67
+ return parsed.origin + pathname;
68
+ } else if (kind === "root") {
69
+ return pathname;
70
+ } else {
71
+ return pathname.slice(1);
72
+ }
73
+ }
@@ -1,3 +1,29 @@
1
+ /**
2
+ * @typedef {object} TouchGestureEvent
3
+ * @prop {"touchgesture"} type
4
+ * @prop {"move" | "end"} phase
5
+ * @prop {1 | 2} pointerCount
6
+ * @prop {number} xDelta
7
+ * @prop {number} yDelta
8
+ * @prop {number} zDelta
9
+ * @prop {() => void} [preventDefault]
10
+ */
11
+ /**
12
+ * Side-effect-free query event for wheel ownership. Handlers should only
13
+ * claim wheel if they would consume native wheel at the current pointer
14
+ * location.
15
+ *
16
+ * @typedef {object} WheelClaimProbeEvent
17
+ * @prop {"wheelclaimprobe"} type
18
+ */
19
+ /**
20
+ * @typedef {UIEvent | TouchGestureEvent | WheelClaimProbeEvent} InteractionUiEvent
21
+ */
22
+ /**
23
+ * @param {unknown} eventLike
24
+ * @returns {eventLike is TouchGestureEvent}
25
+ */
26
+ export function isTouchGestureEvent(eventLike: unknown): eventLike is TouchGestureEvent;
1
27
  /**
2
28
  * Create a safe proxy for an event-like object that exposes only primitive
3
29
  * (string, number, boolean, bigint, symbol, undefined) properties and null.
@@ -16,12 +42,13 @@ export default class InteractionEvent {
16
42
  /**
17
43
  * @param {import("../view/layout/point.js").default} point Event coordinates
18
44
  * inside the visualization canvas.
19
- * @param {UIEvent} uiEvent The event to be wrapped
45
+ * @param {InteractionUiEvent} uiEvent The event to be wrapped
20
46
  */
21
- constructor(point: import("../view/layout/point.js").default, uiEvent: UIEvent);
47
+ constructor(point: import("../view/layout/point.js").default, uiEvent: InteractionUiEvent);
22
48
  point: import("../view/layout/point.js").default;
23
- uiEvent: UIEvent;
49
+ uiEvent: InteractionUiEvent;
24
50
  stopped: boolean;
51
+ wheelClaimed: boolean;
25
52
  /**
26
53
  * The target is known only in the bubbling phase
27
54
  *
@@ -29,6 +56,11 @@ export default class InteractionEvent {
29
56
  */
30
57
  target: import("../view/view.js").default;
31
58
  stopPropagation(): void;
59
+ /**
60
+ * Marks the event as wheel-owned by the current interaction path.
61
+ * This is used by native wheel probes to decide preventDefault timing.
62
+ */
63
+ claimWheel(): void;
32
64
  /**
33
65
  * The event type string of the underlying UI event (e.g. "click", "keydown").
34
66
  *
@@ -41,4 +73,22 @@ export default class InteractionEvent {
41
73
  get mouseEvent(): MouseEvent;
42
74
  #private;
43
75
  }
76
+ export type TouchGestureEvent = {
77
+ type: "touchgesture";
78
+ phase: "move" | "end";
79
+ pointerCount: 1 | 2;
80
+ xDelta: number;
81
+ yDelta: number;
82
+ zDelta: number;
83
+ preventDefault?: () => void;
84
+ };
85
+ /**
86
+ * Side-effect-free query event for wheel ownership. Handlers should only
87
+ * claim wheel if they would consume native wheel at the current pointer
88
+ * location.
89
+ */
90
+ export type WheelClaimProbeEvent = {
91
+ type: "wheelclaimprobe";
92
+ };
93
+ export type InteractionUiEvent = UIEvent | TouchGestureEvent | WheelClaimProbeEvent;
44
94
  //# sourceMappingURL=interactionEvent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"interactionEvent.d.ts","sourceRoot":"","sources":["../../../src/utils/interactionEvent.js"],"names":[],"mappings":"AA6DA;;;;;;;GAOG;AACH,0CAFa,CAAC,UAFH,CAAC,GACC,CAAC,CA0Eb;AA5ID;;;;GAIG;AACH;IAII;;;;OAIG;IACH,mBAJW,OAAO,yBAAyB,EAAE,OAAO,WAEzC,OAAO,EAajB;IAVG,iDAAkB;IAClB,iBAAsB;IACtB,iBAAoB;IAEpB;;;;OAIG;IACH,QAFU,OAAO,iBAAiB,EAAE,OAAO,CAEpB;IAG3B,wBAEC;IAED;;;;;;OAMG;IACH,YAFa,MAAM,CAIlB;IAED,oCAQC;IAED,6BAMC;;CACJ"}
1
+ {"version":3,"file":"interactionEvent.d.ts","sourceRoot":"","sources":["../../../src/utils/interactionEvent.js"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AAEH;;GAEG;AAEH;;;GAGG;AACH,+CAHW,OAAO,GACL,SAAS,IAAI,iBAAiB,CAoB1C;AA4ED;;;;;;;GAOG;AACH,0CAFa,CAAC,UAFH,CAAC,GACC,CAAC,CA0Eb;AAzJD;;;;GAIG;AACH;IAII;;;;OAIG;IACH,mBAJW,OAAO,yBAAyB,EAAE,OAAO,WAEzC,kBAAkB,EAc5B;IAXG,iDAAkB;IAClB,4BAAsB;IACtB,iBAAoB;IACpB,sBAAyB;IAEzB;;;;OAIG;IACH,QAFU,OAAO,iBAAiB,EAAE,OAAO,CAEpB;IAG3B,wBAEC;IAED;;;OAGG;IACH,mBAMC;IAED;;;;;;OAMG;IACH,YAFa,MAAM,CAIlB;IAED,oCAQC;IAED,6BAMC;;CACJ;;UAtHS,cAAc;WACd,MAAM,GAAG,KAAK;kBACd,CAAC,GAAG,CAAC;YACL,MAAM;YACN,MAAM;YACN,MAAM;qBACN,MAAM,IAAI;;;;;;;;UASV,iBAAiB;;iCAId,OAAO,GAAG,iBAAiB,GAAG,oBAAoB"}
@@ -1,3 +1,51 @@
1
+ /**
2
+ * @typedef {object} TouchGestureEvent
3
+ * @prop {"touchgesture"} type
4
+ * @prop {"move" | "end"} phase
5
+ * @prop {1 | 2} pointerCount
6
+ * @prop {number} xDelta
7
+ * @prop {number} yDelta
8
+ * @prop {number} zDelta
9
+ * @prop {() => void} [preventDefault]
10
+ */
11
+
12
+ /**
13
+ * Side-effect-free query event for wheel ownership. Handlers should only
14
+ * claim wheel if they would consume native wheel at the current pointer
15
+ * location.
16
+ *
17
+ * @typedef {object} WheelClaimProbeEvent
18
+ * @prop {"wheelclaimprobe"} type
19
+ */
20
+
21
+ /**
22
+ * @typedef {UIEvent | TouchGestureEvent | WheelClaimProbeEvent} InteractionUiEvent
23
+ */
24
+
25
+ /**
26
+ * @param {unknown} eventLike
27
+ * @returns {eventLike is TouchGestureEvent}
28
+ */
29
+ export function isTouchGestureEvent(eventLike) {
30
+ if (!eventLike || typeof eventLike !== "object") {
31
+ return false;
32
+ }
33
+
34
+ const candidate =
35
+ /** @type {{type?: unknown, phase?: unknown, pointerCount?: unknown, xDelta?: unknown, yDelta?: unknown, zDelta?: unknown}} */ (
36
+ eventLike
37
+ );
38
+
39
+ return (
40
+ candidate.type === "touchgesture" &&
41
+ (candidate.phase === "move" || candidate.phase === "end") &&
42
+ (candidate.pointerCount === 1 || candidate.pointerCount === 2) &&
43
+ Number.isFinite(candidate.xDelta) &&
44
+ Number.isFinite(candidate.yDelta) &&
45
+ Number.isFinite(candidate.zDelta)
46
+ );
47
+ }
48
+
1
49
  /**
2
50
  * This class wraps a MouseEvent (or similar) and allows for
3
51
  * its propagation through the view hierarchy in a similar manner
@@ -10,12 +58,13 @@ export default class InteractionEvent {
10
58
  /**
11
59
  * @param {import("../view/layout/point.js").default} point Event coordinates
12
60
  * inside the visualization canvas.
13
- * @param {UIEvent} uiEvent The event to be wrapped
61
+ * @param {InteractionUiEvent} uiEvent The event to be wrapped
14
62
  */
15
63
  constructor(point, uiEvent) {
16
64
  this.point = point;
17
65
  this.uiEvent = uiEvent;
18
66
  this.stopped = false;
67
+ this.wheelClaimed = false;
19
68
 
20
69
  /**
21
70
  * The target is known only in the bubbling phase
@@ -29,6 +78,18 @@ export default class InteractionEvent {
29
78
  this.stopped = true;
30
79
  }
31
80
 
81
+ /**
82
+ * Marks the event as wheel-owned by the current interaction path.
83
+ * This is used by native wheel probes to decide preventDefault timing.
84
+ */
85
+ claimWheel() {
86
+ if (this.type !== "wheel" && this.type !== "wheelclaimprobe") {
87
+ throw new Error("Can claim wheel only for wheel events!");
88
+ }
89
+
90
+ this.wheelClaimed = true;
91
+ }
92
+
32
93
  /**
33
94
  * The event type string of the underlying UI event (e.g. "click", "keydown").
34
95
  *
@@ -1 +1 @@
1
- {"version":3,"file":"containerMutationHelper.d.ts","sourceRoot":"","sources":["../../../src/view/containerMutationHelper.js"],"names":[],"mappings":"AAOA;;;GAGG;AACH;IACI;;;;;;;;;;OAUG;IAEH;;;;;;;;;;OAUG;IAEH;;;OAGG;IACH,uBAHW,OAAO,oBAAoB,EAAE,OAAO;uBAX3B;mBARR,CAAC,yEAAqB,CAAC,EAAE;sBACtB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,IAAI;sBACpD,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;SAMF;oBACnB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG;oBAClC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;sBACrB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC;sBAClE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;sBAChC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,MAAM;wBACpD,OAAO;OAW3B;IAFG,yFAA0B;IAC1B;uBAhBgB;mBARR,CAAC,yEAAqB,CAAC,EAAE;sBACtB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,IAAI;sBACpD,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;SAMF;oBACnB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG;oBAClC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;sBACrB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC;sBAClE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;sBAChC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,MAAM;wBACpD,OAAO;MAUF;IAG1B;;;;;;OAMG;IACH,wBAJW,OAAO,iBAAiB,EAAE,QAAQ,GAAG,OAAO,iBAAiB,EAAE,UAAU,UACzE,MAAM,GACJ,OAAO,iEAAM,CA8CzB;IAED;;;;OAIG;IACH,qBAFW,MAAM,iBAehB;CACJ"}
1
+ {"version":3,"file":"containerMutationHelper.d.ts","sourceRoot":"","sources":["../../../src/view/containerMutationHelper.js"],"names":[],"mappings":"AAQA;;;GAGG;AACH;IACI;;;;;;;;;;OAUG;IAEH;;;;;;;;;;OAUG;IAEH;;;OAGG;IACH,uBAHW,OAAO,oBAAoB,EAAE,OAAO;uBAX3B;mBARR,CAAC,yEAAqB,CAAC,EAAE;sBACtB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,IAAI;sBACpD,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;SAMF;oBACnB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG;oBAClC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;sBACrB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC;sBAClE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;sBAChC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,MAAM;wBACpD,OAAO;OAW3B;IAFG,yFAA0B;IAC1B;uBAhBgB;mBARR,CAAC,yEAAqB,CAAC,EAAE;sBACtB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,IAAI;sBACpD,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;SAMF;oBACnB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG;oBAClC,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;sBACrB,CAAC,IAAI,iEAAM,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC;sBAClE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;sBAChC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,yEAAqB,KAAK,MAAM;wBACpD,OAAO;MAUF;IAG1B;;;;;;OAMG;IACH,wBAJW,OAAO,iBAAiB,EAAE,QAAQ,GAAG,OAAO,iBAAiB,EAAE,UAAU,UACzE,MAAM,GACJ,OAAO,iEAAM,CAqDzB;IAED;;;;OAIG;IACH,qBAFW,MAAM,iBAehB;CACJ"}
@@ -3,6 +3,7 @@ import {
3
3
  loadViewSubtreeData,
4
4
  } from "../data/flowInit.js";
5
5
  import { configureViewOpacity } from "../genomeSpy/viewHierarchyConfig.js";
6
+ import { ensureAssembliesForView } from "../genome/assemblyPreflight.js";
6
7
  import { finalizeSubtreeGraphics } from "./viewUtils.js";
7
8
 
8
9
  /**
@@ -64,6 +65,13 @@ export default class ContainerMutationHelper {
64
65
  name
65
66
  );
66
67
 
68
+ // Reminder: ensure assemblies from the real child hierarchy before any
69
+ // downstream work that may initialize scales (axis prep / encoders).
70
+ await ensureAssembliesForView(
71
+ childView,
72
+ this.container.context.genomeStore
73
+ );
74
+
67
75
  insertAt(insertIndex, childSpec);
68
76
  const insertionResult = this.options.insertView(childView, insertIndex);
69
77
 
@@ -35,12 +35,12 @@ export function isSubtreeLazyReady(subtreeRoot: View, readinessRequest: DataRead
35
35
  *
36
36
  * @param {import("../types/viewContext.js").default} context
37
37
  * @param {View} subtreeRoot
38
- * @param {DataReadinessRequest} readinessRequest
38
+ * @param {DataReadinessRequest | undefined} readinessRequest
39
39
  * @param {AbortSignal} [signal]
40
40
  * @param {(view: View) => boolean} [viewFilter]
41
41
  * @returns {Promise<void>}
42
42
  */
43
- export function awaitSubtreeLazyReady(context: import("../types/viewContext.js").default, subtreeRoot: View, readinessRequest: DataReadinessRequest, signal?: AbortSignal, viewFilter?: (view: View) => boolean): Promise<void>;
43
+ export function awaitSubtreeLazyReady(context: import("../types/viewContext.js").default, subtreeRoot: View, readinessRequest: DataReadinessRequest | undefined, signal?: AbortSignal, viewFilter?: (view: View) => boolean): Promise<void>;
44
44
  export type View = import("../view/view.js").default;
45
45
  export type DataReadinessRequest = import("../data/sources/lazy/singleAxisLazySource.js").DataReadinessRequest;
46
46
  //# sourceMappingURL=dataReadiness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dataReadiness.d.ts","sourceRoot":"","sources":["../../../src/view/dataReadiness.js"],"names":[],"mappings":"AAGA;;;GAGG;AAEH;;;;;;GAMG;AACH,4CAJW,IAAI,YACJ,OAAO,oBAAoB,EAAE,wBAAwB,EAAE,GACrD,oBAAoB,GAAG,SAAS,CAe5C;AAED;;;;;;;GAOG;AACH,4CALW,IAAI,oBACJ,oBAAoB,eACpB,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,GACrB,OAAO,CAiDnB;AAED;;;;;;;;GAQG;AACH,gDALW,IAAI,oBACJ,oBAAoB,GAAG,SAAS,eAChC,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,GACrB,OAAO,CAwDnB;AAED;;;;;;;;;;GAUG;AACH,+CAPW,OAAO,yBAAyB,EAAE,OAAO,eACzC,IAAI,oBACJ,oBAAoB,WACpB,WAAW,eACX,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,GACrB,OAAO,CAAC,IAAI,CAAC,CAyGzB;mBAtQY,OAAO,iBAAiB,EAAE,OAAO;mCACjC,OAAO,8CAA8C,EAAE,oBAAoB"}
1
+ {"version":3,"file":"dataReadiness.d.ts","sourceRoot":"","sources":["../../../src/view/dataReadiness.js"],"names":[],"mappings":"AAIA;;;GAGG;AAEH;;;;;;GAMG;AACH,4CAJW,IAAI,YACJ,OAAO,oBAAoB,EAAE,wBAAwB,EAAE,GACrD,oBAAoB,GAAG,SAAS,CAe5C;AAED;;;;;;;GAOG;AACH,4CALW,IAAI,oBACJ,oBAAoB,eACpB,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,GACrB,OAAO,CAiDnB;AAED;;;;;;;;GAQG;AACH,gDALW,IAAI,oBACJ,oBAAoB,GAAG,SAAS,eAChC,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,GACrB,OAAO,CAgBnB;AAED;;;;;;;;;;GAUG;AACH,+CAPW,OAAO,yBAAyB,EAAE,OAAO,eACzC,IAAI,oBACJ,oBAAoB,GAAG,SAAS,WAChC,WAAW,eACX,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,GACrB,OAAO,CAAC,IAAI,CAAC,CA0FzB;mBA/MY,OAAO,iBAAiB,EAAE,OAAO;mCACjC,OAAO,8CAA8C,EAAE,oBAAoB"}
@@ -1,4 +1,5 @@
1
1
  import DataSource from "../data/sources/dataSource.js";
2
+ import SingleAxisLazySource from "../data/sources/lazy/singleAxisLazySource.js";
2
3
  import UnitView from "./unitView.js";
3
4
 
4
5
  /**
@@ -95,54 +96,14 @@ export function isSubtreeReady(subtreeRoot, readinessRequest, viewFilter) {
95
96
  * @returns {boolean}
96
97
  */
97
98
  export function isSubtreeLazyReady(subtreeRoot, readinessRequest, viewFilter) {
98
- const shouldConsiderView =
99
- viewFilter ??
100
- ((/** @type {View} */ view) => view.isConfiguredVisible());
101
-
102
- /** @type {Set<DataSource>} */
103
- const dataSources = new Set();
104
-
105
- subtreeRoot.visit((view) => {
106
- if (!(view instanceof UnitView)) {
107
- return;
108
- }
109
- if (!shouldConsiderView(view)) {
110
- return;
111
- }
112
-
113
- /** @type {View | null} */
114
- let current = view;
115
- while (current) {
116
- if (current.flowHandle && current.flowHandle.dataSource) {
117
- break;
118
- }
119
- current = current.dataParent;
120
- }
121
-
122
- if (!current || !current.flowHandle) {
123
- return;
124
- }
125
- const dataSource = current.flowHandle.dataSource;
126
- if (!("isDataReadyForDomain" in dataSource)) {
127
- return;
128
- }
129
- dataSources.add(dataSource);
130
- });
99
+ const dataSources = collectLazyDataSources(subtreeRoot, viewFilter);
131
100
 
132
101
  if (!dataSources.size) {
133
102
  return true;
134
103
  }
135
104
 
136
- if (!readinessRequest) {
137
- return false;
138
- }
139
-
140
105
  for (const dataSource of dataSources) {
141
- const checkReady =
142
- /** @type {import("../data/sources/lazy/singleAxisLazySource.js").DataReadinessCheckable["isDataReadyForDomain"]} */ (
143
- /** @type {any} */ (dataSource).isDataReadyForDomain
144
- );
145
- if (!checkReady.call(dataSource, readinessRequest)) {
106
+ if (!isLazySourceReady(dataSource, readinessRequest)) {
146
107
  return false;
147
108
  }
148
109
  }
@@ -156,7 +117,7 @@ export function isSubtreeLazyReady(subtreeRoot, readinessRequest, viewFilter) {
156
117
  *
157
118
  * @param {import("../types/viewContext.js").default} context
158
119
  * @param {View} subtreeRoot
159
- * @param {DataReadinessRequest} readinessRequest
120
+ * @param {DataReadinessRequest | undefined} readinessRequest
160
121
  * @param {AbortSignal} [signal]
161
122
  * @param {(view: View) => boolean} [viewFilter]
162
123
  * @returns {Promise<void>}
@@ -172,21 +133,6 @@ export function awaitSubtreeLazyReady(
172
133
  viewFilter ??
173
134
  ((/** @type {View} */ view) => view.isConfiguredVisible());
174
135
 
175
- if (!readinessRequest) {
176
- if (
177
- isSubtreeLazyReady(
178
- subtreeRoot,
179
- readinessRequest,
180
- shouldConsiderView
181
- )
182
- ) {
183
- return Promise.resolve();
184
- }
185
- return Promise.reject(
186
- new Error("Lazy subtree readiness requires a readiness request.")
187
- );
188
- }
189
-
190
136
  return new Promise((resolve, reject) => {
191
137
  /** @type {Set<() => void>} */
192
138
  const unregisters = new Set();
@@ -265,3 +211,62 @@ export function awaitSubtreeLazyReady(
265
211
  }
266
212
  });
267
213
  }
214
+
215
+ /**
216
+ * @param {View} subtreeRoot
217
+ * @param {(view: View) => boolean} [viewFilter]
218
+ * @returns {Set<SingleAxisLazySource>}
219
+ */
220
+ function collectLazyDataSources(subtreeRoot, viewFilter) {
221
+ const shouldConsiderView =
222
+ viewFilter ??
223
+ ((/** @type {View} */ view) => view.isConfiguredVisible());
224
+
225
+ /** @type {Set<SingleAxisLazySource>} */
226
+ const dataSources = new Set();
227
+
228
+ subtreeRoot.visit((view) => {
229
+ if (!(view instanceof UnitView)) {
230
+ return;
231
+ }
232
+ if (!shouldConsiderView(view)) {
233
+ return;
234
+ }
235
+
236
+ /** @type {View | null} */
237
+ let current = view;
238
+ while (current) {
239
+ if (current.flowHandle && current.flowHandle.dataSource) {
240
+ break;
241
+ }
242
+ current = current.dataParent;
243
+ }
244
+
245
+ if (!current || !current.flowHandle) {
246
+ return;
247
+ }
248
+
249
+ const dataSource = current.flowHandle.dataSource;
250
+ if (!(dataSource instanceof SingleAxisLazySource)) {
251
+ return;
252
+ }
253
+
254
+ dataSources.add(dataSource);
255
+ });
256
+
257
+ return dataSources;
258
+ }
259
+
260
+ /**
261
+ * @param {SingleAxisLazySource} dataSource
262
+ * @param {DataReadinessRequest | undefined} readinessRequest
263
+ */
264
+ function isLazySourceReady(dataSource, readinessRequest) {
265
+ const request =
266
+ readinessRequest ??
267
+ ({
268
+ [dataSource.channel]: Array.from(dataSource.scaleResolution.getDomain()),
269
+ });
270
+
271
+ return dataSource.isDataReadyForDomain(request);
272
+ }
@@ -354,7 +354,7 @@ export default class FacetView extends ContainerView {
354
354
  coords[channel == "column" ? "width" : "height"],
355
355
  {
356
356
  spacing,
357
- devicePixelRatio: window.devicePixelRatio,
357
+ devicePixelRatio: context.getDevicePixelRatio(),
358
358
  }
359
359
  );
360
360
  };
@@ -1,3 +1,10 @@
1
+ /**
2
+ * @param {import("../../spec/parameter.js").IntervalSelectionConfig["zoom"]} zoom
3
+ * @param {boolean} hasZoomableChannel
4
+ * @param {string} paramName
5
+ * @returns {import("../../spec/parameter.js").EventConfig | undefined}
6
+ */
7
+ export function resolveIntervalZoomEventConfig(zoom: import("../../spec/parameter.js").IntervalSelectionConfig["zoom"], hasZoomableChannel: boolean, paramName: string): import("../../spec/parameter.js").EventConfig | undefined;
1
8
  /**
2
9
  * @param {import("../../spec/view.js").ViewBackground} viewBackground
3
10
  * @returns {import("../../spec/view.js").UnitSpec}
@@ -1 +1 @@
1
- {"version":3,"file":"gridChild.d.ts","sourceRoot":"","sources":["../../../../src/view/gridView/gridChild.js"],"names":[],"mappings":"AAysBA;;;GAGG;AACH,iDAHW,OAAO,oBAAoB,EAAE,cAAc,GACzC,OAAO,oBAAoB,EAAE,QAAQ,CA6BjD;AAED;;;GAGG;AACH,uDAHW,OAAO,oBAAoB,EAAE,cAAc,GACzC,OAAO,oBAAoB,EAAE,QAAQ,CA4CjD;AApwBD;IACI;;;;;;OAMG;IAEH;;;;OAIG;IACH,kBAJW,OAAO,YAAY,EAAE,OAAO,gBAC5B,OAAO,qBAAqB,EAAE,OAAO,UACrC,MAAM,EAgGhB;IA7FG,gGAAgC;IAChC,0EAAgB;IAChB,eAAoB;IAEpB,uBAAuB;IACvB,YADW,QAAQ,CACQ;IAE3B,uBAAuB;IACvB,kBADW,QAAQ,CACc;IAEjC,sFAAsF;IACtF,MADW,OAAO,CAAC,MAAM,CAAC,OAAO,oBAAoB,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAC/D;IAEd,+FAA+F;IAC/F,WADW,OAAO,CAAC,MAAM,CAAC,OAAO,oBAAoB,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAC9D;IAEnB,mFAAmF;IACnF,YADW,OAAO,CAAC,MAAM,CAAC,OAAO,gBAAgB,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC,CAC3D;IAEpB,4BAA4B;IAC5B,eADW,aAAa,CACM;IAE9B,uBAAuB;IACvB,OADW,QAAQ,CACG;IAEtB,wBAAwB;IACxB,QADW,SAAS,CACQ;IAgahC,6GAiBC;IAED;;OAEG;IACH,4BAwKC;IAED;;OAEG;IACH,yBAWC;IAED,uBAqBC;IAED,iCAEC;;CACJ;qBAxrBoB,gBAAgB;qBANK,gBAAgB;yBADjC,oBAAoB;sBASvB,gBAAgB;0BACZ,oBAAoB;sBALxB,wBAAwB;oBAF1B,sBAAsB"}
1
+ {"version":3,"file":"gridChild.d.ts","sourceRoot":"","sources":["../../../../src/view/gridView/gridChild.js"],"names":[],"mappings":"AAu0BA;;;;;GAKG;AACH,qDALW,OAAO,yBAAyB,EAAE,uBAAuB,CAAC,MAAM,CAAC,sBACjE,OAAO,aACP,MAAM,GACJ,OAAO,yBAAyB,EAAE,WAAW,GAAG,SAAS,CAyBrE;AAcD;;;GAGG;AACH,iDAHW,OAAO,oBAAoB,EAAE,cAAc,GACzC,OAAO,oBAAoB,EAAE,QAAQ,CA6BjD;AAED;;;GAGG;AACH,uDAHW,OAAO,oBAAoB,EAAE,cAAc,GACzC,OAAO,oBAAoB,EAAE,QAAQ,CA4CjD;AAx6BD;IACI;;;;;;OAMG;IAEH;;;;OAIG;IACH,kBAJW,OAAO,YAAY,EAAE,OAAO,gBAC5B,OAAO,qBAAqB,EAAE,OAAO,UACrC,MAAM,EAgGhB;IA7FG,gGAAgC;IAChC,0EAAgB;IAChB,eAAoB;IAEpB,uBAAuB;IACvB,YADW,QAAQ,CACQ;IAE3B,uBAAuB;IACvB,kBADW,QAAQ,CACc;IAEjC,sFAAsF;IACtF,MADW,OAAO,CAAC,MAAM,CAAC,OAAO,oBAAoB,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAC/D;IAEd,+FAA+F;IAC/F,WADW,OAAO,CAAC,MAAM,CAAC,OAAO,oBAAoB,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAC9D;IAEnB,mFAAmF;IACnF,YADW,OAAO,CAAC,MAAM,CAAC,OAAO,gBAAgB,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC,CAC3D;IAEpB,4BAA4B;IAC5B,eADW,aAAa,CACM;IAE9B,uBAAuB;IACvB,OADW,QAAQ,CACG;IAEtB,wBAAwB;IACxB,QADW,SAAS,CACQ;IAyhBhC,6GAiBC;IAED;;OAEG;IACH,4BAwKC;IAED;;OAEG;IACH,yBAWC;IAED,uBAqBC;IAED,iCAEC;;CACJ;qBApzBoB,gBAAgB;qBANK,gBAAgB;yBADjC,oBAAoB;sBASvB,gBAAgB;0BACZ,oBAAoB;sBALxB,wBAAwB;oBAF1B,sBAAsB"}