@genome-spy/core 0.71.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 +6842 -5365
- package/dist/bundle/index.js +159 -140
- package/dist/bundle/parquetRead-BnAGCa4_.js +1663 -0
- 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/formats/parquet.d.ts +12 -0
- package/dist/src/data/formats/parquet.d.ts.map +1 -0
- package/dist/src/data/formats/parquet.js +29 -0
- package/dist/src/data/formats/parquetRead.d.ts +18 -0
- package/dist/src/data/formats/parquetRead.d.ts.map +1 -0
- package/dist/src/data/formats/parquetRead.js +326 -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 +141 -17
- package/dist/src/encoder/encoder.d.ts +2 -2
- package/dist/src/fonts/bmFontManager.d.ts +1 -1
- 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 +83 -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/hashTable.d.ts +78 -0
- package/dist/src/gl/hashTable.d.ts.map +1 -0
- package/dist/src/gl/hashTable.js +164 -0
- package/dist/src/gl/includes/common.glsl.js +1 -1
- 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 +71 -39
- 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 +1 -1
- package/dist/src/marks/mark.d.ts.map +1 -1
- package/dist/src/marks/mark.js +8 -4
- 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/utils/radixSort.d.ts.map +1 -1
- package/dist/src/utils/radixSort.js +26 -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.d.ts +1 -1
- 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/unitView.d.ts +1 -1
- 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 +5 -2
|
@@ -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
|
+
}
|
|
@@ -48,7 +48,7 @@ export default class FacetView extends ContainerView<import("../spec/view.js").C
|
|
|
48
48
|
getAccessor(channel: "row" | "column"): any;
|
|
49
49
|
updateFacets(): void;
|
|
50
50
|
updateLabels(): void;
|
|
51
|
-
getFacetGroups():
|
|
51
|
+
getFacetGroups(): number[] | boolean[] | string[];
|
|
52
52
|
/**
|
|
53
53
|
* @param {import("./renderingContext/viewRenderingContext.js").default} context
|
|
54
54
|
* @param {import("./layout/rectangle.js").default} coords
|
|
@@ -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"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { isContinuous } from "vega-scale";
|
|
2
2
|
import {
|
|
3
|
+
asEventConfig,
|
|
3
4
|
asSelectionConfig,
|
|
4
5
|
createIntervalSelection,
|
|
5
6
|
isActiveIntervalSelection,
|
|
6
7
|
isIntervalSelectionConfig,
|
|
7
8
|
selectionContainsPoint,
|
|
8
9
|
} from "../../selection/selection.js";
|
|
10
|
+
import { createPrimitiveEventProxy } from "../../utils/interactionEvent.js";
|
|
9
11
|
import AxisGridView from "../axisGridView.js";
|
|
10
12
|
import AxisView, { CHANNEL_ORIENTS } from "../axisView.js";
|
|
11
13
|
import LayerView from "../layerView.js";
|
|
@@ -17,6 +19,9 @@ import UnitView from "../unitView.js";
|
|
|
17
19
|
import { markViewAsNonAddressable } from "../viewSelectors.js";
|
|
18
20
|
import Scrollbar from "./scrollbar.js";
|
|
19
21
|
import SelectionRect from "./selectionRect.js";
|
|
22
|
+
import { normalizeIntervalForSelection } from "../../scales/selectionDomainUtils.js";
|
|
23
|
+
import { zoomDomainByScaleType } from "../../scales/zoomDomainUtils.js";
|
|
24
|
+
import { createEventFilterFunction } from "../../utils/expression.js";
|
|
20
25
|
|
|
21
26
|
export default class GridChild {
|
|
22
27
|
/**
|
|
@@ -164,6 +169,42 @@ export default class GridChild {
|
|
|
164
169
|
})
|
|
165
170
|
);
|
|
166
171
|
|
|
172
|
+
const requiresShiftToBrush = channels.some((channel) =>
|
|
173
|
+
scaleResolutions[channel].isZoomable()
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const eventConfig =
|
|
177
|
+
/** @type {import("../../spec/parameter.js").EventConfig} */ (
|
|
178
|
+
select.on ??
|
|
179
|
+
(requiresShiftToBrush
|
|
180
|
+
? {
|
|
181
|
+
type: "mousedown",
|
|
182
|
+
filter: "event.shiftKey",
|
|
183
|
+
}
|
|
184
|
+
: {
|
|
185
|
+
type: "mousedown",
|
|
186
|
+
})
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (eventConfig.type !== "mousedown") {
|
|
190
|
+
throw new Error(
|
|
191
|
+
`Interval selection param "${name}" currently supports only "mousedown" in "on".`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const eventPredicate = eventConfig.filter
|
|
196
|
+
? createEventFilterFunction(eventConfig.filter)
|
|
197
|
+
: () => true;
|
|
198
|
+
|
|
199
|
+
const zoomEventConfig = resolveIntervalZoomEventConfig(
|
|
200
|
+
select.zoom,
|
|
201
|
+
requiresShiftToBrush,
|
|
202
|
+
name
|
|
203
|
+
);
|
|
204
|
+
const zoomEventPredicate = zoomEventConfig?.filter
|
|
205
|
+
? createEventFilterFunction(zoomEventConfig.filter)
|
|
206
|
+
: () => true;
|
|
207
|
+
|
|
167
208
|
if (this.selectionRect) {
|
|
168
209
|
throw new Error(
|
|
169
210
|
"Only one interval selection per container is currently allowed!"
|
|
@@ -304,7 +345,9 @@ export default class GridChild {
|
|
|
304
345
|
preventNextClickPropagation = true;
|
|
305
346
|
}
|
|
306
347
|
|
|
307
|
-
const startSelection =
|
|
348
|
+
const startSelection = eventPredicate(
|
|
349
|
+
event.proxiedMouseEvent
|
|
350
|
+
);
|
|
308
351
|
|
|
309
352
|
if (startSelection) {
|
|
310
353
|
clearSelection();
|
|
@@ -373,15 +416,9 @@ export default class GridChild {
|
|
|
373
416
|
|
|
374
417
|
for (const channel of channels) {
|
|
375
418
|
const scaleResolution = scaleResolutions[channel];
|
|
376
|
-
const { zoomExtent
|
|
419
|
+
const { zoomExtent } = scaleResolution;
|
|
377
420
|
const interval = intervals[channel];
|
|
378
421
|
|
|
379
|
-
if (["index", "locus"].includes(scale.type)) {
|
|
380
|
-
// These scales use integer values. Need to round them.
|
|
381
|
-
interval[0] = Math.ceil(interval[0]);
|
|
382
|
-
interval[1] = Math.ceil(interval[1]);
|
|
383
|
-
}
|
|
384
|
-
|
|
385
422
|
if (translatedRectangle) {
|
|
386
423
|
// When dragging, clamp the interval so that the size stays the same and the interval doesn't exceed zoomExtent
|
|
387
424
|
const size = interval[1] - interval[0];
|
|
@@ -398,11 +435,20 @@ export default class GridChild {
|
|
|
398
435
|
interval[1] = max;
|
|
399
436
|
interval[0] = max - size;
|
|
400
437
|
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const normalized = normalizeIntervalForChannel(
|
|
441
|
+
scaleResolution,
|
|
442
|
+
interval
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
if (!normalized) {
|
|
446
|
+
interval[0] = zoomExtent[0];
|
|
447
|
+
interval[1] = zoomExtent[0];
|
|
401
448
|
} else {
|
|
402
|
-
interval[0] =
|
|
403
|
-
interval[1] =
|
|
449
|
+
interval[0] = normalized[0];
|
|
450
|
+
interval[1] = normalized[1];
|
|
404
451
|
}
|
|
405
|
-
interval[1] = Math.min(zoomExtent[1], interval[1]);
|
|
406
452
|
}
|
|
407
453
|
|
|
408
454
|
setter({ type: "interval", intervals });
|
|
@@ -454,6 +500,86 @@ export default class GridChild {
|
|
|
454
500
|
true
|
|
455
501
|
);
|
|
456
502
|
|
|
503
|
+
view.addInteractionEventListener("wheel", (coords, event) => {
|
|
504
|
+
const wheelEvent = event.uiEvent;
|
|
505
|
+
if (!(wheelEvent instanceof WheelEvent)) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (
|
|
510
|
+
!zoomEventConfig ||
|
|
511
|
+
!zoomEventPredicate(createPrimitiveEventProxy(wheelEvent))
|
|
512
|
+
) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (
|
|
517
|
+
Math.abs(wheelEvent.deltaX) >= Math.abs(wheelEvent.deltaY)
|
|
518
|
+
) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
if (!isPointInsideSelection(event.point)) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const selection = selectionExpr();
|
|
526
|
+
if (!isActiveIntervalSelection(selection)) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const wheelMultiplier = wheelEvent.deltaMode ? 120 : 1;
|
|
531
|
+
const scaleFactor =
|
|
532
|
+
2 ** ((wheelEvent.deltaY * wheelMultiplier) / 300);
|
|
533
|
+
|
|
534
|
+
const anchor = invertPoint(event.point);
|
|
535
|
+
/** @type {typeof selection.intervals} */
|
|
536
|
+
const intervals = { ...selection.intervals };
|
|
537
|
+
let changed = false;
|
|
538
|
+
|
|
539
|
+
for (const channel of channels) {
|
|
540
|
+
const currentInterval = intervals[channel];
|
|
541
|
+
if (!currentInterval || currentInterval.length !== 2) {
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const scaleResolution = scaleResolutions[channel];
|
|
546
|
+
const scale = scaleResolution.getScale();
|
|
547
|
+
const zoomed = zoomDomainByScaleType(
|
|
548
|
+
scale,
|
|
549
|
+
/** @type {[number, number]} */ ([...currentInterval]),
|
|
550
|
+
anchor[channel],
|
|
551
|
+
scaleFactor,
|
|
552
|
+
{ onUnsupported: "identity" }
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
const normalized = normalizeIntervalForChannel(
|
|
556
|
+
scaleResolution,
|
|
557
|
+
zoomed
|
|
558
|
+
);
|
|
559
|
+
if (!normalized) {
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (
|
|
564
|
+
normalized[0] !== currentInterval[0] ||
|
|
565
|
+
normalized[1] !== currentInterval[1]
|
|
566
|
+
) {
|
|
567
|
+
intervals[channel] = normalized;
|
|
568
|
+
changed = true;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (changed) {
|
|
573
|
+
setter({
|
|
574
|
+
...selection,
|
|
575
|
+
type: "interval",
|
|
576
|
+
intervals,
|
|
577
|
+
});
|
|
578
|
+
wheelEvent.preventDefault();
|
|
579
|
+
event.stopPropagation();
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
|
|
457
583
|
// Handle mouse cursor changes
|
|
458
584
|
view.addInteractionEventListener("mousemove", (coords, event) => {
|
|
459
585
|
if (isPointInsideSelection(event.point)) {
|
|
@@ -711,6 +837,49 @@ export default class GridChild {
|
|
|
711
837
|
}
|
|
712
838
|
}
|
|
713
839
|
|
|
840
|
+
/**
|
|
841
|
+
* @param {import("../../spec/parameter.js").IntervalSelectionConfig["zoom"]} zoom
|
|
842
|
+
* @param {boolean} hasZoomableChannel
|
|
843
|
+
* @param {string} paramName
|
|
844
|
+
* @returns {import("../../spec/parameter.js").EventConfig | undefined}
|
|
845
|
+
*/
|
|
846
|
+
export function resolveIntervalZoomEventConfig(
|
|
847
|
+
zoom,
|
|
848
|
+
hasZoomableChannel,
|
|
849
|
+
paramName
|
|
850
|
+
) {
|
|
851
|
+
const defaultEnabled = !hasZoomableChannel;
|
|
852
|
+
const resolved = zoom === undefined ? defaultEnabled : zoom;
|
|
853
|
+
if (resolved === false) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
if (resolved === true) {
|
|
858
|
+
return { type: "wheel" };
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const eventConfig = asEventConfig(resolved);
|
|
862
|
+
if (eventConfig.type !== "wheel") {
|
|
863
|
+
throw new Error(
|
|
864
|
+
`Interval selection param "${paramName}" currently supports only "wheel" in "zoom".`
|
|
865
|
+
);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
return eventConfig;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* @param {import("../../scales/scaleResolution.js").default} scaleResolution
|
|
873
|
+
* @param {[number, number]} interval
|
|
874
|
+
* @returns {[number, number] | undefined}
|
|
875
|
+
*/
|
|
876
|
+
function normalizeIntervalForChannel(scaleResolution, interval) {
|
|
877
|
+
const scale = scaleResolution.getScale();
|
|
878
|
+
return normalizeIntervalForSelection(interval, scaleResolution.zoomExtent, {
|
|
879
|
+
roundToIntegers: scale.type === "index" || scale.type === "locus",
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
|
|
714
883
|
/**
|
|
715
884
|
* @param {import("../../spec/view.js").ViewBackground} viewBackground
|
|
716
885
|
* @returns {import("../../spec/view.js").UnitSpec}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gridView.d.ts","sourceRoot":"","sources":["../../../../src/view/gridView/gridView.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gridView.d.ts","sourceRoot":"","sources":["../../../../src/view/gridView/gridView.js"],"names":[],"mappings":"AAi+BA;;GAEG;AACH,sHAUC;AAoCD;;;;;GAKG;AACH,4CAJW,OAAO,wBAAwB,EAAE,OAAO,UACxC,OAAO,oBAAoB,EAAE,UAAU,YACvC,QAAQ,aAmBlB;AAnhCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,8BAH2D,KAAK,SAAnD,OAAQ,oBAAoB,EAAE,aAAc;IA6CrD;;;;;;;;;OASG;IACH,kBARW,KAAK,WACL,OAAO,4BAA4B,EAAE,OAAO,gBAC5C,aAAa,yFAEb,MAAM,WACN,MAAM,YACN,OAAO,YAAY,EAAE,WAAW,EAyC1C;IAtBG,uBAA0B;IAwB9B;;OAEG;IACH,6FAEC;IAED;;;;;;OAMG;IACH,4FAFa,SAAS,CAIrB;IAED;;;;;;;OAOG;IACH,oGAHW,MAAM,GACJ,SAAS,CASrB;IAED;;;;;OAKG;IACH,iGAQC;IAED;;;;;OAKG;IACH,qBAFW,MAAM,QAUhB;IAeD;;OAEG;IACH,mBAFW,qEAAM,QAWhB;IAYD;;OAEG;IACH,sFAEC;IAED,yBAEC;IAED;;OAEG;IACH,sCAKC;IAED;;;;OAIG;IACH,gCAqCC;;CAkrBJ;qBAn9B0D,gBAAgB;sBADrD,wBAAwB;0BAEpB,qBAAqB;sBAIzB,gBAAgB"}
|
|
@@ -871,6 +871,26 @@ export default class GridView extends ContainerView {
|
|
|
871
871
|
const pointedChild = this.#visibleChildren.find((gridChild) =>
|
|
872
872
|
gridChild.coords.containsPoint(event.point.x, event.point.y)
|
|
873
873
|
);
|
|
874
|
+
const pointedView = pointedChild?.view;
|
|
875
|
+
|
|
876
|
+
if (event.type === "wheelclaimprobe") {
|
|
877
|
+
// Probe path: claim wheel ownership without executing regular wheel
|
|
878
|
+
// behavior. InteractionController uses this to decide whether native
|
|
879
|
+
// wheel should be preventDefault()'ed before inertia kicks in.
|
|
880
|
+
if (!pointedView) {
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (isZoomInteractionView(pointedView)) {
|
|
885
|
+
if (hasZoomableResolutions(pointedView)) {
|
|
886
|
+
event.claimWheel();
|
|
887
|
+
}
|
|
888
|
+
} else {
|
|
889
|
+
pointedView.propagateInteractionEvent(event);
|
|
890
|
+
}
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
|
|
874
894
|
this.#keyboardZoomController?.handlePointerEvent(pointedChild, event);
|
|
875
895
|
|
|
876
896
|
for (const scrollbar of Object.values(pointedChild?.scrollbars ?? {})) {
|
|
@@ -882,7 +902,6 @@ export default class GridView extends ContainerView {
|
|
|
882
902
|
}
|
|
883
903
|
}
|
|
884
904
|
|
|
885
|
-
const pointedView = pointedChild?.view;
|
|
886
905
|
if (pointedView) {
|
|
887
906
|
pointedView.propagateInteractionEvent(event);
|
|
888
907
|
|
|
@@ -892,10 +911,7 @@ export default class GridView extends ContainerView {
|
|
|
892
911
|
|
|
893
912
|
// Hmm, maybe this should be registered when needed and not include
|
|
894
913
|
// as a hardcoded interaction?
|
|
895
|
-
if (
|
|
896
|
-
pointedView instanceof UnitView ||
|
|
897
|
-
pointedView instanceof LayerView
|
|
898
|
-
) {
|
|
914
|
+
if (isZoomInteractionView(pointedView)) {
|
|
899
915
|
interactionToZoom(
|
|
900
916
|
event,
|
|
901
917
|
pointedChild.coords,
|
|
@@ -923,8 +939,22 @@ export default class GridView extends ContainerView {
|
|
|
923
939
|
* @param {import("../layout/rectangle.js").default} coords Coordinates
|
|
924
940
|
* @param {View} view
|
|
925
941
|
* @param {import("../zoom.js").ZoomEvent} zoomEvent
|
|
942
|
+
* @returns {boolean} `true` when there was at least one zoomable resolution
|
|
926
943
|
*/
|
|
927
944
|
#handleZoom(coords, view, zoomEvent) {
|
|
945
|
+
let zoomable = false;
|
|
946
|
+
let changed = false;
|
|
947
|
+
|
|
948
|
+
const p = coords.normalizePoint(zoomEvent.x, zoomEvent.y);
|
|
949
|
+
const tp = coords.normalizePoint(
|
|
950
|
+
zoomEvent.x + zoomEvent.xDelta,
|
|
951
|
+
zoomEvent.y + zoomEvent.yDelta
|
|
952
|
+
);
|
|
953
|
+
const delta = {
|
|
954
|
+
x: tp.x - p.x,
|
|
955
|
+
y: tp.y - p.y,
|
|
956
|
+
};
|
|
957
|
+
|
|
928
958
|
for (const [channel, resolutionSet] of Object.entries(
|
|
929
959
|
getZoomableResolutions(view)
|
|
930
960
|
)) {
|
|
@@ -932,27 +962,23 @@ export default class GridView extends ContainerView {
|
|
|
932
962
|
continue;
|
|
933
963
|
}
|
|
934
964
|
|
|
935
|
-
|
|
936
|
-
const tp = coords.normalizePoint(
|
|
937
|
-
zoomEvent.x + zoomEvent.xDelta,
|
|
938
|
-
zoomEvent.y + zoomEvent.yDelta
|
|
939
|
-
);
|
|
940
|
-
|
|
941
|
-
const delta = {
|
|
942
|
-
x: tp.x - p.x,
|
|
943
|
-
y: tp.y - p.y,
|
|
944
|
-
};
|
|
965
|
+
zoomable = true;
|
|
945
966
|
|
|
946
967
|
for (const resolution of resolutionSet) {
|
|
947
|
-
resolution.zoom(
|
|
968
|
+
const resolutionChanged = resolution.zoom(
|
|
948
969
|
2 ** zoomEvent.zDelta,
|
|
949
970
|
channel == "y" ? 1 - p[channel] : p[channel],
|
|
950
971
|
channel == "x" ? delta.x : -delta.y
|
|
951
972
|
);
|
|
973
|
+
changed = resolutionChanged || changed;
|
|
952
974
|
}
|
|
953
975
|
}
|
|
954
976
|
|
|
955
|
-
|
|
977
|
+
if (changed) {
|
|
978
|
+
this.context.animator.requestRender();
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
return zoomable;
|
|
956
982
|
}
|
|
957
983
|
|
|
958
984
|
/**
|
|
@@ -980,6 +1006,23 @@ export function isClippedChildren(view) {
|
|
|
980
1006
|
return clipped;
|
|
981
1007
|
}
|
|
982
1008
|
|
|
1009
|
+
/**
|
|
1010
|
+
* @param {View} view
|
|
1011
|
+
* @returns {boolean}
|
|
1012
|
+
*/
|
|
1013
|
+
function hasZoomableResolutions(view) {
|
|
1014
|
+
const zoomableResolutions = getZoomableResolutions(view);
|
|
1015
|
+
return zoomableResolutions.x.size > 0 || zoomableResolutions.y.size > 0;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* @param {View} view
|
|
1020
|
+
* @returns {view is UnitView | LayerView}
|
|
1021
|
+
*/
|
|
1022
|
+
function isZoomInteractionView(view) {
|
|
1023
|
+
return view instanceof UnitView || view instanceof LayerView;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
983
1026
|
/**
|
|
984
1027
|
* @param {import("../../spec/view.js").AnyConcatSpec} spec
|
|
985
1028
|
* @returns {("horizontal" | "vertical")[]}
|
|
@@ -21,7 +21,7 @@ export default class UnitView<TSpec extends import("../spec/view.js").UnitSpec =
|
|
|
21
21
|
constructor(spec: TSpec, context: import("../types/viewContext.js").default, layoutParent: import("./containerView.js").default, dataParent: import("./view.js").default, name: string, options?: import("./view.js").ViewOptions);
|
|
22
22
|
/** @type {import("../marks/mark.js").default} */
|
|
23
23
|
mark: import("../marks/mark.js").default;
|
|
24
|
-
getMarkType(): "link" | "
|
|
24
|
+
getMarkType(): "link" | "point" | "text" | "rect" | "rule";
|
|
25
25
|
/**
|
|
26
26
|
* Pulls scales and axes up in the view hierarcy according to the resolution rules, using dataParents.
|
|
27
27
|
* TODO: legends
|
package/dist/src/view/zoom.d.ts
CHANGED
|
@@ -3,11 +3,11 @@ export function isStillZooming(): boolean;
|
|
|
3
3
|
/**
|
|
4
4
|
* @param {import("../utils/interactionEvent.js").default} event
|
|
5
5
|
* @param {import("./layout/rectangle.js").default} coords
|
|
6
|
-
* @param {(zoomEvent: ZoomEvent) => void} handleZoom
|
|
6
|
+
* @param {(zoomEvent: ZoomEvent) => boolean | void} handleZoom
|
|
7
7
|
* @param {import("../types/viewContext.js").Hover} [hover]
|
|
8
8
|
* @param {import("../utils/animator.js").default} [animator]
|
|
9
9
|
*/
|
|
10
|
-
export function interactionToZoom(event: import("../utils/interactionEvent.js").default, coords: import("./layout/rectangle.js").default, handleZoom: (zoomEvent: ZoomEvent) => void, hover?: import("../types/viewContext.js").Hover, animator?: import("../utils/animator.js").default): void;
|
|
10
|
+
export function interactionToZoom(event: import("../utils/interactionEvent.js").default, coords: import("./layout/rectangle.js").default, handleZoom: (zoomEvent: ZoomEvent) => boolean | void, hover?: import("../types/viewContext.js").Hover, animator?: import("../utils/animator.js").default): void;
|
|
11
11
|
export type ZoomEvent = {
|
|
12
12
|
x: number;
|
|
13
13
|
y: number;
|
|
@@ -15,4 +15,16 @@ export type ZoomEvent = {
|
|
|
15
15
|
yDelta: number;
|
|
16
16
|
zDelta: number;
|
|
17
17
|
};
|
|
18
|
+
export type ZoomInteractionState = {
|
|
19
|
+
smoother: ReturnType<typeof makeLerpSmoother>;
|
|
20
|
+
touchPanEventBuffer: RingBuffer<{
|
|
21
|
+
point: Point;
|
|
22
|
+
timestamp: number;
|
|
23
|
+
}>;
|
|
24
|
+
touchPanLastPoint: Point | undefined;
|
|
25
|
+
touchPanPointerCount: 0 | 1 | 2;
|
|
26
|
+
};
|
|
27
|
+
import { makeLerpSmoother } from "../utils/animator.js";
|
|
28
|
+
import RingBuffer from "../utils/ringBuffer.js";
|
|
29
|
+
import Point from "./layout/point.js";
|
|
18
30
|
//# sourceMappingURL=zoom.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zoom.d.ts","sourceRoot":"","sources":["../../../src/view/zoom.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"zoom.d.ts","sourceRoot":"","sources":["../../../src/view/zoom.js"],"names":[],"mappings":"AAgCA,yCAEC;AAED,0CAGC;AAgBD;;;;;;GAMG;AACH,yCANW,OAAO,8BAA8B,EAAE,OAAO,UAC9C,OAAO,uBAAuB,EAAE,OAAO,cACvC,CAAC,SAAS,EAAE,SAAS,KAAK,OAAO,GAAG,IAAI,UACxC,OAAO,yBAAyB,EAAE,KAAK,aACvC,OAAO,sBAAsB,EAAE,OAAO,QA8LhD;;OAxPS,MAAM;OACN,MAAM;YACN,MAAM;YACN,MAAM;YACN,MAAM;;;cAUN,UAAU,CAAC,OAAO,gBAAgB,CAAC;yBACnC,UAAU,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC,CAAC;uBAC7C,KAAK,GAAG,SAAS;0BACjB,CAAC,GAAG,CAAC,GAAG,CAAC;;iCAVc,sBAAsB;uBAChC,wBAAwB;kBAE7B,mBAAmB"}
|