@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.
- package/LICENSE +1 -1
- package/dist/bundle/index.es.js +6779 -5393
- package/dist/bundle/index.js +133 -121
- package/dist/schema.json +281 -17
- package/dist/src/data/formats/bed.d.ts +8 -0
- package/dist/src/data/formats/bed.d.ts.map +1 -0
- package/dist/src/data/formats/bed.js +53 -0
- package/dist/src/data/formats/bedpe.d.ts +8 -0
- package/dist/src/data/formats/bedpe.d.ts.map +1 -0
- package/dist/src/data/formats/bedpe.js +160 -0
- package/dist/src/data/sources/dataUtils.d.ts +16 -0
- package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
- package/dist/src/data/sources/dataUtils.js +53 -3
- package/dist/src/data/sources/urlSource.d.ts +4 -0
- package/dist/src/data/sources/urlSource.d.ts.map +1 -1
- package/dist/src/data/sources/urlSource.js +133 -14
- package/dist/src/genome/assemblyPreflight.d.ts +31 -0
- package/dist/src/genome/assemblyPreflight.d.ts.map +1 -0
- package/dist/src/genome/assemblyPreflight.js +99 -0
- package/dist/src/genome/genome.d.ts +2 -2
- package/dist/src/genome/genome.d.ts.map +1 -1
- package/dist/src/genome/genome.js +4 -0
- package/dist/src/genome/genomeStore.d.ts +34 -3
- package/dist/src/genome/genomeStore.d.ts.map +1 -1
- package/dist/src/genome/genomeStore.js +409 -18
- package/dist/src/genome/rootGenomeConfig.d.ts +26 -0
- package/dist/src/genome/rootGenomeConfig.d.ts.map +1 -0
- package/dist/src/genome/rootGenomeConfig.js +94 -0
- package/dist/src/genomeSpy/interactionController.d.ts +5 -1
- package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
- package/dist/src/genomeSpy/interactionController.js +244 -29
- package/dist/src/genomeSpy/renderCoordinator.js +1 -1
- package/dist/src/genomeSpy.d.ts +13 -3
- package/dist/src/genomeSpy.d.ts.map +1 -1
- package/dist/src/genomeSpy.js +81 -7
- package/dist/src/gl/canvasSizeHelper.d.ts +74 -0
- package/dist/src/gl/canvasSizeHelper.d.ts.map +1 -0
- package/dist/src/gl/canvasSizeHelper.js +203 -0
- package/dist/src/gl/webGLHelper.d.ts +25 -11
- package/dist/src/gl/webGLHelper.d.ts.map +1 -1
- package/dist/src/gl/webGLHelper.js +59 -33
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -2
- package/dist/src/marks/link.d.ts.map +1 -1
- package/dist/src/marks/link.js +5 -3
- package/dist/src/marks/mark.d.ts.map +1 -1
- package/dist/src/marks/mark.js +6 -1
- package/dist/src/scales/domainPlanner.d.ts +34 -3
- package/dist/src/scales/domainPlanner.d.ts.map +1 -1
- package/dist/src/scales/domainPlanner.js +247 -26
- package/dist/src/scales/scaleInstanceManager.d.ts +2 -1
- package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
- package/dist/src/scales/scaleInstanceManager.js +10 -11
- package/dist/src/scales/scaleInteractionController.d.ts.map +1 -1
- package/dist/src/scales/scaleInteractionController.js +16 -14
- package/dist/src/scales/scaleResolution.d.ts +16 -0
- package/dist/src/scales/scaleResolution.d.ts.map +1 -1
- package/dist/src/scales/scaleResolution.js +314 -54
- package/dist/src/scales/scaleResolutionTestUtils.d.ts +21 -0
- package/dist/src/scales/scaleResolutionTestUtils.d.ts.map +1 -0
- package/dist/src/scales/scaleResolutionTestUtils.js +33 -0
- package/dist/src/scales/selectionDomainUtils.d.ts +22 -0
- package/dist/src/scales/selectionDomainUtils.d.ts.map +1 -0
- package/dist/src/scales/selectionDomainUtils.js +79 -0
- package/dist/src/scales/zoomDomainUtils.d.ts +18 -0
- package/dist/src/scales/zoomDomainUtils.d.ts.map +1 -0
- package/dist/src/scales/zoomDomainUtils.js +69 -0
- package/dist/src/screenshotHarness.d.ts +16 -0
- package/dist/src/screenshotHarness.d.ts.map +1 -0
- package/dist/src/screenshotHarness.js +242 -0
- package/dist/src/singlePageApp.js +1 -1
- package/dist/src/spec/data.d.ts +23 -3
- package/dist/src/spec/genome.d.ts +22 -2
- package/dist/src/spec/parameter.d.ts +39 -2
- package/dist/src/spec/root.d.ts +20 -1
- package/dist/src/spec/scale.d.ts +41 -5
- package/dist/src/styles/genome-spy.css +8 -0
- package/dist/src/styles/genome-spy.css.d.ts +1 -1
- package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
- package/dist/src/styles/genome-spy.css.js +8 -0
- package/dist/src/tooltip/dataTooltipHandler.js +59 -10
- package/dist/src/types/embedApi.d.ts +19 -0
- package/dist/src/utils/inferSpecBaseUrl.d.ts +14 -0
- package/dist/src/utils/inferSpecBaseUrl.d.ts.map +1 -0
- package/dist/src/utils/inferSpecBaseUrl.js +73 -0
- package/dist/src/utils/interactionEvent.d.ts +53 -3
- package/dist/src/utils/interactionEvent.d.ts.map +1 -1
- package/dist/src/utils/interactionEvent.js +62 -1
- package/dist/src/view/containerMutationHelper.d.ts.map +1 -1
- package/dist/src/view/containerMutationHelper.js +8 -0
- package/dist/src/view/dataReadiness.d.ts +2 -2
- package/dist/src/view/dataReadiness.d.ts.map +1 -1
- package/dist/src/view/dataReadiness.js +63 -58
- package/dist/src/view/facetView.js +1 -1
- package/dist/src/view/gridView/gridChild.d.ts +7 -0
- package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
- package/dist/src/view/gridView/gridChild.js +180 -11
- package/dist/src/view/gridView/gridView.d.ts.map +1 -1
- package/dist/src/view/gridView/gridView.js +60 -17
- package/dist/src/view/zoom.d.ts +14 -2
- package/dist/src/view/zoom.d.ts.map +1 -1
- package/dist/src/view/zoom.js +373 -76
- 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
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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 {
|
|
45
|
+
* @param {InteractionUiEvent} uiEvent The event to be wrapped
|
|
20
46
|
*/
|
|
21
|
-
constructor(point: import("../view/layout/point.js").default, uiEvent:
|
|
47
|
+
constructor(point: import("../view/layout/point.js").default, uiEvent: InteractionUiEvent);
|
|
22
48
|
point: import("../view/layout/point.js").default;
|
|
23
|
-
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":"
|
|
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 {
|
|
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":"
|
|
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":"
|
|
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
|
|
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
|
-
|
|
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
|
+
}
|
|
@@ -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":"
|
|
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"}
|