@genome-spy/core 0.44.0 → 0.46.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 (129) hide show
  1. package/dist/bundle/{index-lmJu1tfP.js → index-BtRKzKhM.js} +6 -6
  2. package/dist/bundle/{index--cKb-dKG.js → index-BwFvhduA.js} +5 -5
  3. package/dist/bundle/{index-1QVesMzU.js → index-C8lYPtq_.js} +1 -1
  4. package/dist/bundle/{index-Pv3tKJ1W.js → index-CkI3Kd2P.js} +3 -3
  5. package/dist/bundle/{index-Y-LdHNIz.js → index-CmBp-spD.js} +1 -1
  6. package/dist/bundle/{index-z4Cs62EO.js → index-Dixm7K89.js} +4 -4
  7. package/dist/bundle/{index-noY1e-G6.js → index-Sk-Wtwdn.js} +5 -5
  8. package/dist/bundle/{index-UyrC0vvF.js → index-Z7JiNsFI.js} +4 -4
  9. package/dist/bundle/{index-LD6yPc3X.js → index-mihmTLo-.js} +1 -1
  10. package/dist/bundle/index.es.js +6339 -5615
  11. package/dist/bundle/index.js +222 -150
  12. package/dist/bundle/{long-Veu0zKh9.js → long-CYrAUkxh.js} +2 -2
  13. package/dist/bundle/{remoteFile-Ur-gRKsH.js → remoteFile-1_eCK3VV.js} +1 -1
  14. package/dist/schema.json +481 -90
  15. package/dist/src/data/collector.d.ts +1 -0
  16. package/dist/src/data/collector.d.ts.map +1 -1
  17. package/dist/src/data/collector.js +23 -5
  18. package/dist/src/data/flow.test.js +4 -0
  19. package/dist/src/data/flowNode.d.ts +10 -8
  20. package/dist/src/data/flowNode.d.ts.map +1 -1
  21. package/dist/src/data/flowNode.js +25 -15
  22. package/dist/src/data/flowOptimizer.test.js +12 -3
  23. package/dist/src/data/sources/dataSource.d.ts +17 -0
  24. package/dist/src/data/sources/dataSource.d.ts.map +1 -1
  25. package/dist/src/data/sources/dataSource.js +34 -0
  26. package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
  27. package/dist/src/data/sources/dataUtils.js +3 -1
  28. package/dist/src/data/sources/inlineSource.js +1 -1
  29. package/dist/src/data/sources/lazy/bigBedSource.d.ts +1 -1
  30. package/dist/src/data/sources/lazy/bigBedSource.d.ts.map +1 -1
  31. package/dist/src/data/sources/lazy/bigBedSource.js +58 -20
  32. package/dist/src/data/sources/lazy/bigWigSource.d.ts +0 -1
  33. package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
  34. package/dist/src/data/sources/lazy/bigWigSource.js +58 -19
  35. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts +0 -8
  36. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
  37. package/dist/src/data/sources/lazy/singleAxisLazySource.js +1 -15
  38. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts +6 -2
  39. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
  40. package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +41 -23
  41. package/dist/src/data/sources/namedSource.js +1 -1
  42. package/dist/src/data/sources/sequenceSource.d.ts.map +1 -1
  43. package/dist/src/data/sources/sequenceSource.js +16 -6
  44. package/dist/src/data/sources/sequenceSource.test.js +23 -5
  45. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  46. package/dist/src/data/sources/urlSource.js +24 -7
  47. package/dist/src/data/transforms/aggregate.d.ts.map +1 -1
  48. package/dist/src/data/transforms/aggregate.js +5 -2
  49. package/dist/src/data/transforms/filter.d.ts +2 -2
  50. package/dist/src/data/transforms/filter.d.ts.map +1 -1
  51. package/dist/src/data/transforms/filter.js +3 -6
  52. package/dist/src/data/transforms/filter.test.js +6 -0
  53. package/dist/src/data/transforms/formula.d.ts +2 -2
  54. package/dist/src/data/transforms/formula.d.ts.map +1 -1
  55. package/dist/src/data/transforms/formula.js +3 -3
  56. package/dist/src/data/transforms/formula.test.js +7 -1
  57. package/dist/src/encoder/encoder.d.ts +2 -4
  58. package/dist/src/encoder/encoder.d.ts.map +1 -1
  59. package/dist/src/encoder/encoder.js +8 -8
  60. package/dist/src/encoder/encoder.test.js +3 -0
  61. package/dist/src/genomeSpy.d.ts +12 -7
  62. package/dist/src/genomeSpy.d.ts.map +1 -1
  63. package/dist/src/genomeSpy.js +132 -139
  64. package/dist/src/gl/glslScaleGenerator.js +1 -1
  65. package/dist/src/index.d.ts +1 -1
  66. package/dist/src/index.d.ts.map +1 -1
  67. package/dist/src/index.js +1 -1
  68. package/dist/src/marks/mark.d.ts +8 -5
  69. package/dist/src/marks/mark.d.ts.map +1 -1
  70. package/dist/src/marks/mark.js +67 -12
  71. package/dist/src/marks/point.common.glsl.js +1 -1
  72. package/dist/src/marks/point.d.ts +1 -4
  73. package/dist/src/marks/point.d.ts.map +1 -1
  74. package/dist/src/marks/point.js +31 -23
  75. package/dist/src/marks/point.vertex.glsl.js +1 -1
  76. package/dist/src/marks/text.d.ts.map +1 -1
  77. package/dist/src/marks/text.js +15 -7
  78. package/dist/src/spec/data.d.ts +11 -10
  79. package/dist/src/spec/mark.d.ts +11 -21
  80. package/dist/src/spec/parameter.d.ts +11 -7
  81. package/dist/src/spec/root.d.ts +0 -8
  82. package/dist/src/spec/title.d.ts +5 -4
  83. package/dist/src/spec/view.d.ts +64 -6
  84. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  85. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  86. package/dist/src/styles/genome-spy.css.js +56 -5
  87. package/dist/src/styles/genome-spy.scss +68 -10
  88. package/dist/src/styles/update.sh +6 -0
  89. package/dist/src/tooltip/dataTooltipHandler.js +1 -1
  90. package/dist/src/tooltip/refseqGeneTooltipHandler.js +1 -1
  91. package/dist/src/tooltip/tooltipHandler.d.ts +1 -1
  92. package/dist/src/tooltip/tooltipHandler.d.ts.map +1 -1
  93. package/dist/src/tooltip/tooltipHandler.ts +1 -1
  94. package/dist/src/types/embedApi.d.ts +6 -0
  95. package/dist/src/types/viewContext.d.ts +11 -5
  96. package/dist/src/utils/debounce.d.ts +2 -2
  97. package/dist/src/utils/debounce.d.ts.map +1 -1
  98. package/dist/src/utils/debounce.js +5 -2
  99. package/dist/src/utils/expression.d.ts +2 -2
  100. package/dist/src/utils/expression.d.ts.map +1 -1
  101. package/dist/src/utils/expression.js +2 -2
  102. package/dist/src/utils/formatObject.d.ts +2 -2
  103. package/dist/src/utils/formatObject.d.ts.map +1 -1
  104. package/dist/src/utils/formatObject.js +2 -2
  105. package/dist/src/utils/inputBinding.d.ts +5 -0
  106. package/dist/src/utils/inputBinding.d.ts.map +1 -0
  107. package/dist/src/utils/inputBinding.js +115 -0
  108. package/dist/src/utils/ui/tooltip.js +1 -1
  109. package/dist/src/view/paramMediator.d.ts +109 -0
  110. package/dist/src/view/paramMediator.d.ts.map +1 -0
  111. package/dist/src/view/paramMediator.js +338 -0
  112. package/dist/src/view/paramMediator.test.js +224 -0
  113. package/dist/src/view/scaleResolution.d.ts.map +1 -1
  114. package/dist/src/view/scaleResolution.js +11 -6
  115. package/dist/src/view/view.d.ts +4 -1
  116. package/dist/src/view/view.d.ts.map +1 -1
  117. package/dist/src/view/view.js +19 -5
  118. package/dist/src/view/viewFactory.d.ts +4 -1
  119. package/dist/src/view/viewFactory.d.ts.map +1 -1
  120. package/dist/src/view/viewFactory.js +86 -13
  121. package/dist/src/view/viewUtils.d.ts +7 -8
  122. package/dist/src/view/viewUtils.d.ts.map +1 -1
  123. package/dist/src/view/viewUtils.js +30 -34
  124. package/package.json +16 -17
  125. package/dist/src/paramBroker.d.ts +0 -46
  126. package/dist/src/paramBroker.d.ts.map +0 -1
  127. package/dist/src/paramBroker.js +0 -118
  128. /package/dist/bundle/{__vite-browser-external-ENoMJThg.js → __vite-browser-external-C--ziKoh.js} +0 -0
  129. /package/dist/bundle/{_commonjsHelpers-QtkX90xp.js → _commonjsHelpers-BIiJCwQW.js} +0 -0
@@ -0,0 +1,338 @@
1
+ import { isString } from "vega-util";
2
+ import createFunction from "../utils/expression.js";
3
+
4
+ /**
5
+ * A class that manages parameters and expressions.
6
+ * Supports nesting and scoped parameters.
7
+ *
8
+ * @typedef {import("../utils/expression.js").ExpressionFunction & { addListener: (listener: () => void) => void, invalidate: () => void, identifier: () => string}} ExprRefFunction
9
+ */
10
+ export default class ParamMediator {
11
+ /**
12
+ * @typedef {import("../spec/parameter.js").VariableParameter} VariableParameter
13
+ * @typedef {(value: any) => void} ParameterSetter
14
+ */
15
+
16
+ /** @type {Map<string, any>} */
17
+ #paramValues;
18
+
19
+ /**
20
+ * @type {Map<string, Set<() => void>>}
21
+ * @protected
22
+ */
23
+ paramListeners;
24
+
25
+ /** @type {Map<string, (value: any) => void>} */
26
+ #allocatedSetters = new Map();
27
+
28
+ /** @type {Map<string, ExprRefFunction>} */
29
+ #expressions = new Map();
30
+
31
+ /** @type {Map<string, VariableParameter>} */
32
+ #paramConfigs = new Map();
33
+
34
+ /** @type {() => ParamMediator} */
35
+ #parentFinder;
36
+
37
+ /**
38
+ * @param {() => ParamMediator} [parentFinder]
39
+ * An optional function that returns the parent mediator.
40
+ * N.B. The function must always return the same mediator for the same parent,
41
+ * i.e., the changing the structure of the hierarchy is NOT supported.
42
+ */
43
+ constructor(parentFinder) {
44
+ this.#parentFinder = parentFinder ?? (() => undefined);
45
+
46
+ this.#paramValues = new Map();
47
+ this.paramListeners = new Map();
48
+ }
49
+
50
+ /**
51
+ * @param {VariableParameter} param
52
+ * @returns {ParameterSetter}
53
+ */
54
+ registerParam(param) {
55
+ if ("value" in param && "expr" in param) {
56
+ throw new Error(
57
+ "Parameter must not have both value and expr: " + param.name
58
+ );
59
+ }
60
+
61
+ /** @type {ParameterSetter} */
62
+ let setter;
63
+
64
+ if ("value" in param) {
65
+ setter = this.allocateSetter(param.name, param.value);
66
+ } else if ("expr" in param) {
67
+ const expr = this.createExpression(param.expr);
68
+ // TODO: getSetter(param) should return a setter that throws if
69
+ // modifying the value is attempted.
70
+ const realSetter = this.allocateSetter(param.name, expr(null));
71
+ expr.addListener(() => realSetter(expr(null)));
72
+ // NOP
73
+ setter = (_) => undefined;
74
+ }
75
+
76
+ this.#paramConfigs.set(param.name, param);
77
+
78
+ return setter;
79
+ }
80
+
81
+ /**
82
+ *
83
+ * @param {string} paramName
84
+ * @param {T} initialValue
85
+ * @param {boolean} [passive] If true, the setter will not notify listeners when the value changes.
86
+ * @returns {(value: T) => void}
87
+ * @template T
88
+ */
89
+ allocateSetter(paramName, initialValue, passive = false) {
90
+ if (this.#allocatedSetters.has(paramName)) {
91
+ throw new Error(
92
+ "Setter already allocated for parameter: " + paramName
93
+ );
94
+ }
95
+
96
+ /** @type {(value: any) => void} */
97
+ const setter = (value) => {
98
+ const previous = this.#paramValues.get(paramName);
99
+ if (value !== previous) {
100
+ this.#paramValues.set(paramName, value);
101
+
102
+ const listeners = this.paramListeners.get(paramName);
103
+ if (listeners && !passive) {
104
+ for (const listener of listeners) {
105
+ listener();
106
+ }
107
+ }
108
+ }
109
+ };
110
+
111
+ setter(initialValue);
112
+
113
+ this.#allocatedSetters.set(paramName, setter);
114
+
115
+ return setter;
116
+ }
117
+
118
+ /**
119
+ * Gets an existing setter for a parameter. Throws if the setter is not found.
120
+ * @param {string} paramName
121
+ */
122
+ getSetter(paramName) {
123
+ const setter = this.#allocatedSetters.get(paramName);
124
+ if (!setter) {
125
+ throw new Error("Setter not found for parameter: " + paramName);
126
+ }
127
+ return setter;
128
+ }
129
+
130
+ /**
131
+ * Get the value of a parameter from this mediator.
132
+ * @param {string} paramName
133
+ */
134
+ getValue(paramName) {
135
+ return this.#paramValues.get(paramName);
136
+ }
137
+
138
+ /**
139
+ * Get the value of a parameter from this mediator or the ancestors.
140
+ * @param {string} paramName
141
+ */
142
+ findValue(paramName) {
143
+ const mediator = this.findMediatorForParam(paramName);
144
+ return mediator?.getValue(paramName);
145
+ }
146
+
147
+ /**
148
+ * Returns configs for all parameters that have been registered using `registerParam`.
149
+ */
150
+ get paramConfigs() {
151
+ return /** @type {ReadonlyMap<string, VariableParameter>} */ (
152
+ this.#paramConfigs
153
+ );
154
+ }
155
+
156
+ /**
157
+ *
158
+ * @param {string} paramName
159
+ * @returns {ParamMediator}
160
+ * @protected
161
+ */
162
+ findMediatorForParam(paramName) {
163
+ if (this.#paramValues.has(paramName)) {
164
+ return this;
165
+ } else {
166
+ return this.#parentFinder()?.findMediatorForParam(paramName);
167
+ }
168
+ }
169
+
170
+ // TODO: deallocateSetter
171
+
172
+ /**
173
+ * Parse expr and return a function that returns the value of the parameter.
174
+ *
175
+ * @param {string} expr
176
+ */
177
+ createExpression(expr) {
178
+ if (this.#expressions.has(expr)) {
179
+ return this.#expressions.get(expr);
180
+ }
181
+
182
+ const globalObject = {};
183
+
184
+ /** @type {ExprRefFunction} */
185
+ const fn = /** @type {any} */ (createFunction(expr, globalObject));
186
+
187
+ /** @type {Map<string, ParamMediator>} */
188
+ const mediatorsForParams = new Map();
189
+
190
+ for (const param of fn.globals) {
191
+ const mediator = this.findMediatorForParam(param);
192
+ if (!mediator) {
193
+ throw new Error(
194
+ `Unknown variable "${param}" in expression: ${expr}`
195
+ );
196
+ }
197
+
198
+ mediatorsForParams.set(param, mediator);
199
+
200
+ Object.defineProperty(globalObject, param, {
201
+ enumerable: true,
202
+ get() {
203
+ return mediator.getValue(param);
204
+ },
205
+ });
206
+ }
207
+ // TODO: There should be a way to "materialize" the global object when
208
+ // it is used in expressions in transformation batches, i.e., when the same
209
+ // expression is applied to multiple data objects. In that case, the global
210
+ // object remains constant and the Map lookups cause unnecessary overhead.
211
+
212
+ // Keep track of them so that they can be detached later
213
+ const myListeners = new Set();
214
+
215
+ /**
216
+ *
217
+ * @param {() => void} listener
218
+ */
219
+ fn.addListener = (listener) => {
220
+ for (const [param, mediator] of mediatorsForParams) {
221
+ const listeners =
222
+ mediator.paramListeners.get(param) ?? new Set();
223
+ mediator.paramListeners.set(param, listeners);
224
+
225
+ listeners.add(listener);
226
+ myListeners.add(listener);
227
+ }
228
+ };
229
+
230
+ /**
231
+ * Detach listeners. This must be called if the expression is no longer used.
232
+ * TODO: What if the expression is used in multiple places?
233
+ */
234
+ fn.invalidate = () => {
235
+ for (const [param, mediator] of mediatorsForParams) {
236
+ for (const listener of myListeners) {
237
+ mediator.paramListeners.get(param)?.delete(listener);
238
+ }
239
+ }
240
+ };
241
+
242
+ // TODO: This should contain unique identifier for each parameter.
243
+ // As the same parameter name may be used in different branches of the
244
+ // hierarchy, they should be distinguished by a unique identifier, e.g.,
245
+ // a serial number of something similar.
246
+ fn.identifier = () => fn.code;
247
+
248
+ this.#expressions.set(expr, fn);
249
+
250
+ return fn;
251
+ }
252
+
253
+ /**
254
+ * A convenience method for evaluating an expression.
255
+ *
256
+ * @param {string} expr
257
+ */
258
+ evaluateAndGet(expr) {
259
+ const fn = this.createExpression(expr);
260
+ return fn();
261
+ }
262
+ }
263
+
264
+ /**
265
+ * @param {any} x
266
+ * @returns {x is import("../spec/parameter.js").ExprRef}
267
+ */
268
+ export function isExprRef(x) {
269
+ return typeof x == "object" && x != null && "expr" in x && isString(x.expr);
270
+ }
271
+
272
+ /**
273
+ * Removes ExprRef from the type and checks that the value is not an ExprRef.
274
+ * This is designed to be used with `activateExprRefProps`.
275
+ *
276
+ * @param {T | import("../spec/parameter.js").ExprRef} x
277
+ * @template T
278
+ * @returns {T}
279
+ */
280
+ export function withoutExprRef(x) {
281
+ if (isExprRef(x)) {
282
+ throw new Error(
283
+ `ExprRef ${JSON.stringify(
284
+ x
285
+ )} not allowed here. Expected a scalar value.`
286
+ );
287
+ }
288
+ return /** @type {T} */ (x);
289
+ }
290
+
291
+ /**
292
+ * Takes a record of properties that may have ExprRefs as values. Converts the
293
+ * ExprRefs to getters and setups a listener that is called when any of the
294
+ * expressions (upstream parameters) change.
295
+ *
296
+ * @param {ParamMediator} paramMediator
297
+ * @param {T} props The properties object
298
+ * @param {(props: (keyof T)[]) => void} [listener] Listener to be called when any of the expressions change
299
+ * @returns T
300
+ * @template {Record<string, any | import("../spec/parameter.js").ExprRef>} T
301
+ */
302
+ export function activateExprRefProps(paramMediator, props, listener) {
303
+ /** @type {Record<string, any | import("../spec/parameter.js").ExprRef>} */
304
+ const activatedProps = { ...props };
305
+
306
+ /** @type {(keyof T)[]} */
307
+ const alteredProps = [];
308
+
309
+ const batchPropertyChange = (/** @type {keyof T} */ prop) => {
310
+ alteredProps.push(prop);
311
+ if (alteredProps.length === 1) {
312
+ queueMicrotask(() => {
313
+ listener(alteredProps.slice());
314
+ alteredProps.length = 0;
315
+ });
316
+ }
317
+ };
318
+
319
+ for (const [key, value] of Object.entries(props)) {
320
+ if (isExprRef(value)) {
321
+ const fn = paramMediator.createExpression(value.expr);
322
+ if (listener) {
323
+ fn.addListener(() => batchPropertyChange(key));
324
+ }
325
+
326
+ Object.defineProperty(activatedProps, key, {
327
+ enumerable: true,
328
+ get() {
329
+ return fn();
330
+ },
331
+ });
332
+ } else {
333
+ activatedProps[key] = value;
334
+ }
335
+ }
336
+
337
+ return /** @type {T} */ (activatedProps);
338
+ }
@@ -0,0 +1,224 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import ParamMediator, { activateExprRefProps } from "./paramMediator.js";
3
+
4
+ describe("Single-level ParamMediator", () => {
5
+ test("Trivial case", () => {
6
+ const pm = new ParamMediator();
7
+ pm.registerParam({ name: "foo", value: 42 });
8
+ expect(pm.getValue("foo")).toBe(42);
9
+ });
10
+
11
+ test("Setter", () => {
12
+ const pm = new ParamMediator();
13
+ const setter = pm.allocateSetter("foo", 42);
14
+ expect(pm.getValue("foo")).toBe(42);
15
+
16
+ setter(43);
17
+ expect(pm.getValue("foo")).toBe(43);
18
+ });
19
+
20
+ test("Expressions have access to parameters", () => {
21
+ const pm = new ParamMediator();
22
+ pm.registerParam({ name: "foo", value: 42 });
23
+ const expr = pm.createExpression("foo + 1");
24
+ expect(expr()).toBe(43);
25
+ });
26
+
27
+ test("Throws on an unknown parameter", () => {
28
+ const pm = new ParamMediator();
29
+ expect(() => pm.createExpression("foo")).toThrow();
30
+ });
31
+
32
+ test("Listener on an expression gets called (only) when a parameter changes", () => {
33
+ const pm = new ParamMediator();
34
+ const setter = pm.allocateSetter("foo", 42);
35
+ const expr = pm.createExpression("foo + 1");
36
+
37
+ let result;
38
+ let calls = 0;
39
+
40
+ expr.addListener(() => {
41
+ result = expr();
42
+ calls++;
43
+ });
44
+
45
+ setter(50);
46
+ expect(result).toBe(51);
47
+ expect(calls).toBe(1);
48
+
49
+ setter(60);
50
+ expect(result).toBe(61);
51
+ expect(calls).toBe(2);
52
+
53
+ setter(60);
54
+ expect(result).toBe(61);
55
+ expect(calls).toBe(2);
56
+ });
57
+
58
+ test("Passive parameter does not trigger listeners", () => {
59
+ const pm = new ParamMediator();
60
+ const setter = pm.allocateSetter("foo", 42, true);
61
+ const expr = pm.createExpression("foo");
62
+
63
+ let result = expr();
64
+
65
+ expr.addListener(() => (result = expr()));
66
+
67
+ setter(50);
68
+ expect(result).toBe(42);
69
+ });
70
+
71
+ test("Expression invalidation", () => {
72
+ const pm = new ParamMediator();
73
+ const setter = pm.allocateSetter("foo", 42);
74
+ const expr = pm.createExpression("foo + 1");
75
+
76
+ let result = expr();
77
+ expect(result).toBe(43);
78
+
79
+ expr.addListener(() => (result = expr()));
80
+
81
+ setter(50);
82
+ expect(result).toBe(51);
83
+
84
+ expr.invalidate();
85
+ // Listeners should be invalidated now: the result must remain the same.
86
+ setter(60);
87
+ expect(result).toBe(51);
88
+ });
89
+
90
+ test("Expression parameter handles dependencies", () => {
91
+ const pm = new ParamMediator();
92
+ const setter = pm.registerParam({ name: "foo", value: 42 });
93
+ pm.registerParam({ name: "bar", expr: "foo + 1" });
94
+ pm.registerParam({ name: "baz", expr: "bar + 2" });
95
+
96
+ const expr = pm.createExpression("baz");
97
+
98
+ let result = expr();
99
+ expect(result).toBe(45);
100
+
101
+ expr.addListener(() => (result = expr()));
102
+
103
+ setter(52);
104
+ expect(result).toBe(55);
105
+ });
106
+
107
+ test("Throws if both value and expr are provided", () => {
108
+ const pm = new ParamMediator();
109
+ expect(() =>
110
+ pm.registerParam({ name: "foo", value: 42, expr: "bar" })
111
+ ).toThrow();
112
+ });
113
+ });
114
+
115
+ describe("Nested ParamMediators", () => {
116
+ test("Value in parent", () => {
117
+ const parent = new ParamMediator();
118
+ const child = new ParamMediator(() => parent);
119
+
120
+ parent.registerParam({ name: "foo", value: 42 });
121
+ expect(parent.findValue("foo")).toBe(42);
122
+ expect(child.findValue("foo")).toBe(42);
123
+ });
124
+
125
+ test("Value in child", () => {
126
+ const parent = new ParamMediator();
127
+ const child = new ParamMediator(() => parent);
128
+
129
+ child.registerParam({ name: "foo", value: 42 });
130
+ expect(parent.findValue("foo")).toBeUndefined();
131
+ expect(child.findValue("foo")).toBe(42);
132
+ });
133
+
134
+ test("Child overrides parent", () => {
135
+ const parent = new ParamMediator();
136
+ const child = new ParamMediator(() => parent);
137
+
138
+ parent.registerParam({ name: "foo", value: 1 });
139
+ child.registerParam({ name: "foo", value: 2 });
140
+
141
+ expect(parent.findValue("foo")).toBe(1);
142
+ expect(child.findValue("foo")).toBe(2);
143
+ });
144
+
145
+ test("Expression", () => {
146
+ const parent = new ParamMediator();
147
+ const child = new ParamMediator(() => parent);
148
+
149
+ parent.registerParam({ name: "foo", value: 1 });
150
+ child.registerParam({ name: "bar", value: 2 });
151
+
152
+ const expr = child.createExpression("foo + bar");
153
+ expect(expr()).toBe(3);
154
+ });
155
+
156
+ test("Listener on an expression", () => {
157
+ const parent = new ParamMediator();
158
+ const child = new ParamMediator(() => parent);
159
+
160
+ const parentSetter = parent.allocateSetter("foo", 1);
161
+ const childSetter = parent.allocateSetter("bar", 2);
162
+
163
+ const expr = child.createExpression("foo + bar");
164
+
165
+ let result = expr();
166
+ expr.addListener(() => (result = expr()));
167
+
168
+ expect(result).toBe(3);
169
+
170
+ parentSetter(10);
171
+ expect(result).toBe(12);
172
+
173
+ childSetter(20);
174
+ expect(result).toBe(30);
175
+ });
176
+ });
177
+
178
+ test("activateExprRefProps", async () => {
179
+ const pm = new ParamMediator();
180
+
181
+ const fooSetter = pm.registerParam({ name: "foo", value: 7 });
182
+ const barSetter = pm.registerParam({ name: "bar", value: 11 });
183
+
184
+ /** @type {Record<string, any | import("../spec/parameter.js").ExprRef} */
185
+ const props = {
186
+ a: 42,
187
+ b: { expr: "foo" },
188
+ c: { expr: "bar" },
189
+ };
190
+
191
+ /** @type {string[]} */
192
+ let altered = [];
193
+
194
+ const activatedProps = activateExprRefProps(pm, props, (props) => {
195
+ altered = props;
196
+ });
197
+
198
+ expect(activatedProps).toEqual({
199
+ a: 42,
200
+ b: 7,
201
+ c: 11,
202
+ });
203
+
204
+ fooSetter(8);
205
+
206
+ // Let the scheduled microtask call the listener
207
+ await Promise.resolve();
208
+
209
+ expect(altered).toEqual(["b"]);
210
+
211
+ fooSetter(1);
212
+ barSetter(2);
213
+
214
+ // Let the scheduled microtask call the listener
215
+ await Promise.resolve();
216
+
217
+ expect(altered).toEqual(["b", "c"]);
218
+
219
+ expect(activatedProps).toEqual({
220
+ a: 42,
221
+ b: 1,
222
+ c: 2,
223
+ });
224
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"scaleResolution.d.ts","sourceRoot":"","sources":["../../../src/view/scaleResolution.js"],"names":[],"mappings":"AAy6BA;;;;;;;;;;GAUG;AACH,6CAFW,OAAO,WAAW,EAAE,OAAO,GAAG,OAAO,WAAW,EAAE,OAAO,EAAE,QA4BrE;AAh6BD,0CAA2C;AAC3C,gCAAiC;AACjC,gCAAiC;AACjC,4BAA6B;AAC7B,4BAA6B;AAE7B;;;;GAIG;AACH;;;;;;;GAOG;AACH;IAyCI;;OAEG;IACH,2DASC;IARG,8CAAsB;IACtB,oDAAoD;IACpD,SADW,gBAAgB,EAAE,CACZ;IACjB,+DAA+D;IAC/D,MADW,MAAM,CACD;IAEhB,iEAAiE;IACjE,MADW,MAAM,CACI;IAOzB;;;;;;;OAOG;IACH,4KAEC;IAED;;;OAGG;IACH,+KAEC;IAcD;;;;;;OAMG;IACH,kHA0BC;IAkLD;;;;OAIG;IACH,qEAMC;IAED;;;;OAIG;IACH,+DAQC;IAED;;OAEG;IACH,oBAyCC;IAED;;OAEG;IACH;eA3WkC,OAAO,kBAAkB,EAAE,KAAK;MAkZjE;IAED,mBAEC;IAED;;OAEG;IACH,oBAFa,mFAA6B,CAOzC;IAED;;;;OAIG;IACH,oBAKC;IAED;;OAEG;IACH,sBAGC;IAUD;;;;;;;OAOG;IACH,kBALW,MAAM,eACN,MAAM,OACN,MAAM,GACJ,OAAO,CAwEnB;IAED;;;;;;OAMG;IACH,eAJW,mFAA6B,aAC7B,OAAO,GAAG,MAAM,iBAgD1B;IAED;;;;OAIG;IACH,qBAcC;IAED;;;;;OAKG;IACH,uBAOC;IA8DD;;;OAGG;IACH,aAFa,OAAO,qBAAqB,EAAE,OAAO,CAajD;IAID;;;;;OAKG;IACH,uBAFW,MAAM,yDAUhB;IAED;;OAEG;IACH,iBAFW,MAAM,yDAKhB;IAED;;;OAGG;IACH,6EAFa,MAAM,CAQlB;IAED;;;OAGG;IACH,mHAFa,MAAM,EAAE,CAOpB;;CA6BJ;wIA5xBY;IAAC,IAAI,EAAE,OAAO,eAAe,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,CAAC,CAAA;CAAC"}
1
+ {"version":3,"file":"scaleResolution.d.ts","sourceRoot":"","sources":["../../../src/view/scaleResolution.js"],"names":[],"mappings":"AA86BA;;;;;;;;;;GAUG;AACH,6CAFW,OAAO,WAAW,EAAE,OAAO,GAAG,OAAO,WAAW,EAAE,OAAO,EAAE,QA4BrE;AAr6BD,0CAA2C;AAC3C,gCAAiC;AACjC,gCAAiC;AACjC,4BAA6B;AAC7B,4BAA6B;AAE7B;;;;GAIG;AACH;;;;;;;GAOG;AACH;IAyCI;;OAEG;IACH,2DASC;IARG,8CAAsB;IACtB,oDAAoD;IACpD,SADW,gBAAgB,EAAE,CACZ;IACjB,+DAA+D;IAC/D,MADW,MAAM,CACD;IAEhB,iEAAiE;IACjE,MADW,MAAM,CACI;IAWzB;;;;;;;OAOG;IACH,4KAEC;IAED;;;OAGG;IACH,+KAEC;IAcD;;;;;;OAMG;IACH,kHA0BC;IAmLD;;;;OAIG;IACH,qEAMC;IAED;;;;OAIG;IACH,+DAQC;IAED;;OAEG;IACH,oBAyCC;IAED;;OAEG;IACH;eAhXkC,OAAO,kBAAkB,EAAE,KAAK;MAuZjE;IAED,mBAEC;IAED;;OAEG;IACH,oBAFa,mFAA6B,CAOzC;IAED;;;;OAIG;IACH,oBAKC;IAED;;OAEG;IACH,sBAGC;IAUD;;;;;;;OAOG;IACH,kBALW,MAAM,eACN,MAAM,OACN,MAAM,GACJ,OAAO,CAwEnB;IAED;;;;;;OAMG;IACH,eAJW,mFAA6B,aAC7B,OAAO,GAAG,MAAM,iBAgD1B;IAED;;;;OAIG;IACH,qBAcC;IAED;;;;;OAKG;IACH,uBAOC;IA8DD;;;OAGG;IACH,aAFa,OAAO,qBAAqB,EAAE,OAAO,CAajD;IAID;;;;;OAKG;IACH,uBAFW,MAAM,yDAUhB;IAED;;OAEG;IACH,iBAFW,MAAM,yDAKhB;IAED;;;OAGG;IACH,6EAFa,MAAM,CAQlB;IAED;;;OAGG;IACH,mHAFa,MAAM,EAAE,CAOpB;;CA6BJ;wIAjyBY;IAAC,IAAI,EAAE,OAAO,eAAe,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,CAAC,CAAA;CAAC"}
@@ -36,7 +36,7 @@ import { NominalDomain } from "../utils/domainArray.js";
36
36
  import { easeCubicInOut } from "d3-ease";
37
37
  import { asArray, shallowArrayEquals } from "../utils/arrayUtils.js";
38
38
  import eerp from "../utils/eerp.js";
39
- import { isExprRef } from "../marks/mark.js";
39
+ import { isExprRef } from "./paramMediator.js";
40
40
 
41
41
  // Register scaleLocus to Vega-Scale.
42
42
  // Loci are discrete but the scale's domain can be adjusted in a continuous manner.
@@ -100,7 +100,7 @@ export default class ScaleResolution {
100
100
  * Keeps track of the expression references in the range. If range is modified,
101
101
  * new expressions are created and the old ones must be invalidated.
102
102
  *
103
- * @type {Set<import("../paramBroker.js").ExprRefFunction>}
103
+ * @type {Set<import("./paramMediator.js").ExprRefFunction>}
104
104
  */
105
105
  #rangeExprRefListeners = new Set();
106
106
 
@@ -118,8 +118,12 @@ export default class ScaleResolution {
118
118
  this.name = undefined;
119
119
  }
120
120
 
121
+ get #firstMemberView() {
122
+ return this.members[0].view;
123
+ }
124
+
121
125
  get #viewContext() {
122
- return this.members[0].view.context;
126
+ return this.#firstMemberView.context;
123
127
  }
124
128
 
125
129
  /**
@@ -338,9 +342,10 @@ export default class ScaleResolution {
338
342
 
339
343
  expressions = range.map((elem) => {
340
344
  if (isExprRef(elem)) {
341
- const fn = this.#viewContext.paramBroker.createExpression(
342
- elem.expr
343
- );
345
+ const fn =
346
+ this.#firstMemberView.paramMediator.createExpression(
347
+ elem.expr
348
+ );
344
349
  fn.addListener(evaluateAndSet);
345
350
  this.#rangeExprRefListeners.add(fn);
346
351
  return () => fn(null);
@@ -84,6 +84,8 @@ export default class View {
84
84
  * @type {Record<import("../spec/channel.js").PrimaryPositionalChannel, boolean>}
85
85
  */
86
86
  needsAxes: Record<import("../spec/channel.js").PrimaryPositionalChannel, boolean>;
87
+ /** @type {ParamMediator} */
88
+ paramMediator: ParamMediator;
87
89
  getPadding(): Padding;
88
90
  /**
89
91
  * Returns a padding that indicates how much axes and titles extend over the plot area.
@@ -267,7 +269,7 @@ export default class View {
267
269
  * Returns `true` if this view and its children supports picking.
268
270
  */
269
271
  isPickingSupported(): boolean;
270
- getTitleText(): string;
272
+ getTitleText(): any;
271
273
  /**
272
274
  * @param {any} key string
273
275
  * @param {function(key?):T} callable A function that produces a value to be cached
@@ -321,6 +323,7 @@ export type ViewOptions = {
321
323
  */
322
324
  contributesToScaleDomain?: boolean;
323
325
  };
326
+ import ParamMediator from "./paramMediator.js";
324
327
  import Padding from "./layout/padding.js";
325
328
  import { FlexDimensions } from "./layout/flexLayout.js";
326
329
  //# sourceMappingURL=view.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../../src/view/view.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../../src/view/view.js"],"names":[],"mappings":"AAyBA,oBAAoB;AACpB,sCAAuC;AACvC,0BAA0B;AAC1B,sCAAuC;AAKvC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH;IAsBI;;;;;;;;;OASG;IACH,kBARW,OAAO,iBAAiB,EAAE,QAAQ,WAClC,OAAO,yBAAyB,EAAE,OAAO,gBACzC,OAAO,oBAAoB,EAAE,OAAO,cACpC,OAAO,WAAW,EAAE,OAAO,QAC3B,MAAM,YACN,WAAW,EAmDrB;IAtED;;OAEG;IACH,wBAFmB,MAAM,KAAE,MAAM,CAEQ;IAEzC;;;;OAIG;IACH,QAFU,OAAO,uBAAuB,EAAE,OAAO,CAE1C;IAiBH,mDAAsB;IACtB,mDAAgC;IAChC,iBAA4B;IAC5B,aAA6B;IAC7B,yCAAgB;IAEhB;QACI;;;WAGG;eADO,QAAQ,OAAO,OAAO,oBAAoB,EAAE,gBAAgB,EAAE,OAAO,sBAAsB,EAAE,OAAO,CAAC,CAAC;QAGhH;;;WAGG;cADO,QAAQ,OAAO,OAAO,oBAAoB,EAAE,wBAAwB,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAC,CAAC;MAG1H;IAID;;;;kCA/DE,OAAO;;;;kCAEP,OAAO;MAiER;IAED;;;OAGG;IACH,WAFU,OAAO,OAAO,oBAAoB,EAAE,wBAAwB,EAAE,OAAO,CAAC,CAEzC;IAEvC,4BAA4B;IAC5B,eADW,aAAa,CAGvB;IASL,sBAIC;IAED;;;;OAIG;IACH,eAFa,OAAO,CAInB;IAED;;;;;OAKG;IACH,gBAFa,OAAO,CAMnB;IAED;;;;;OAKG;IACH,WAFa,cAAc,CAW1B;IAED;;OAEG;IACH,mBAFa,cAAc,CAkB1B;IAoED,+BAEC;IAED,2BAEC;IAED;;;;;;;;OAQG;IACH,aAFa,OAAO,CAMnB;IAED;;;;;;;OAOG;IACH,uBAFa,MAAM,CAMlB;IAED,wBAKC;IAkBD;;OAEG;IACH,6BAEC;IAED;;OAEG;IACH,2BAEC;IAED;;;;OAIG;IACH,yBAFW,gBAAgB,QAO1B;IAED;;;;OAIG;IACH,2BAHW,MAAM,kBACG,gBAAgB,KAAE,IAAI,QASzC;IAED;;;;;;;OAOG;IACH,+BALW,OAAO,uBAAuB,EAAE,OAAO,SAEvC,OAAO,8BAA8B,EAAE,OAAO,aAC9C,OAAO,QASjB;IAED;;;;;;;;;;OAUG;IACH,kCAJW,MAAM,YACN,wBAAwB,eACxB,OAAO,QAajB;IAED;;;;;;;OAOG;IACH,eAJW,OAAO,GACL,WAAW,CAmBvB;IAED;;OAEG;IACH,yBAOC;IAED;;OAEG;IACH,6BASC;IAED;;;OAGG;IACH,uBAEC;IA/aQ,yJAIN;IA2bH;;;;;;OAMG;IACH,eAFY,OAAO,oBAAoB,EAAE,QAAQ,CAuBhD;IAED;;;;OAIG;IACH,+BAJW,IAAI,UAEM,MAAM,KAAE,GAAG,CAM/B;IAED;;;;;OAKG;IACH,6BAHW,IAAI,GACF,MAAM,EAAE,CASpB;IAED;;;;;;;;;;;;;;;OAeG;IACH,yBAFY,YAAY,CAIvB;IAED;;OAEG;IACH,4BAFW,OAAO,oBAAoB,EAAE,gBAAgB,0CAWvD;IAED;;OAEG;IACH,2BAFW,OAAO,oBAAoB,EAAE,iBAAiB,yCAWxD;IAED;;;;OAIG;IACH,iCAJW,OAAO,oBAAoB,EAAE,OAAO,GAAG,SAAS,kBAChD,OAAO,iBAAiB,EAAE,gBAAgB,GACxC,OAAO,iBAAiB,EAAE,kBAAkB,CAIxD;IAED;;;;OAIG;IACH,0CAJW,OAAO,oBAAoB,EAAE,OAAO,kBACpC,OAAO,iBAAiB,EAAE,gBAAgB,GACxC,OAAO,iBAAiB,EAAE,kBAAkB,CAQxD;IAED;;;;OAIG;IACH,8BAJW,OAAO,oBAAoB,EAAE,OAAO,kBACpC,OAAO,iBAAiB,EAAE,gBAAgB,GACxC,OAAO,iBAAiB,EAAE,kBAAkB,CAIxD;IAED;;OAEG;IACH,cAFa,MAAM,CAOlB;IAED;;OAEG;IACH,8BAEC;IAED,oBASC;IAED;;;;;;OAMG;IACH,yBANW,GAAG,wCAQb;IAED;;;;OAIG;IACH,8BAHW,MAAM,cACN,MAAM,GAAG,SAAS,GAAG,WAAW,QAiB1C;IAED,4BAEC;IAED;;;;;OAKG;IACH,iCAFW,OAAO,8BAA8B,EAAE,OAAO,QAIxD;;CACJ;AAoEM,iCAHI,GAAG,0CAGkC;0BA5tBnC,8BAAsB,IAAI;qCAG5B,IAAI,KACF,WAAW;sBAEX,eAAe,GAAG;IAC9B,SAAgB,CAAC,SAAW,IAAI,KAAE,IAAI,CAAC;IACvC,cAAqB,CAAC,SAAW,IAAI,KAAE,IAAI,CAAC;IAC5C,aAAoB,CAAC,SAAW,IAAI,KAAE,IAAI,CAAA;CAAC;;;;;UAIlC,OAAO,iBAAiB,EAAE,kBAAkB;;;;cAC5C,GAAG;;gDAGF,OAAO,uBAAuB,EAAE,OAAO,SAEvC,OAAO,8BAA8B,EAAE,OAAO;;;;;+BAG/C,OAAO;;;;+BAEP,OAAO;;0BAxCwB,oBAAoB;oBAbzC,qBAAqB;+BADlC,wBAAwB"}