@genome-spy/core 0.63.0 → 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 (41) hide show
  1. package/dist/bundle/index.es.js +3644 -3437
  2. package/dist/bundle/index.js +374 -300
  3. package/dist/schema.json +84 -12
  4. package/dist/src/gl/webGLHelper.d.ts.map +1 -1
  5. package/dist/src/gl/webGLHelper.js +8 -0
  6. package/dist/src/marks/link.fragment.glsl.js +1 -1
  7. package/dist/src/marks/link.vertex.glsl.js +1 -1
  8. package/dist/src/marks/point.fragment.glsl.js +1 -1
  9. package/dist/src/marks/point.vertex.glsl.js +1 -1
  10. package/dist/src/marks/rect.fragment.glsl.js +1 -1
  11. package/dist/src/marks/rect.vertex.glsl.js +1 -1
  12. package/dist/src/marks/rule.fragment.glsl.js +1 -1
  13. package/dist/src/marks/rule.vertex.glsl.js +1 -1
  14. package/dist/src/marks/text.fragment.glsl.js +1 -1
  15. package/dist/src/marks/text.vertex.glsl.js +1 -1
  16. package/dist/src/selection/selection.d.ts +5 -0
  17. package/dist/src/selection/selection.d.ts.map +1 -1
  18. package/dist/src/selection/selection.js +43 -6
  19. package/dist/src/selection/selection.test.d.ts +2 -0
  20. package/dist/src/selection/selection.test.d.ts.map +1 -0
  21. package/dist/src/selection/selection.test.js +14 -0
  22. package/dist/src/spec/parameter.d.ts +28 -2
  23. package/dist/src/styles/{genome-spy.scss → genome-spy.css} +25 -21
  24. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  25. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  26. package/dist/src/styles/genome-spy.css.js +264 -195
  27. package/dist/src/styles/update.sh +14 -4
  28. package/dist/src/utils/expression.d.ts +5 -0
  29. package/dist/src/utils/expression.d.ts.map +1 -1
  30. package/dist/src/utils/expression.js +37 -0
  31. package/dist/src/utils/interactionEvent.d.ts +18 -1
  32. package/dist/src/utils/interactionEvent.d.ts.map +1 -1
  33. package/dist/src/utils/interactionEvent.js +101 -1
  34. package/dist/src/utils/interactionEvent.test.d.ts +2 -0
  35. package/dist/src/utils/interactionEvent.test.d.ts.map +1 -0
  36. package/dist/src/utils/interactionEvent.test.js +35 -0
  37. package/dist/src/view/facetView.d.ts +1 -1
  38. package/dist/src/view/facetView.d.ts.map +1 -1
  39. package/dist/src/view/unitView.d.ts.map +1 -1
  40. package/dist/src/view/unitView.js +45 -2
  41. 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"}
@@ -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.63.0",
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": "61b7ba62f15827e617d5e8321e532e4c3a2efd75"
70
+ "gitHead": "a1edb39d7a50b3a2b05c9ee0d860170065fd5ad6"
71
71
  }