@genome-spy/core 0.62.2 → 0.64.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 (64) hide show
  1. package/dist/bundle/index.es.js +4568 -4301
  2. package/dist/bundle/index.js +371 -297
  3. package/dist/schema.json +84 -12
  4. package/dist/src/genomeSpy.d.ts +11 -0
  5. package/dist/src/genomeSpy.d.ts.map +1 -1
  6. package/dist/src/genomeSpy.js +98 -20
  7. package/dist/src/gl/includes/common.glsl.js +1 -1
  8. package/dist/src/gl/webGLHelper.d.ts +18 -14
  9. package/dist/src/gl/webGLHelper.d.ts.map +1 -1
  10. package/dist/src/gl/webGLHelper.js +65 -64
  11. package/dist/src/index.d.ts.map +1 -1
  12. package/dist/src/index.js +1 -0
  13. package/dist/src/marks/link.fragment.glsl.js +1 -1
  14. package/dist/src/marks/link.vertex.glsl.js +1 -1
  15. package/dist/src/marks/mark.d.ts +6 -1
  16. package/dist/src/marks/mark.d.ts.map +1 -1
  17. package/dist/src/marks/mark.js +10 -16
  18. package/dist/src/marks/point.fragment.glsl.js +1 -1
  19. package/dist/src/marks/point.vertex.glsl.js +1 -1
  20. package/dist/src/marks/rect.fragment.glsl.js +1 -1
  21. package/dist/src/marks/rect.vertex.glsl.js +1 -1
  22. package/dist/src/marks/rule.fragment.glsl.js +1 -1
  23. package/dist/src/marks/rule.vertex.glsl.js +1 -1
  24. package/dist/src/marks/text.d.ts.map +1 -1
  25. package/dist/src/marks/text.fragment.glsl.js +1 -1
  26. package/dist/src/marks/text.js +7 -15
  27. package/dist/src/marks/text.vertex.glsl.js +1 -1
  28. package/dist/src/selection/selection.d.ts +5 -0
  29. package/dist/src/selection/selection.d.ts.map +1 -1
  30. package/dist/src/selection/selection.js +43 -6
  31. package/dist/src/selection/selection.test.d.ts +2 -0
  32. package/dist/src/selection/selection.test.d.ts.map +1 -0
  33. package/dist/src/selection/selection.test.js +14 -0
  34. package/dist/src/spec/parameter.d.ts +28 -2
  35. package/dist/src/styles/{genome-spy.scss → genome-spy.css} +25 -21
  36. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  37. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  38. package/dist/src/styles/genome-spy.css.js +264 -195
  39. package/dist/src/styles/update.sh +14 -4
  40. package/dist/src/types/embedApi.d.ts +16 -0
  41. package/dist/src/types/viewContext.d.ts +0 -2
  42. package/dist/src/utils/expression.d.ts +5 -0
  43. package/dist/src/utils/expression.d.ts.map +1 -1
  44. package/dist/src/utils/expression.js +37 -0
  45. package/dist/src/utils/interactionEvent.d.ts +18 -1
  46. package/dist/src/utils/interactionEvent.d.ts.map +1 -1
  47. package/dist/src/utils/interactionEvent.js +101 -1
  48. package/dist/src/utils/interactionEvent.test.d.ts +2 -0
  49. package/dist/src/utils/interactionEvent.test.d.ts.map +1 -0
  50. package/dist/src/utils/interactionEvent.test.js +35 -0
  51. package/dist/src/view/facetView.d.ts +1 -1
  52. package/dist/src/view/facetView.d.ts.map +1 -1
  53. package/dist/src/view/gridView/gridView.js +1 -1
  54. package/dist/src/view/renderingContext/bufferedViewRenderingContext.d.ts +32 -17
  55. package/dist/src/view/renderingContext/bufferedViewRenderingContext.d.ts.map +1 -1
  56. package/dist/src/view/renderingContext/bufferedViewRenderingContext.js +85 -39
  57. package/dist/src/view/renderingContext/simpleViewRenderingContext.d.ts.map +1 -1
  58. package/dist/src/view/renderingContext/simpleViewRenderingContext.js +5 -1
  59. package/dist/src/view/renderingContext/viewRenderingContext.d.ts +1 -0
  60. package/dist/src/view/renderingContext/viewRenderingContext.d.ts.map +1 -1
  61. package/dist/src/view/renderingContext/viewRenderingContext.js +4 -0
  62. package/dist/src/view/unitView.d.ts.map +1 -1
  63. package/dist/src/view/unitView.js +45 -2
  64. package/package.json +8 -8
@@ -4,8 +4,10 @@
4
4
  * as in the DOM.
5
5
  */
6
6
  export default class InteractionEvent {
7
+ /** @type {MouseEvent} */
8
+ #primitiveMouseEventProxy;
9
+
7
10
  /**
8
- *
9
11
  * @param {import("../view/layout/point.js").default} point Event coordinates
10
12
  * inside the visualization canvas.
11
13
  * @param {UIEvent} uiEvent The event to be wrapped
@@ -27,10 +29,27 @@ export default class InteractionEvent {
27
29
  this.stopped = true;
28
30
  }
29
31
 
32
+ /**
33
+ * The event type string of the underlying UI event (e.g. "click", "keydown").
34
+ *
35
+ * This getter proxies and returns the `type` property from the internal `UIEvent` instance (`this.uiEvent`).
36
+ *
37
+ * @returns {string} The UI event type.
38
+ */
30
39
  get type() {
31
40
  return this.uiEvent.type;
32
41
  }
33
42
 
43
+ get proxiedMouseEvent() {
44
+ if (!this.#primitiveMouseEventProxy) {
45
+ this.#primitiveMouseEventProxy = createPrimitiveEventProxy(
46
+ this.mouseEvent
47
+ );
48
+ }
49
+
50
+ return this.#primitiveMouseEventProxy;
51
+ }
52
+
34
53
  get mouseEvent() {
35
54
  if (this.uiEvent instanceof MouseEvent) {
36
55
  return this.uiEvent;
@@ -39,3 +58,84 @@ export default class InteractionEvent {
39
58
  }
40
59
  }
41
60
  }
61
+
62
+ /**
63
+ * Create a safe proxy for an event-like object that exposes only primitive
64
+ * (string, number, boolean, bigint, symbol, undefined) properties and null.
65
+ *
66
+ * @param {T} target The event-like object to wrap.
67
+ * @returns {T} A proxy exposing only primitive properties.
68
+ * @template T
69
+ */
70
+ export function createPrimitiveEventProxy(target) {
71
+ /**
72
+ * @param {any} v
73
+ * @returns {boolean}
74
+ */
75
+ const isPrimitiveOrNull = (v) =>
76
+ v === null || (typeof v !== "object" && typeof v !== "function");
77
+
78
+ /** @type {ProxyHandler<any>} */
79
+ const handler = {
80
+ /**
81
+ * @param {any} target
82
+ * @param {PropertyKey} prop
83
+ * @param {any} receiver
84
+ */
85
+ get(target, prop, receiver) {
86
+ const value = Reflect.get(target, prop, target);
87
+ if (!isPrimitiveOrNull(value)) {
88
+ throw new Error(
89
+ `Access to non-primitive property "${String(prop)}" is not allowed.`
90
+ );
91
+ }
92
+ return value;
93
+ },
94
+
95
+ getPrototypeOf() {
96
+ return null;
97
+ },
98
+
99
+ /**
100
+ * @param {any} target
101
+ * @returns {ArrayLike<string|symbol>}
102
+ */
103
+ ownKeys(target) {
104
+ const keys = Reflect.ownKeys(target).filter((k) =>
105
+ isPrimitiveOrNull(target[k])
106
+ );
107
+ return keys.map((k) => (typeof k === "symbol" ? k : String(k)));
108
+ },
109
+
110
+ /**
111
+ * @param {any} target
112
+ * @param {PropertyKey} prop
113
+ * @returns {PropertyDescriptor|undefined}
114
+ */
115
+ getOwnPropertyDescriptor(target, prop) {
116
+ const desc = Reflect.getOwnPropertyDescriptor(target, prop);
117
+ if (!desc) return undefined;
118
+ // hide accessor properties (getters/setters)
119
+ if ("get" in desc || "set" in desc) return undefined;
120
+ if (!isPrimitiveOrNull(desc.value)) return undefined;
121
+ // Preserve configurability/enumerability/writability to satisfy proxy invariants
122
+ return {
123
+ value: desc.value,
124
+ writable: !!desc.writable,
125
+ enumerable: !!desc.enumerable,
126
+ configurable: !!desc.configurable,
127
+ };
128
+ },
129
+
130
+ /**
131
+ * @param {any} target
132
+ * @param {PropertyKey} prop
133
+ * @returns {boolean} */
134
+ has(target, prop) {
135
+ if (!(prop in target)) return false;
136
+ return isPrimitiveOrNull(target[prop]);
137
+ },
138
+ };
139
+
140
+ return new Proxy(target, handler);
141
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=interactionEvent.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactionEvent.test.d.ts","sourceRoot":"","sources":["../../../src/utils/interactionEvent.test.js"],"names":[],"mappings":""}
@@ -0,0 +1,35 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { createPrimitiveEventProxy } from "./interactionEvent.js";
3
+
4
+ describe("createPrimitiveEventProxy", () => {
5
+ it("exposes primitive properties and hides non-primitives", () => {
6
+ const mock = {
7
+ type: "click",
8
+ clientX: 42,
9
+ meta: { foo: "bar" },
10
+ nested: { a: 1 },
11
+ };
12
+
13
+ /** @type {any} */
14
+ const proxy = createPrimitiveEventProxy(mock);
15
+
16
+ // allowed primitives
17
+ expect(proxy.type).toBe("click");
18
+ expect(proxy.clientX).toBe(42);
19
+
20
+ // non-primitive access throws
21
+ expect(() => proxy.meta).toThrow(/non-primitive/);
22
+
23
+ // keys enumeration hides non-primitives
24
+ const keys = Object.keys(proxy);
25
+ expect(keys).toContain("type");
26
+ expect(keys).not.toContain("meta");
27
+
28
+ // `in` operator respects the policy
29
+ expect("type" in proxy).toBe(true);
30
+ expect("meta" in proxy).toBe(false);
31
+
32
+ // prototype is hidden
33
+ expect(Object.getPrototypeOf(proxy)).toBeNull();
34
+ });
35
+ });
@@ -48,7 +48,7 @@ export default class FacetView extends ContainerView {
48
48
  getAccessor(channel: "row" | "column"): any;
49
49
  updateFacets(): void;
50
50
  updateLabels(): void;
51
- getFacetGroups(): any[];
51
+ getFacetGroups(): string[] | number[] | boolean[];
52
52
  /**
53
53
  * @param {import("./renderingContext/viewRenderingContext.js").default} context
54
54
  * @param {import("./layout/rectangle.js").default} coords
@@ -1 +1 @@
1
- {"version":3,"file":"facetView.d.ts","sourceRoot":"","sources":["../../../src/view/facetView.js"],"names":[],"mappings":"AAmDA;;;;;;;;;;;;;;GAcG;AACH;IACI;;;;;;;OAOG;IACH;;;;;;OAMG;IACH,kBALW,OAAO,gBAAgB,EAAE,SAAS,WAClC,OAAO,gBAAgB,EAAE,WAAW,UACpC,aAAa,QACb,MAAM,EA8BhB;IAzBG,UAAgB;IAEhB,WAEC;IAED;;;;OAIG;IACH,aAFU,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,CAYvC;IAED,oDAAoD;IACpD,kBADY,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,CACa;IA2BjE,sBAIC;IAED;;;OAGG;IACH,qBAFW,KAAK,GAAG,QAAQ,OAwB1B;IAED,qBAyCC;IAED,qBASC;IA6BD,wBAgBC;IAOD;;;;OAIG;IACH,gBAJW,OAAO,4CAA4C,EAAE,OAAO,UAC5D,OAAO,uBAAuB,EAAE,OAAO,YACvC,OAAO,WAAW,EAAE,gBAAgB,QA2L9C;IA3KO,wBAA0B;CA4KrC;2BA/bY,QAAQ,GAAG,KAAK;;;;;;aA4CnB,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE;;;0BA1Df,oBAAoB;qBACzB,eAAe"}
1
+ {"version":3,"file":"facetView.d.ts","sourceRoot":"","sources":["../../../src/view/facetView.js"],"names":[],"mappings":"AAmDA;;;;;;;;;;;;;;GAcG;AACH;IACI;;;;;;;OAOG;IACH;;;;;;OAMG;IACH,kBALW,OAAO,gBAAgB,EAAE,SAAS,WAClC,OAAO,gBAAgB,EAAE,WAAW,UACpC,aAAa,QACb,MAAM,EA8BhB;IAzBG,UAAgB;IAEhB,WAEC;IAED;;;;OAIG;IACH,aAFU,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,CAYvC;IAED,oDAAoD;IACpD,kBADY,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,CACa;IA2BjE,sBAIC;IAED;;;OAGG;IACH,qBAFW,KAAK,GAAG,QAAQ,OAwB1B;IAED,qBAyCC;IAED,qBASC;IA6BD,kDAgBC;IAOD;;;;OAIG;IACH,gBAJW,OAAO,4CAA4C,EAAE,OAAO,UAC5D,OAAO,uBAAuB,EAAE,OAAO,YACvC,OAAO,WAAW,EAAE,gBAAgB,QA2L9C;IA3KO,wBAA0B;CA4KrC;2BA/bY,QAAQ,GAAG,KAAK;;;;;;aA4CnB,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE;;;0BA1Df,oBAAoB;qBACzB,eAAe"}
@@ -449,7 +449,7 @@ export default class GridView extends ContainerView {
449
449
  context.pushView(this, coords);
450
450
 
451
451
  const flexOpts = {
452
- devicePixelRatio: this.context.devicePixelRatio,
452
+ devicePixelRatio: context.getDevicePixelRatio(),
453
453
  };
454
454
  const columnFlexCoords = mapToPixelCoords(
455
455
  this.#makeFlexItems("column"),
@@ -1,30 +1,45 @@
1
+ /**
2
+ * @typedef {object} BufferedViewRenderingOptions
3
+ * @prop {import("../../gl/webGLHelper.js").default} webGLHelper
4
+ * @prop {{width: number, height: number}} canvasSize Size of the canvas in logical pixels.
5
+ * @prop {number} devicePixelRatio
6
+ * @prop {import("twgl.js").FramebufferInfo} [framebufferInfo]
7
+ * @prop {string} [clearColor] Clear color for the WebGL context,
8
+ * defaults to transparent black.
9
+ */
10
+ /**
11
+ * View rendering context that buffers the actual WebGL rendering for
12
+ * efficient animation.
13
+ */
1
14
  export default class BufferedViewRenderingContext extends ViewRenderingContext {
2
15
  /**
3
16
  * @param {import("../../types/rendering.js").GlobalRenderingOptions} globalOptions
4
- * @param {import("../../gl/webGLHelper.js").default} webGLHelper
17
+ * @param {BufferedViewRenderingOptions} bufferedOptions
5
18
  */
6
- constructor(globalOptions: import("../../types/rendering.js").GlobalRenderingOptions, webGLHelper: import("../../gl/webGLHelper.js").default);
7
- webGLHelper: import("../../gl/webGLHelper.js").default;
8
- /**
9
- * @type {import("../../types/rendering.js").BufferedRenderingRequest[]}
10
- */
11
- buffer: import("../../types/rendering.js").BufferedRenderingRequest[];
12
- /** @type {import("../layout/rectangle.js").default} */
13
- coords: import("../layout/rectangle.js").default;
14
- /** @type {Set<import("../view.js").default>} */
15
- views: Set<import("../view.js").default>;
19
+ constructor(globalOptions: import("../../types/rendering.js").GlobalRenderingOptions, bufferedOptions: BufferedViewRenderingOptions);
16
20
  /**
17
21
  * Renders marks in an optimized order, minimizing the number of WebGL state
18
22
  * changes.
19
23
  */
20
24
  render(): void;
21
- _buildBatch(): void;
25
+ #private;
26
+ }
27
+ export type BufferedViewRenderingOptions = {
28
+ webGLHelper: import("../../gl/webGLHelper.js").default;
22
29
  /**
23
- * Store the operations as a sequence of commands for cheap subsequent rendering.
24
- *
25
- * @type {(function():void)[]}
30
+ * Size of the canvas in logical pixels.
26
31
  */
27
- batch: (() => void)[];
28
- }
32
+ canvasSize: {
33
+ width: number;
34
+ height: number;
35
+ };
36
+ devicePixelRatio: number;
37
+ framebufferInfo?: import("twgl.js").FramebufferInfo;
38
+ /**
39
+ * Clear color for the WebGL context,
40
+ * defaults to transparent black.
41
+ */
42
+ clearColor?: string;
43
+ };
29
44
  import ViewRenderingContext from "./viewRenderingContext.js";
30
45
  //# sourceMappingURL=bufferedViewRenderingContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bufferedViewRenderingContext.d.ts","sourceRoot":"","sources":["../../../../src/view/renderingContext/bufferedViewRenderingContext.js"],"names":[],"mappings":"AAIA;IACI;;;OAGG;IACH,2BAHW,OAAO,0BAA0B,EAAE,sBAAsB,eACzD,OAAO,yBAAyB,EAAE,OAAO,EAiBnD;IAZG,uDAA8B;IAE9B;;OAEG;IACH,QAFU,OAAO,0BAA0B,EAAE,wBAAwB,EAAE,CAEvD;IAEhB,uDAAuD;IACvD,QADW,OAAO,wBAAwB,EAAE,OAAO,CAC5B;IAEvB,gDAAgD;IAChD,OADW,GAAG,CAAC,OAAO,YAAY,EAAE,OAAO,CAAC,CACtB;IAsC1B;;;OAGG;IACH,eA+BC;IAED,oBA2EC;IA1EG;;;;OAIG;IACH,OAFU,CAAC,MAAW,IAAI,CAAC,EAAE,CAEd;CAsEtB;iCA5KgC,2BAA2B"}
1
+ {"version":3,"file":"bufferedViewRenderingContext.d.ts","sourceRoot":"","sources":["../../../../src/view/renderingContext/bufferedViewRenderingContext.js"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AAEH;;;GAGG;AACH;IA2BI;;;OAGG;IACH,2BAHW,OAAO,0BAA0B,EAAE,sBAAsB,mBACzD,4BAA4B,EActC;IAyCD;;;OAGG;IACH,eAmCC;;CA8EJ;;iBArNS,OAAO,yBAAyB,EAAE,OAAO;;;;gBACzC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAC;sBAC/B,MAAM;sBACN,OAAO,SAAS,EAAE,eAAe;;;;;iBACjC,MAAM;;iCATiB,2BAA2B"}
@@ -1,27 +1,69 @@
1
1
  import { group } from "d3-array";
2
2
 
3
3
  import ViewRenderingContext from "./viewRenderingContext.js";
4
-
4
+ import { color } from "d3-color";
5
+
6
+ /**
7
+ * @typedef {object} BufferedViewRenderingOptions
8
+ * @prop {import("../../gl/webGLHelper.js").default} webGLHelper
9
+ * @prop {{width: number, height: number}} canvasSize Size of the canvas in logical pixels.
10
+ * @prop {number} devicePixelRatio
11
+ * @prop {import("twgl.js").FramebufferInfo} [framebufferInfo]
12
+ * @prop {string} [clearColor] Clear color for the WebGL context,
13
+ * defaults to transparent black.
14
+ */
15
+
16
+ /**
17
+ * View rendering context that buffers the actual WebGL rendering for
18
+ * efficient animation.
19
+ */
5
20
  export default class BufferedViewRenderingContext extends ViewRenderingContext {
21
+ /** @type {[number, number, number, number]} */
22
+ #clearColor = [0, 0, 0, 0];
23
+
24
+ /** @type {(() => void)[]} */
25
+ #batch;
26
+
27
+ /**
28
+ * @type {import("../../types/rendering.js").BufferedRenderingRequest[]}
29
+ */
30
+ #buffer = [];
31
+
32
+ /** @type {import("twgl.js").FramebufferInfo} */
33
+ #framebufferInfo;
34
+
35
+ /** @type {import("../../gl/webGLHelper.js").default} */
36
+ #webGLHelper;
37
+
38
+ /** @type {Set<import("../view.js").default>} */
39
+ #views = new Set();
40
+
41
+ /** @type {import("../layout/rectangle.js").default} */
42
+ #coords = undefined;
43
+
44
+ #dpr = 1;
45
+ #canvasSize = { width: 0, height: 0 };
46
+
6
47
  /**
7
48
  * @param {import("../../types/rendering.js").GlobalRenderingOptions} globalOptions
8
- * @param {import("../../gl/webGLHelper.js").default} webGLHelper
49
+ * @param {BufferedViewRenderingOptions} bufferedOptions
9
50
  */
10
- constructor(globalOptions, webGLHelper) {
51
+ constructor(globalOptions, bufferedOptions) {
11
52
  super(globalOptions);
12
53
 
13
- this.webGLHelper = webGLHelper;
14
-
15
- /**
16
- * @type {import("../../types/rendering.js").BufferedRenderingRequest[]}
17
- */
18
- this.buffer = [];
54
+ this.#webGLHelper = bufferedOptions.webGLHelper;
55
+ this.#framebufferInfo = bufferedOptions.framebufferInfo;
56
+ this.#dpr = bufferedOptions.devicePixelRatio;
57
+ this.#canvasSize = bufferedOptions.canvasSize;
19
58
 
20
- /** @type {import("../layout/rectangle.js").default} */
21
- this.coords = undefined;
59
+ if (bufferedOptions.clearColor) {
60
+ const c = color(bufferedOptions.clearColor).rgb();
61
+ this.#clearColor = [c.r / 255, c.g / 255, c.b / 255, c.opacity];
62
+ }
63
+ }
22
64
 
23
- /** @type {Set<import("../view.js").default>} */
24
- this.views = new Set();
65
+ getDevicePixelRatio() {
66
+ return this.#dpr;
25
67
  }
26
68
 
27
69
  /**
@@ -33,8 +75,8 @@ export default class BufferedViewRenderingContext extends ViewRenderingContext {
33
75
  * @override
34
76
  */
35
77
  pushView(view, coords) {
36
- this.views.add(view);
37
- this.coords = coords;
78
+ this.#views.add(view);
79
+ this.#coords = coords;
38
80
  }
39
81
 
40
82
  /**
@@ -50,10 +92,10 @@ export default class BufferedViewRenderingContext extends ViewRenderingContext {
50
92
 
51
93
  const callback = mark.render(options);
52
94
  if (callback) {
53
- this.buffer.push({
95
+ this.#buffer.push({
54
96
  mark,
55
97
  callback,
56
- coords: this.coords,
98
+ coords: this.#coords,
57
99
  clipRect: options.clipRect,
58
100
  });
59
101
  }
@@ -64,45 +106,47 @@ export default class BufferedViewRenderingContext extends ViewRenderingContext {
64
106
  * changes.
65
107
  */
66
108
  render() {
67
- if (!this.batch) {
68
- this._buildBatch();
109
+ if (!this.#batch) {
110
+ this.#buildBatch();
69
111
  }
70
112
 
71
- if (this.batch.length == 0) {
113
+ if (this.#batch.length == 0) {
72
114
  return;
73
115
  }
74
116
 
75
- const gl = this.webGLHelper.gl;
76
- const picking = this.globalOptions.picking;
117
+ const gl = this.#webGLHelper.gl;
118
+ const fbi = this.#framebufferInfo;
77
119
 
78
- gl.bindFramebuffer(
79
- gl.FRAMEBUFFER,
80
- picking ? this.webGLHelper._pickingBufferInfo.framebuffer : null
81
- );
120
+ if (fbi) {
121
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbi.framebuffer);
122
+ gl.viewport(0, 0, fbi.width, fbi.height);
123
+ } else {
124
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
125
+ }
82
126
 
83
- this.webGLHelper.clearAll();
127
+ gl.disable(gl.SCISSOR_TEST);
128
+ gl.clearColor(...this.#clearColor);
129
+ gl.clear(gl.COLOR_BUFFER_BIT);
84
130
 
85
- for (const view of this.views) {
131
+ for (const view of this.#views) {
86
132
  view.onBeforeRender();
87
133
  }
88
134
 
89
135
  // Execute the batch
90
- for (const op of this.batch) {
136
+ for (const op of this.#batch) {
91
137
  op();
92
138
  }
93
139
 
94
- if (picking) {
140
+ if (this.#framebufferInfo) {
95
141
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
96
142
  }
97
143
  }
98
144
 
99
- _buildBatch() {
145
+ #buildBatch() {
100
146
  /**
101
147
  * Store the operations as a sequence of commands for cheap subsequent rendering.
102
- *
103
- * @type {(function():void)[]}
104
148
  */
105
- this.batch = [];
149
+ this.#batch = [];
106
150
 
107
151
  /**
108
152
  * Is drawing enabled or not. As an optimization this is toggled off for invisible views.
@@ -129,7 +173,7 @@ export default class BufferedViewRenderingContext extends ViewRenderingContext {
129
173
  // Note: by reversing the buffer, we ensure ensure that the last instance
130
174
  // of a mark determines the order of the groups.
131
175
  const requestByMark = group(
132
- this.buffer.reverse(),
176
+ this.#buffer.reverse(),
133
177
  (request) => request.mark
134
178
  );
135
179
 
@@ -140,11 +184,11 @@ export default class BufferedViewRenderingContext extends ViewRenderingContext {
140
184
  }
141
185
 
142
186
  // eslint-disable-next-line no-loop-func
143
- this.batch.push(() => {
187
+ this.#batch.push(() => {
144
188
  enabled = mark.unitView.getEffectiveOpacity() > 0;
145
189
  });
146
190
  // Change program, set common uniforms (mark properties, shared domains)
147
- this.batch.push(
191
+ this.#batch.push(
148
192
  ...mark
149
193
  .prepareRender(this.globalOptions)
150
194
  .map((op) => ifEnabled(op))
@@ -156,18 +200,20 @@ export default class BufferedViewRenderingContext extends ViewRenderingContext {
156
200
  const coords = request.coords;
157
201
  // Render each facet
158
202
  if (!coords.equals(previousCoords)) {
159
- this.batch.push(
203
+ this.#batch.push(
160
204
  // eslint-disable-next-line no-loop-func
161
205
  ifEnabled(() => {
162
206
  // Suppress rendering if viewport is outside the clipRect
163
207
  viewportVisible = mark.setViewport(
208
+ this.#canvasSize,
209
+ this.#dpr,
164
210
  coords,
165
211
  request.clipRect
166
212
  );
167
213
  })
168
214
  );
169
215
  }
170
- this.batch.push(ifEnabledAndVisible(request.callback));
216
+ this.#batch.push(ifEnabledAndVisible(request.callback));
171
217
  previousCoords = request.coords;
172
218
  }
173
219
  }
@@ -1 +1 @@
1
- {"version":3,"file":"simpleViewRenderingContext.d.ts","sourceRoot":"","sources":["../../../../src/view/renderingContext/simpleViewRenderingContext.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH;IAMQ,uDAAuD;IACvD,QADW,OAAO,wBAAwB,EAAE,OAAO,CAC5B;IAEvB,gDAAgD;IAChD,OADW,GAAG,CAAC,OAAO,YAAY,EAAE,OAAO,CAAC,CACtB;CA6C7B;;;;;mBAzDY,OAAO,YAAY,EAAE,OAAO;iCANR,2BAA2B"}
1
+ {"version":3,"file":"simpleViewRenderingContext.d.ts","sourceRoot":"","sources":["../../../../src/view/renderingContext/simpleViewRenderingContext.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH;IAMQ,uDAAuD;IACvD,QADW,OAAO,wBAAwB,EAAE,OAAO,CAC5B;IAEvB,gDAAgD;IAChD,OADW,GAAG,CAAC,OAAO,YAAY,EAAE,OAAO,CAAC,CACtB;CAiD7B;;;;;mBA7DY,OAAO,YAAY,EAAE,OAAO;iCANR,2BAA2B"}
@@ -58,7 +58,11 @@ export default class SimpleViewRenderingContext extends ViewRenderingContext {
58
58
  for (const op of mark.prepareRender(this.globalOptions)) {
59
59
  op();
60
60
  }
61
- mark.setViewport(this.coords, options.clipRect);
61
+
62
+ const canvasSize = { width: 100, height: 100 }; // Placeholder, should be replaced with actual canvas size
63
+ const dpr = this.getDevicePixelRatio();
64
+
65
+ mark.setViewport(canvasSize, dpr, this.coords, options.clipRect);
62
66
  mark.render(options)();
63
67
  }
64
68
  }
@@ -28,6 +28,7 @@ export default class ViewRenderingContext {
28
28
  * @param {import("../../types/rendering.js").RenderingOptions} options
29
29
  */
30
30
  renderMark(mark: import("../../marks/mark.js").default, options: import("../../types/rendering.js").RenderingOptions): void;
31
+ getDevicePixelRatio(): number;
31
32
  }
32
33
  export type View = import("../view.js").default;
33
34
  //# sourceMappingURL=viewRenderingContext.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"viewRenderingContext.d.ts","sourceRoot":"","sources":["../../../../src/view/renderingContext/viewRenderingContext.js"],"names":[],"mappings":"AAAA;;GAEG;AACH;IACI;;;OAGG;IACH,2BAFW,OAAO,0BAA0B,EAAE,sBAAsB,EAInE;IADG,yEAAkC;IAGtC;;;;;;OAMG;IACH,eAJW,IAAI,UACJ,OAAO,wBAAwB,EAAE,OAAO,QAKlD;IAED;;;;OAIG;IACH,cAFW,IAAI,QAId;IAED;;;;OAIG;IACH,iBAHW,OAAO,qBAAqB,EAAE,OAAO,WACrC,OAAO,0BAA0B,EAAE,gBAAgB,QAI7D;CACJ;mBAvCY,OAAO,YAAY,EAAE,OAAO"}
1
+ {"version":3,"file":"viewRenderingContext.d.ts","sourceRoot":"","sources":["../../../../src/view/renderingContext/viewRenderingContext.js"],"names":[],"mappings":"AAAA;;GAEG;AACH;IACI;;;OAGG;IACH,2BAFW,OAAO,0BAA0B,EAAE,sBAAsB,EAInE;IADG,yEAAkC;IAGtC;;;;;;OAMG;IACH,eAJW,IAAI,UACJ,OAAO,wBAAwB,EAAE,OAAO,QAKlD;IAED;;;;OAIG;IACH,cAFW,IAAI,QAId;IAED;;;;OAIG;IACH,iBAHW,OAAO,qBAAqB,EAAE,OAAO,WACrC,OAAO,0BAA0B,EAAE,gBAAgB,QAI7D;IAED,8BAEC;CACJ;mBA3CY,OAAO,YAAY,EAAE,OAAO"}
@@ -38,4 +38,8 @@ export default class ViewRenderingContext {
38
38
  renderMark(mark, options) {
39
39
  //
40
40
  }
41
+
42
+ getDevicePixelRatio() {
43
+ return 1;
44
+ }
41
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"unitView.d.ts","sourceRoot":"","sources":["../../../src/view/unitView.js"],"names":[],"mappings":"AA4BA;;;;GAIG;AACH,wBAHU,MAAM,CAAC,OAAO,iBAAiB,EAAE,QAAQ,EAAE,cAAc,kBAAkB,EAAE,OAAO,CAAC,CAc7F;AAEF;IAeI;;;;;;;;OAQG;IACH,kBAPW,OAAO,iBAAiB,EAAE,QAAQ,WAClC,OAAO,yBAAyB,EAAE,OAAO,gBACzC,OAAO,oBAAoB,EAAE,OAAO,cACpC,OAAO,WAAW,EAAE,OAAO,QAC3B,MAAM,YACN,OAAO,WAAW,EAAE,WAAW,EAiCzC;IA5BG,yCAAgB;IAIZ,iDAAiD;IACjD,MADW,OAAO,kBAAkB,EAAE,OAAO,CACnB;IAkHlC,2DAIC;IAgBD;;;;;OAKG;IAEH,iEAsHC;IAED;;;;;OAKG;IACH,4IAEC;IAkBD;;OAEG;IACH,uDAEC;IAED;;;;;;;;;;;;;OAaG;IACH,uEAHW,OAAO,oBAAoB,EAAE,IAAI,iDAqB3C;IAED,uBAQC;IAgBD;;;;OAIG;IACH,8BAJW,MAAM,+DAEJ,OAAO,iBAAiB,EAAE,kBAAkB,CAKxD;;CACJ;iBAragB,WAAW"}
1
+ {"version":3,"file":"unitView.d.ts","sourceRoot":"","sources":["../../../src/view/unitView.js"],"names":[],"mappings":"AA6BA;;;;GAIG;AACH,wBAHU,MAAM,CAAC,OAAO,iBAAiB,EAAE,QAAQ,EAAE,cAAc,kBAAkB,EAAE,OAAO,CAAC,CAc7F;AAEF;IAeI;;;;;;;;OAQG;IACH,kBAPW,OAAO,iBAAiB,EAAE,QAAQ,WAClC,OAAO,yBAAyB,EAAE,OAAO,gBACzC,OAAO,oBAAoB,EAAE,OAAO,cACpC,OAAO,WAAW,EAAE,OAAO,QAC3B,MAAM,YACN,OAAO,WAAW,EAAE,WAAW,EAiCzC;IA5BG,yCAAgB;IAIZ,iDAAiD;IACjD,MADW,OAAO,kBAAkB,EAAE,OAAO,CACnB;IA4JlC,2DAIC;IAgBD;;;;;OAKG;IAEH,iEAsHC;IAED;;;;;OAKG;IACH,4IAEC;IAkBD;;OAEG;IACH,uDAEC;IAED;;;;;;;;;;;;;OAaG;IACH,uEAHW,OAAO,oBAAoB,EAAE,IAAI,iDAqB3C;IAED,uBAQC;IAgBD;;;;OAIG;IACH,8BAJW,MAAM,+DAEJ,OAAO,iBAAiB,EAAE,kBAAkB,CAKxD;;CACJ;iBAhdgB,WAAW"}
@@ -25,6 +25,7 @@ import {
25
25
  updateMultiPointSelection,
26
26
  } from "../selection/selection.js";
27
27
  import { UNIQUE_ID_KEY } from "../data/transforms/identifier.js";
28
+ import { createEventFilterFunction } from "../utils/expression.js";
28
29
 
29
30
  /**
30
31
  *
@@ -108,6 +109,16 @@ export default class UnitView extends View {
108
109
  }
109
110
 
110
111
  const select = asSelectionConfig(param.select);
112
+ // Normalized config has eventConfig in "on"
113
+ const eventConfig =
114
+ /** @type {import("../spec/parameter.js").EventConfig} */ (
115
+ select.on
116
+ );
117
+
118
+ const clearEventConfig =
119
+ /** @type {import("../spec/parameter.js").EventConfig} */ (
120
+ select.clear
121
+ );
111
122
 
112
123
  if (isPointSelectionConfig(select)) {
113
124
  // Handle projection-free point selections
@@ -122,10 +133,17 @@ export default class UnitView extends View {
122
133
  return h?.mark?.unitView === this ? h.datum : null;
123
134
  };
124
135
 
136
+ const eventPredicate = eventConfig.filter
137
+ ? createEventFilterFunction(eventConfig.filter)
138
+ : () => true;
139
+
125
140
  const listener = (
126
141
  /** @type {any} */ _,
127
142
  /** @type {import("../utils/interactionEvent.js").default} */ event
128
143
  ) => {
144
+ if (!eventPredicate(event.proxiedMouseEvent)) {
145
+ return;
146
+ }
129
147
  const datum = getHoveredDatum();
130
148
  const id = datum ? datum[UNIQUE_ID_KEY] : none;
131
149
 
@@ -164,11 +182,36 @@ export default class UnitView extends View {
164
182
  };
165
183
 
166
184
  this.addInteractionEventListener(
167
- ["mouseover", "pointerover"].includes(select.on)
185
+ ["mouseover", "pointerover"].includes(eventConfig.type)
168
186
  ? "mousemove"
169
- : "click",
187
+ : eventConfig.type,
170
188
  listener
171
189
  );
190
+
191
+ if (clearEventConfig) {
192
+ const clearPredicate = clearEventConfig.filter
193
+ ? createEventFilterFunction(clearEventConfig.filter)
194
+ : () => true;
195
+
196
+ const clearListener = (
197
+ /** @type {any} */ _,
198
+ /** @type {import("../utils/interactionEvent.js").default} */ event
199
+ ) => {
200
+ if (!clearPredicate(event.proxiedMouseEvent)) {
201
+ return;
202
+ }
203
+ lastId = none;
204
+ const selection = select.toggle
205
+ ? createMultiPointSelection()
206
+ : createSinglePointSelection(null);
207
+ setter(selection);
208
+ };
209
+
210
+ this.addInteractionEventListener(
211
+ clearEventConfig.type,
212
+ clearListener
213
+ );
214
+ }
172
215
  }
173
216
  }
174
217
  }
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "contributors": [],
9
9
  "license": "MIT",
10
- "version": "0.62.2",
10
+ "version": "0.64.0",
11
11
  "jsdelivr": "dist/bundle/index.js",
12
12
  "unpkg": "dist/bundle/index.js",
13
13
  "browser": "dist/bundle/index.js",
@@ -44,12 +44,12 @@
44
44
  "@gmod/indexedfasta": "^2.1.1",
45
45
  "@gmod/tabix": "^1.6.1",
46
46
  "@gmod/vcf": "^6.0.0",
47
- "@types/d3-array": "^3.2.1",
47
+ "@types/d3-array": "^3.2.2",
48
48
  "@types/d3-dsv": "^3.0.7",
49
49
  "@types/d3-ease": "^3.0.2",
50
50
  "@types/d3-format": "^3.0.4",
51
51
  "@types/d3-interpolate": "^3.0.4",
52
- "@types/d3-scale": "^4.0.8",
52
+ "@types/d3-scale": "^4.0.9",
53
53
  "d3-array": "^3.2.4",
54
54
  "d3-color": "^3.1.0",
55
55
  "d3-ease": "^3.0.1",
@@ -59,13 +59,13 @@
59
59
  "internmap": "^2.0.3",
60
60
  "lit": "^3.3.0",
61
61
  "twgl.js": "^4.19.1",
62
- "vega-expression": "^6.0.0",
63
- "vega-loader": "^5.0.0",
64
- "vega-scale": "^8.0.0",
65
- "vega-util": "^2.0.0"
62
+ "vega-expression": "^6.1.0",
63
+ "vega-loader": "^5.1.0",
64
+ "vega-scale": "^8.1.0",
65
+ "vega-util": "^2.1.0"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@types/long": "^4.0.1"
69
69
  },
70
- "gitHead": "74d02e7425143681fec5fda16d539d94bfdfe034"
70
+ "gitHead": "a1edb39d7a50b3a2b05c9ee0d860170065fd5ad6"
71
71
  }