@genome-spy/core 0.67.0 → 0.68.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 (172) hide show
  1. package/dist/bundle/index.es.js +7641 -6313
  2. package/dist/bundle/index.js +115 -134
  3. package/dist/schema.json +534 -132
  4. package/dist/src/data/collector.d.ts +20 -0
  5. package/dist/src/data/collector.d.ts.map +1 -1
  6. package/dist/src/data/collector.js +148 -0
  7. package/dist/src/data/dataFlow.d.ts +6 -0
  8. package/dist/src/data/dataFlow.d.ts.map +1 -1
  9. package/dist/src/data/dataFlow.js +10 -0
  10. package/dist/src/data/flowInit.d.ts.map +1 -1
  11. package/dist/src/data/flowInit.js +2 -3
  12. package/dist/src/data/flowNode.d.ts +8 -0
  13. package/dist/src/data/flowNode.d.ts.map +1 -1
  14. package/dist/src/data/flowNode.js +18 -0
  15. package/dist/src/data/keyIndex.d.ts +18 -0
  16. package/dist/src/data/keyIndex.d.ts.map +1 -0
  17. package/dist/src/data/keyIndex.js +241 -0
  18. package/dist/src/data/keyIndex.test.d.ts +2 -0
  19. package/dist/src/data/keyIndex.test.d.ts.map +1 -0
  20. package/dist/src/data/sources/dataSource.d.ts.map +1 -1
  21. package/dist/src/data/sources/dataSource.js +5 -1
  22. package/dist/src/data/sources/dataSourceFactory.d.ts +14 -12
  23. package/dist/src/data/sources/dataSourceFactory.d.ts.map +1 -1
  24. package/dist/src/data/sources/dataSourceFactory.js +52 -16
  25. package/dist/src/data/sources/lazy/mockLazySource.d.ts +29 -0
  26. package/dist/src/data/sources/lazy/mockLazySource.d.ts.map +1 -0
  27. package/dist/src/data/sources/lazy/mockLazySource.js +44 -0
  28. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts +22 -1
  29. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
  30. package/dist/src/data/sources/lazy/singleAxisLazySource.js +34 -2
  31. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
  32. package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +15 -0
  33. package/dist/src/data/sources/lazy/tabixSource.d.ts.map +1 -1
  34. package/dist/src/data/sources/lazy/tabixSource.js +15 -5
  35. package/dist/src/data/transforms/stack.d.ts.map +1 -1
  36. package/dist/src/data/transforms/stack.js +1 -0
  37. package/dist/src/encoder/accessor.d.ts +43 -0
  38. package/dist/src/encoder/accessor.d.ts.map +1 -1
  39. package/dist/src/encoder/accessor.js +164 -0
  40. package/dist/src/encoder/encoder.d.ts +11 -2
  41. package/dist/src/encoder/encoder.d.ts.map +1 -1
  42. package/dist/src/encoder/encoder.js +24 -4
  43. package/dist/src/encoder/metadataChannels.d.ts +15 -0
  44. package/dist/src/encoder/metadataChannels.d.ts.map +1 -0
  45. package/dist/src/encoder/metadataChannels.js +65 -0
  46. package/dist/src/encoder/metadataChannels.test.d.ts +2 -0
  47. package/dist/src/encoder/metadataChannels.test.d.ts.map +1 -0
  48. package/dist/src/genome/scaleLocus.d.ts.map +1 -1
  49. package/dist/src/genome/scaleLocus.js +14 -1
  50. package/dist/src/genomeSpy/containerUi.d.ts +0 -1
  51. package/dist/src/genomeSpy/containerUi.d.ts.map +1 -1
  52. package/dist/src/genomeSpy/containerUi.js +0 -14
  53. package/dist/src/genomeSpy/loadingIndicatorManager.d.ts +3 -7
  54. package/dist/src/genomeSpy/loadingIndicatorManager.d.ts.map +1 -1
  55. package/dist/src/genomeSpy/loadingIndicatorManager.js +68 -20
  56. package/dist/src/genomeSpy/loadingStatusRegistry.d.ts +52 -0
  57. package/dist/src/genomeSpy/loadingStatusRegistry.d.ts.map +1 -0
  58. package/dist/src/genomeSpy/loadingStatusRegistry.js +86 -0
  59. package/dist/src/genomeSpy/viewContextFactory.d.ts.map +1 -1
  60. package/dist/src/genomeSpy/viewContextFactory.js +0 -1
  61. package/dist/src/genomeSpy/viewDataInit.d.ts.map +1 -1
  62. package/dist/src/genomeSpy/viewDataInit.js +56 -11
  63. package/dist/src/genomeSpy.d.ts +0 -2
  64. package/dist/src/genomeSpy.d.ts.map +1 -1
  65. package/dist/src/genomeSpy.js +46 -26
  66. package/dist/src/marks/mark.d.ts.map +1 -1
  67. package/dist/src/marks/mark.js +18 -11
  68. package/dist/src/marks/markUtils.js +1 -1
  69. package/dist/src/scale/scale.d.ts +6 -1
  70. package/dist/src/scale/scale.d.ts.map +1 -1
  71. package/dist/src/scale/scale.js +83 -23
  72. package/dist/src/scales/axisResolution.d.ts.map +1 -1
  73. package/dist/src/scales/axisResolution.js +10 -0
  74. package/dist/src/scales/{scaleDomainAggregator.d.ts → domainPlanner.d.ts} +6 -3
  75. package/dist/src/scales/domainPlanner.d.ts.map +1 -0
  76. package/dist/src/scales/{scaleDomainAggregator.js → domainPlanner.js} +128 -10
  77. package/dist/src/scales/domainPlanner.test.d.ts +2 -0
  78. package/dist/src/scales/domainPlanner.test.d.ts.map +1 -0
  79. package/dist/src/scales/scaleInteractionController.d.ts +6 -0
  80. package/dist/src/scales/scaleInteractionController.d.ts.map +1 -1
  81. package/dist/src/scales/scaleInteractionController.js +41 -3
  82. package/dist/src/scales/scaleResolution.d.ts +19 -17
  83. package/dist/src/scales/scaleResolution.d.ts.map +1 -1
  84. package/dist/src/scales/scaleResolution.js +181 -70
  85. package/dist/src/scales/scaleResolution.test.d.ts.map +1 -1
  86. package/dist/src/selection/selection.d.ts +21 -0
  87. package/dist/src/selection/selection.d.ts.map +1 -1
  88. package/dist/src/selection/selection.js +82 -0
  89. package/dist/src/spec/channel.d.ts +52 -15
  90. package/dist/src/spec/data.d.ts +4 -0
  91. package/dist/src/spec/parameter.d.ts +16 -11
  92. package/dist/src/spec/testing.d.ts +12 -0
  93. package/dist/src/spec/testing.d.ts.map +1 -0
  94. package/dist/src/spec/testing.js +20 -0
  95. package/dist/src/spec/view.d.ts +45 -10
  96. package/dist/src/styles/genome-spy.css +3 -31
  97. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  98. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  99. package/dist/src/styles/genome-spy.css.js +0 -29
  100. package/dist/src/types/encoder.d.ts +37 -2
  101. package/dist/src/types/rendering.d.ts +4 -3
  102. package/dist/src/types/viewContext.d.ts +0 -14
  103. package/dist/src/utils/throttle.d.ts +4 -1
  104. package/dist/src/utils/throttle.d.ts.map +1 -1
  105. package/dist/src/utils/throttle.js +54 -23
  106. package/dist/src/utils/throttle.test.d.ts +2 -0
  107. package/dist/src/utils/throttle.test.d.ts.map +1 -0
  108. package/dist/src/utils/transition.d.ts +21 -0
  109. package/dist/src/utils/transition.d.ts.map +1 -1
  110. package/dist/src/utils/transition.js +28 -0
  111. package/dist/src/utils/ui/tooltip.d.ts.map +1 -1
  112. package/dist/src/utils/ui/tooltip.js +7 -1
  113. package/dist/src/utils/ui/tooltip.test.d.ts +2 -0
  114. package/dist/src/utils/ui/tooltip.test.d.ts.map +1 -0
  115. package/dist/src/view/axisGridView.d.ts.map +1 -1
  116. package/dist/src/view/axisGridView.js +22 -5
  117. package/dist/src/view/axisView.d.ts.map +1 -1
  118. package/dist/src/view/axisView.js +20 -5
  119. package/dist/src/view/concatView.js +3 -3
  120. package/dist/src/view/containerMutationHelper.js +1 -1
  121. package/dist/src/view/containerView.d.ts +9 -5
  122. package/dist/src/view/containerView.d.ts.map +1 -1
  123. package/dist/src/view/containerView.js +34 -9
  124. package/dist/src/view/dataReadiness.d.ts +46 -0
  125. package/dist/src/view/dataReadiness.d.ts.map +1 -0
  126. package/dist/src/view/dataReadiness.js +267 -0
  127. package/dist/src/view/dataReadiness.test.d.ts +2 -0
  128. package/dist/src/view/dataReadiness.test.d.ts.map +1 -0
  129. package/dist/src/view/facetView.d.ts.map +1 -1
  130. package/dist/src/view/facetView.js +7 -5
  131. package/dist/src/view/flowBuilder.d.ts.map +1 -1
  132. package/dist/src/view/flowBuilder.js +5 -1
  133. package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
  134. package/dist/src/view/gridView/gridChild.js +8 -0
  135. package/dist/src/view/gridView/gridView.d.ts.map +1 -1
  136. package/dist/src/view/gridView/gridView.js +119 -2
  137. package/dist/src/view/gridView/scrollbar.d.ts.map +1 -1
  138. package/dist/src/view/gridView/scrollbar.js +3 -0
  139. package/dist/src/view/gridView/selectionRect.d.ts.map +1 -1
  140. package/dist/src/view/gridView/selectionRect.js +20 -5
  141. package/dist/src/view/gridView/separatorView.d.ts +51 -0
  142. package/dist/src/view/gridView/separatorView.d.ts.map +1 -0
  143. package/dist/src/view/gridView/separatorView.js +275 -0
  144. package/dist/src/view/layerView.js +3 -3
  145. package/dist/src/view/layout/flexLayout.d.ts +0 -30
  146. package/dist/src/view/layout/flexLayout.d.ts.map +1 -1
  147. package/dist/src/view/layout/flexLayout.js +0 -86
  148. package/dist/src/view/paramMediator.d.ts +19 -0
  149. package/dist/src/view/paramMediator.d.ts.map +1 -1
  150. package/dist/src/view/paramMediator.js +86 -19
  151. package/dist/src/view/testUtils.d.ts.map +1 -1
  152. package/dist/src/view/testUtils.js +6 -1
  153. package/dist/src/view/unitView.d.ts +8 -13
  154. package/dist/src/view/unitView.d.ts.map +1 -1
  155. package/dist/src/view/unitView.js +110 -41
  156. package/dist/src/view/view.d.ts +22 -14
  157. package/dist/src/view/view.d.ts.map +1 -1
  158. package/dist/src/view/view.js +93 -9
  159. package/dist/src/view/viewFactory.d.ts.map +1 -1
  160. package/dist/src/view/viewFactory.js +20 -1
  161. package/dist/src/view/viewSelectors.d.ts +148 -0
  162. package/dist/src/view/viewSelectors.d.ts.map +1 -0
  163. package/dist/src/view/viewSelectors.js +773 -0
  164. package/dist/src/view/viewSelectors.test.d.ts +2 -0
  165. package/dist/src/view/viewSelectors.test.d.ts.map +1 -0
  166. package/dist/src/view/viewUtils.d.ts +0 -8
  167. package/dist/src/view/viewUtils.d.ts.map +1 -1
  168. package/dist/src/view/viewUtils.js +1 -21
  169. package/package.json +3 -3
  170. package/dist/src/scales/scaleDomainAggregator.d.ts.map +0 -1
  171. package/dist/src/scales/scaleDomainAggregator.test.d.ts +0 -2
  172. package/dist/src/scales/scaleDomainAggregator.test.d.ts.map +0 -1
@@ -0,0 +1,275 @@
1
+ import UnitView from "../unitView.js";
2
+ import { markViewAsNonAddressable } from "../viewSelectors.js";
3
+
4
+ /**
5
+ * @typedef {"horizontal" | "vertical"} SeparatorDirection
6
+ */
7
+
8
+ const DEFAULT_SEPARATOR_PROPS = Object.freeze({
9
+ size: 1,
10
+ color: "#ccc",
11
+ opacity: 1,
12
+ strokeDash: [4, 4],
13
+ strokeCap: "butt",
14
+ });
15
+
16
+ /**
17
+ * Draws separator rules for a single direction in a grid layout.
18
+ */
19
+ export default class SeparatorView {
20
+ /** @type {SeparatorDirection} */
21
+ #direction;
22
+
23
+ /** @type {boolean} */
24
+ #includePlotMargin;
25
+
26
+ /** @type {UnitView} */
27
+ #view;
28
+
29
+ /** @type {import("../../data/flowNode.js").Datum[]} */
30
+ #data = [];
31
+
32
+ /** @type {number[]} */
33
+ #positions = [];
34
+
35
+ /** @type {{ x: number[]; y: number[] }} */
36
+ #domains = {
37
+ x: [0, 0],
38
+ y: [0, 0],
39
+ };
40
+
41
+ /**
42
+ * @param {{
43
+ * direction: SeparatorDirection,
44
+ * props: import("../../spec/view.js").SeparatorProps,
45
+ * context: import("../../types/viewContext.js").default,
46
+ * layoutParent: import("../containerView.js").default,
47
+ * dataParent: import("../view.js").default,
48
+ * getName: (prefix: string) => string
49
+ * }} options
50
+ */
51
+ constructor({
52
+ direction,
53
+ props,
54
+ context,
55
+ layoutParent,
56
+ dataParent,
57
+ getName,
58
+ }) {
59
+ this.#direction = direction;
60
+ this.#includePlotMargin = props.includePlotMargin ?? true;
61
+ const markProps = { ...props };
62
+ delete markProps.includePlotMargin;
63
+ this.#view = this.#createView(
64
+ markProps,
65
+ context,
66
+ layoutParent,
67
+ dataParent,
68
+ getName
69
+ );
70
+ }
71
+
72
+ /**
73
+ * @returns {UnitView}
74
+ */
75
+ get view() {
76
+ return this.#view;
77
+ }
78
+
79
+ /**
80
+ * @param {import("../layout/flexLayout.js").LocSize[]} flexCoords
81
+ * @param {number} count
82
+ * @param {import("../layout/rectangle.js").default} coords
83
+ * @param {(direction: "row" | "column", index: number) => number} getViewSlot
84
+ * @param {boolean} wrappingFacet
85
+ * @param {import("../layout/padding.js").default} overhang
86
+ */
87
+ update(flexCoords, count, coords, getViewSlot, wrappingFacet, overhang) {
88
+ this.#collectPositions(flexCoords, count, getViewSlot, wrappingFacet);
89
+ this.#updateDirection(coords, overhang);
90
+ }
91
+
92
+ /**
93
+ * @param {import("../renderingContext/viewRenderingContext.js").default} context
94
+ * @param {import("../layout/rectangle.js").default} coords
95
+ * @param {import("../../types/rendering.js").RenderingOptions} options
96
+ */
97
+ render(context, coords, options) {
98
+ this.#view.render(context, coords, options);
99
+ }
100
+
101
+ /**
102
+ * @param {import("../layout/flexLayout.js").LocSize[]} flexCoords
103
+ * @param {number} count
104
+ * @param {(direction: "row" | "column", index: number) => number} getViewSlot
105
+ * @param {boolean} wrappingFacet
106
+ */
107
+ #collectPositions(flexCoords, count, getViewSlot, wrappingFacet) {
108
+ this.#positions.length = 0;
109
+
110
+ if (count < 2) {
111
+ return;
112
+ }
113
+
114
+ const axis = this.#direction === "vertical" ? "column" : "row";
115
+ const spacingOffset = wrappingFacet ? 3 : 2;
116
+
117
+ for (let index = 1; index < count; index++) {
118
+ const viewSlot = getViewSlot(axis, index);
119
+ const spacingSlot = viewSlot - spacingOffset;
120
+ const spacing = flexCoords[spacingSlot];
121
+ const location = spacing ? spacing.location : 0;
122
+ const size = spacing ? spacing.size : 0;
123
+ this.#positions.push(location + size / 2);
124
+ }
125
+ }
126
+
127
+ /**
128
+ * @param {import("../layout/rectangle.js").default} coords
129
+ * @param {import("../layout/padding.js").default} overhang
130
+ */
131
+ #updateDirection(coords, overhang) {
132
+ const xStart = this.#includePlotMargin ? 0 : overhang.left;
133
+ const xEnd = this.#includePlotMargin
134
+ ? coords.width
135
+ : coords.width - overhang.right;
136
+ const yStart = this.#includePlotMargin ? 0 : overhang.bottom;
137
+ const yEnd = this.#includePlotMargin
138
+ ? coords.height
139
+ : coords.height - overhang.top;
140
+
141
+ this.#data.length = this.#positions.length;
142
+
143
+ for (let i = 0; i < this.#positions.length; i++) {
144
+ const pos = this.#positions[i];
145
+ const entry = this.#data[i] ?? {};
146
+
147
+ if (this.#direction === "vertical") {
148
+ entry.x = pos;
149
+ entry.x2 = pos;
150
+ entry.y = yStart;
151
+ entry.y2 = yEnd;
152
+ } else {
153
+ const y = coords.height - pos;
154
+ entry.x = xStart;
155
+ entry.x2 = xEnd;
156
+ entry.y = y;
157
+ entry.y2 = y;
158
+ }
159
+
160
+ this.#data[i] = entry;
161
+ }
162
+
163
+ const dataSource =
164
+ /** @type {import("../../data/sources/inlineSource.js").default} */ (
165
+ this.#view.flowHandle?.dataSource
166
+ );
167
+
168
+ if (!dataSource) {
169
+ return;
170
+ }
171
+
172
+ dataSource.updateDynamicData(this.#data);
173
+
174
+ this.#domains.x[1] = coords.width;
175
+ this.#domains.y[1] = coords.height;
176
+
177
+ const xScale = this.#view.getScaleResolution("x")?.getScale();
178
+ if (xScale) {
179
+ xScale.domain(this.#domains.x);
180
+ }
181
+
182
+ const yScale = this.#view.getScaleResolution("y")?.getScale();
183
+ if (yScale) {
184
+ yScale.domain(this.#domains.y);
185
+ }
186
+ }
187
+
188
+ /**
189
+ * @param {Omit<import("../../spec/view.js").SeparatorProps, "includePlotMargin">} props
190
+ * @param {import("../../types/viewContext.js").default} context
191
+ * @param {import("../containerView.js").default} layoutParent
192
+ * @param {import("../view.js").default} dataParent
193
+ * @param {(prefix: string) => string} getName
194
+ * @returns {UnitView}
195
+ */
196
+ #createView(props, context, layoutParent, dataParent, getName) {
197
+ const spec = createSeparatorSpec(props);
198
+ const name =
199
+ this.#direction === "horizontal"
200
+ ? getName("separatorHorizontal")
201
+ : getName("separatorVertical");
202
+
203
+ const view = new UnitView(
204
+ spec,
205
+ context,
206
+ layoutParent,
207
+ dataParent,
208
+ name,
209
+ {
210
+ blockEncodingInheritance: true,
211
+ }
212
+ );
213
+
214
+ markViewAsNonAddressable(view, { skipSubtree: true });
215
+
216
+ return view;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * @param {import("../../spec/view.js").SeparatorProps | boolean | undefined} separator
222
+ * @returns {import("../../spec/view.js").SeparatorProps | null}
223
+ */
224
+ export function resolveSeparatorProps(separator) {
225
+ if (!separator) {
226
+ return null;
227
+ }
228
+
229
+ const props =
230
+ separator === true
231
+ ? { ...DEFAULT_SEPARATOR_PROPS }
232
+ : { ...DEFAULT_SEPARATOR_PROPS, ...separator };
233
+
234
+ if (props.strokeDash === DEFAULT_SEPARATOR_PROPS.strokeDash) {
235
+ props.strokeDash = DEFAULT_SEPARATOR_PROPS.strokeDash.slice();
236
+ }
237
+
238
+ return props;
239
+ }
240
+
241
+ /**
242
+ * @param {Omit<import("../../spec/view.js").SeparatorProps, "includePlotMargin">} props
243
+ * @returns {import("../../spec/view.js").UnitSpec}
244
+ */
245
+ function createSeparatorSpec(props) {
246
+ return {
247
+ configurableVisibility: false,
248
+ domainInert: true,
249
+ data: { values: [] },
250
+ resolve: {
251
+ scale: { x: "excluded", y: "excluded" },
252
+ axis: { x: "excluded", y: "excluded" },
253
+ },
254
+ mark: {
255
+ ...props,
256
+ type: "rule",
257
+ clip: props.clip ?? false,
258
+ tooltip: null,
259
+ },
260
+ encoding: {
261
+ x: {
262
+ field: "x",
263
+ type: "quantitative",
264
+ scale: { nice: false, zero: false },
265
+ },
266
+ y: {
267
+ field: "y",
268
+ type: "quantitative",
269
+ scale: { nice: false, zero: false },
270
+ },
271
+ x2: { field: "x2" },
272
+ y2: { field: "y2" },
273
+ },
274
+ };
275
+ }
@@ -37,13 +37,13 @@ export default class LayerView extends ContainerView {
37
37
  async initializeChildren() {
38
38
  this.#children = await Promise.all(
39
39
  this.spec.layer.map(
40
- (childSpec, i) =>
40
+ (childSpec) =>
41
41
  /** @type {(Promise<LayerView | import("./unitView.js").default>)} */ (
42
42
  this.context.createOrImportView(
43
43
  childSpec,
44
44
  this,
45
45
  this,
46
- "grid" + i,
46
+ this.getNextAutoName("layer"),
47
47
  (importedSpec) => {
48
48
  if (
49
49
  !isLayerSpec(importedSpec) &&
@@ -123,7 +123,7 @@ export default class LayerView extends ContainerView {
123
123
  view.disposeSubtree();
124
124
  this.#children.splice(index, 1);
125
125
  },
126
- defaultName: (index) => "layer" + index,
126
+ defaultName: () => this.getNextAutoName("layer"),
127
127
  });
128
128
  }
129
129
 
@@ -70,36 +70,6 @@ export function isSizeDef(spec: any): spec is SizeDef;
70
70
  * @returns {SizeDef}
71
71
  */
72
72
  export function parseSizeDef(size: "container" | number | SizeDef | import("../../spec/view.js").Step): SizeDef;
73
- /**
74
- * Interpolates between two LocSizes
75
- *
76
- * @param {LocSize} from
77
- * @param {LocSize} to
78
- * @param {function():number} ratio
79
- * @returns {LocSize}
80
- */
81
- export function interpolateLocSizes(from: LocSize, to: LocSize, ratio: () => number): LocSize;
82
- /**
83
- * Wraps a LocSize and allows scrolling.
84
- *
85
- * @param {LocSize} locSize
86
- * @param {number | function():number} offset
87
- * @returns {LocSize}
88
- */
89
- export function translateLocSize(locSize: LocSize, offset: number | (() => number)): LocSize;
90
- /**
91
- * Wraps a LocSize and allows scaling.
92
- *
93
- * @param {LocSize} locSize
94
- * @param {number | function():number} factor
95
- * @returns {LocSize}
96
- */
97
- export function scaleLocSize(locSize: LocSize, factor: number | (() => number)): LocSize;
98
- /**
99
- * @param {LocSize} locSize
100
- * @param {number} value
101
- */
102
- export function locSizeEncloses(locSize: LocSize, value: number): boolean;
103
73
  export class FlexDimensions {
104
74
  /**
105
75
  *
@@ -1 +1 @@
1
- {"version":3,"file":"flexLayout.d.ts","sourceRoot":"","sources":["../../../../src/view/layout/flexLayout.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wCALW,OAAO,EAAE,iBACT,MAAM,mDACN,WAAW,GACT,OAAO,EAAE,CAqGrB;AAED;;;;;GAKG;AACH,sCAHW,QAAQ,CAAC,OAAO,CAAC,gBACjB,WAAW,UAQrB;AAED;;;GAGG;AACH,sCAHW,QAAQ,CAAC,OAAO,CAAC,GACf,OAAO,CAWnB;AAED;;;GAGG;AACH,oCAFW,OAAO,EAAE,WAInB;AAED;;;GAGG;AACH,sCAFW,OAAO,EAAE;;;EASnB;AA4ED;;;;GAIG;AACH,uCAFW,OAAO,WAIjB;AAWD;;;;GAIG;AACH,gCAHW,GAAC,GACC,IAAI,IAAI,OAAO,CAI3B;AAED;;;;GAIG;AACH,mCAHW,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,oBAAoB,EAAE,IAAI,GAChE,OAAO,CAiBnB;AAID;;;;;;;GAOG;AACH,0CALW,OAAO,MACP,OAAO,SACP,MAAW,MAAM,GACf,OAAO,CA4BnB;AAED;;;;;;GAMG;AACH,0CAJW,OAAO,UACP,MAAM,IAAG,MAAW,MAAM,CAAA,GACxB,OAAO,CAanB;AAED;;;;;;GAMG;AACH,sCAJW,OAAO,UACP,MAAM,IAAG,MAAW,MAAM,CAAA,GACxB,OAAO,CAanB;AAED;;;GAGG;AACH,yCAHW,OAAO,SACP,MAAM,WAIhB;AA/MD;IACI;;;;OAIG;IACH,mBAHW,OAAO,UACP,OAAO,EAQjB;IAJG,gBAAgB;IAChB,wBAAkB;IAClB,gBAAgB;IAChB,yBAAoB;IAGxB;;;;OAIG;IACH,oBAFW,OAAO,cAAc,EAAE,OAAO,kBAIxC;IAED;;;;OAIG;IACH,yBAFW,OAAO,cAAc,EAAE,OAAO,kBAIxC;IAmBD;;;;OAIG;IACH,aAFa,OAAO,CAInB;;CACJ;AAED;;;;GAIG;AACH,2BAFU,OAAO,CAKd;AAEH,iDAGE;;;;;;;;SAlPQ,MAAM;;;;WACN,MAAM;;;;;;cAGN,MAAM;UACN,MAAM;;;;;;;;;;;;;;cAGN,MAAM;;;;;uBACN,MAAM;;;;aAEN,MAAM;;;;cACN,OAAO"}
1
+ {"version":3,"file":"flexLayout.d.ts","sourceRoot":"","sources":["../../../../src/view/layout/flexLayout.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wCALW,OAAO,EAAE,iBACT,MAAM,mDACN,WAAW,GACT,OAAO,EAAE,CAqGrB;AAED;;;;;GAKG;AACH,sCAHW,QAAQ,CAAC,OAAO,CAAC,gBACjB,WAAW,UAQrB;AAED;;;GAGG;AACH,sCAHW,QAAQ,CAAC,OAAO,CAAC,GACf,OAAO,CAWnB;AAED;;;GAGG;AACH,oCAFW,OAAO,EAAE,WAInB;AAED;;;GAGG;AACH,sCAFW,OAAO,EAAE;;;EASnB;AA4ED;;;;GAIG;AACH,uCAFW,OAAO,WAIjB;AAWD;;;;GAIG;AACH,gCAHW,GAAC,GACC,IAAI,IAAI,OAAO,CAI3B;AAED;;;;GAIG;AACH,mCAHW,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,oBAAoB,EAAE,IAAI,GAChE,OAAO,CAiBnB;AAzHD;IACI;;;;OAIG;IACH,mBAHW,OAAO,UACP,OAAO,EAQjB;IAJG,gBAAgB;IAChB,wBAAkB;IAClB,gBAAgB;IAChB,yBAAoB;IAGxB;;;;OAIG;IACH,oBAFW,OAAO,cAAc,EAAE,OAAO,kBAIxC;IAED;;;;OAIG;IACH,yBAFW,OAAO,cAAc,EAAE,OAAO,kBAIxC;IAmBD;;;;OAIG;IACH,aAFa,OAAO,CAInB;;CACJ;AAED;;;;GAIG;AACH,2BAFU,OAAO,CAKd;AAEH,iDAGE;;;;;;;;SAlPQ,MAAM;;;;WACN,MAAM;;;;;;cAGN,MAAM;UACN,MAAM;;;;;;;;;;;;;;cAGN,MAAM;;;;;uBACN,MAAM;;;;aAEN,MAAM;;;;cACN,OAAO"}
@@ -302,89 +302,3 @@ export function parseSizeDef(size) {
302
302
 
303
303
  throw new Error(`Invalid sizeDef: ${size}`);
304
304
  }
305
-
306
- // TODO: Find a better place for the following utilities: ////////////////////////////////////
307
-
308
- /**
309
- * Interpolates between two LocSizes
310
- *
311
- * @param {LocSize} from
312
- * @param {LocSize} to
313
- * @param {function():number} ratio
314
- * @returns {LocSize}
315
- */
316
- export function interpolateLocSizes(from, to, ratio) {
317
- return {
318
- get location() {
319
- const r = ratio();
320
- switch (r) {
321
- case 0:
322
- return from.location;
323
- case 1:
324
- return to.location;
325
- default:
326
- return r * to.location + (1 - r) * from.location;
327
- }
328
- },
329
-
330
- get size() {
331
- const r = ratio();
332
- switch (r) {
333
- case 0:
334
- return from.size;
335
- case 1:
336
- return to.size;
337
- default:
338
- return r * to.size + (1 - r) * from.size;
339
- }
340
- },
341
- };
342
- }
343
-
344
- /**
345
- * Wraps a LocSize and allows scrolling.
346
- *
347
- * @param {LocSize} locSize
348
- * @param {number | function():number} offset
349
- * @returns {LocSize}
350
- */
351
- export function translateLocSize(locSize, offset) {
352
- const fn = isNumber(offset) ? () => offset : offset;
353
- return {
354
- get location() {
355
- return locSize.location + fn();
356
- },
357
-
358
- get size() {
359
- return locSize.size;
360
- },
361
- };
362
- }
363
-
364
- /**
365
- * Wraps a LocSize and allows scaling.
366
- *
367
- * @param {LocSize} locSize
368
- * @param {number | function():number} factor
369
- * @returns {LocSize}
370
- */
371
- export function scaleLocSize(locSize, factor) {
372
- const fn = isNumber(factor) ? () => factor : factor;
373
- return {
374
- get location() {
375
- return locSize.location * fn();
376
- },
377
-
378
- get size() {
379
- return locSize.size * fn();
380
- },
381
- };
382
- }
383
-
384
- /**
385
- * @param {LocSize} locSize
386
- * @param {number} value
387
- */
388
- export function locSizeEncloses(locSize, value) {
389
- return value >= locSize.location && value < locSize.location + locSize.size;
390
- }
@@ -22,6 +22,15 @@ export function isVariableParameter(param: import("../spec/parameter.js").Parame
22
22
  * @returns {param is import("../spec/parameter.js").SelectionParameter}
23
23
  */
24
24
  export function isSelectionParameter(param: import("../spec/parameter.js").Parameter): param is import("../spec/parameter.js").SelectionParameter;
25
+ /**
26
+ * Computes the default value for a parameter specification.
27
+ *
28
+ * @param {Parameter} param
29
+ * @param {ParamMediator} [paramMediator]
30
+ * @param {ExprRefFunction} [exprFn]
31
+ * @returns {any}
32
+ */
33
+ export function getDefaultParamValue(param: import("../spec/parameter.js").Parameter, paramMediator?: ParamMediator, exprFn?: ExprRefFunction): any;
25
34
  /**
26
35
  * Takes a record of properties that may have ExprRefs as values. Converts the
27
36
  * ExprRefs to getters and setups a listener that is called when any of the
@@ -97,6 +106,16 @@ export default class ParamMediator {
97
106
  * @param {string} paramName
98
107
  */
99
108
  getValue(paramName: string): any;
109
+ /**
110
+ * Subscribe to changes of a parameter's value. The listener is called only
111
+ * when the stored value changes. For expression parameters, the listener is
112
+ * called when upstream changes re-evaluate to a different value.
113
+ *
114
+ * @param {string} paramName
115
+ * @param {() => void} listener
116
+ * @returns {() => void}
117
+ */
118
+ subscribe(paramName: string, listener: () => void): () => void;
100
119
  /**
101
120
  * Get the value of a parameter from this mediator or the ancestors.
102
121
  * @param {string} paramName
@@ -1 +1 @@
1
- {"version":3,"file":"paramMediator.d.ts","sourceRoot":"","sources":["../../../src/view/paramMediator.js"],"names":[],"mappings":"AA8VA;;;GAGG;AACH,6BAHW,GAAG,GACD,CAAC,IAAI,OAAO,sBAAsB,EAAE,OAAO,CAIvD;AAED;;;;;;;GAOG;AACH,+BAHa,CAAC,KADH,CAAC,GAAG,OAAO,sBAAsB,EAAE,OAAO,GAExC,CAAC,CAWb;AAED;;;GAGG;AACH,sFAFa,KAAK,IAAI,OAAO,sBAAsB,EAAE,iBAAiB,CAIrE;AAED;;;GAGG;AACH,uFAFa,KAAK,IAAI,OAAO,sBAAsB,EAAE,kBAAkB,CAItE;AAED;;;;;;;;;;GAUG;AACH,qCAF4E,CAAC,SAA/D,MAAM,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,sBAAsB,EAAE,OAAO,CAAE,iBAJhE,aAAa,SACb,CAAC,aACD,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,IAAI,GAuCjB,CAAC,CACtB;AAED;;;;;;GAMG;AACH,4CAHW,MAAM,GACJ,MAAM,CAUlB;AAED;;;;;;;GAOG;AACH,2CAHW,GAAG,GACD,eAAe,CAY3B;AAldD;;;;;;;;GAQG;AACH;IA2BI;;;;;OAKG;IACH,2BALW,MAAM,aAAa,EAU7B;IA7BD;;;OAGG;IACH,0BAHU,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,CAGvB;IA2Bf;;;OAGG;IACH,wEAzCqB,GAAG,KAAK,IAAI,CA2GhC;IAED;;;;;;;OAOG;IACH,eAFa,CAAC,aAJH,MAAM,gBACN,CAAC,YACD,OAAO,GACL,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAgC9B;IAED;;;OAGG;IACH,qBAFW,MAAM,WA1Ic,GAAG,KAAK,IAAI,CAkJ1C;IAED;;;OAGG;IACH,oBAFW,MAAM,OAIhB;IAED;;;OAGG;IACH,qBAFW,MAAM,OAKhB;IAED;;OAEG;IACH,oBACsB,WAAW,CAAC,MAAM,2CAAY,CAGnD;IAED;;;;OAIG;IACH,gCAHW,MAAM,GACJ,aAAa,CAQzB;IAID;;;;OAIG;IACH,uBAFW,MAAM,mBAuFhB;IAED;;;;OAIG;IACH,qBAFW,MAAM,OAKhB;IAED;;;;;OAKG;IACH,sBAFa,OAAO,CAiBnB;;CACJ;;;;;;;;8BA1UY,OAAO,wBAAwB,EAAE,kBAAkB,GAAG;IAAE,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,MAAM,CAAA;CAAC"}
1
+ {"version":3,"file":"paramMediator.d.ts","sourceRoot":"","sources":["../../../src/view/paramMediator.js"],"names":[],"mappings":"AAiXA;;;GAGG;AACH,6BAHW,GAAG,GACD,CAAC,IAAI,OAAO,sBAAsB,EAAE,OAAO,CAIvD;AAED;;;;;;;GAOG;AACH,+BAHa,CAAC,KADH,CAAC,GAAG,OAAO,sBAAsB,EAAE,OAAO,GAExC,CAAC,CAWb;AAED;;;GAGG;AACH,sFAFa,KAAK,IAAI,OAAO,sBAAsB,EAAE,iBAAiB,CAIrE;AAED;;;GAGG;AACH,uFAFa,KAAK,IAAI,OAAO,sBAAsB,EAAE,kBAAkB,CAItE;AAED;;;;;;;GAOG;AACH,sGAJW,aAAa,WACb,eAAe,GACb,GAAG,CAwCf;AAED;;;;;;;;;;GAUG;AACH,qCAF4E,CAAC,SAA/D,MAAM,CAAC,MAAM,EAAE,GAAG,GAAG,OAAO,sBAAsB,EAAE,OAAO,CAAE,iBAJhE,aAAa,SACb,CAAC,aACD,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,IAAI,GAuCjB,CAAC,CACtB;AAED;;;;;;GAMG;AACH,4CAHW,MAAM,GACJ,MAAM,CAUlB;AAED;;;;;;;GAOG;AACH,2CAHW,GAAG,GACD,eAAe,CAY3B;AArhBD;;;;;;;;GAQG;AACH;IA2BI;;;;;OAKG;IACH,2BALW,MAAM,aAAa,EAU7B;IA7BD;;;OAGG;IACH,0BAHU,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,CAGvB;IA2Bf;;;OAGG;IACH,wEAzCqB,GAAG,KAAK,IAAI,CAkGhC;IAED;;;;;;;OAOG;IACH,eAFa,CAAC,aAJH,MAAM,gBACN,CAAC,YACD,OAAO,GACL,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAgC9B;IAED;;;OAGG;IACH,qBAFW,MAAM,WAjIc,GAAG,KAAK,IAAI,CAyI1C;IAED;;;OAGG;IACH,oBAFW,MAAM,OAIhB;IAED;;;;;;;;OAQG;IACH,qBAJW,MAAM,YACN,MAAM,IAAI,GACR,MAAM,IAAI,CAmBtB;IAED;;;OAGG;IACH,qBAFW,MAAM,OAKhB;IAED;;OAEG;IACH,oBACsB,WAAW,CAAC,MAAM,2CAAY,CAGnD;IAED;;;;OAIG;IACH,gCAHW,MAAM,GACJ,aAAa,CAQzB;IAID;;;;OAIG;IACH,uBAFW,MAAM,mBAuFhB;IAED;;;;OAIG;IACH,qBAFW,MAAM,OAKhB;IAED;;;;;OAKG;IACH,sBAFa,OAAO,CAiBnB;;CACJ;;;;;;;;8BA7VY,OAAO,wBAAwB,EAAE,kBAAkB,GAAG;IAAE,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,MAAM,CAAA;CAAC"}
@@ -73,6 +73,7 @@ export default class ParamMediator {
73
73
 
74
74
  /** @type {ParameterSetter} */
75
75
  let setter;
76
+ let defaultValue;
76
77
 
77
78
  if (param.push == "outer") {
78
79
  const outerMediator = this.findMediatorForParam(name);
@@ -93,36 +94,26 @@ export default class ParamMediator {
93
94
  // support mutation (i.e. adding/removing children) in future.
94
95
  this.#allocatedSetters.set(name, setter);
95
96
  } else if ("value" in param) {
96
- setter = this.allocateSetter(name, param.value);
97
+ defaultValue = getDefaultParamValue(param, this);
98
+ setter = this.allocateSetter(name, defaultValue);
97
99
  } else if ("expr" in param) {
98
100
  const expr = this.createExpression(param.expr);
99
101
  // TODO: getSetter(param) should return a setter that throws if
100
102
  // modifying the value is attempted.
101
- const realSetter = this.allocateSetter(name, expr(null));
103
+ defaultValue = getDefaultParamValue(param, this, expr);
104
+ const realSetter = this.allocateSetter(name, defaultValue);
102
105
  expr.addListener(() => realSetter(expr(null)));
103
106
  // NOP
104
107
  setter = (_) => undefined;
105
108
  } else {
106
- setter = this.allocateSetter(name, null);
109
+ defaultValue = getDefaultParamValue(param, this);
110
+ setter = this.allocateSetter(name, defaultValue);
107
111
  }
108
112
 
109
113
  if ("select" in param) {
110
- const select = asSelectionConfig(param.select);
111
- if (isPointSelectionConfig(select)) {
112
- // Set initial value so that production rules in shaders can be generated, etc.
113
- setter(
114
- select.toggle
115
- ? createMultiPointSelection()
116
- : createSinglePointSelection(null)
117
- );
118
- } else if (isIntervalSelectionConfig(select)) {
119
- if (!select.encodings) {
120
- throw new Error(
121
- `Interval selection "${name}" must have encodings defined!`
122
- );
123
- }
124
- setter(createIntervalSelection(select.encodings));
125
- }
114
+ defaultValue ??= getDefaultParamValue(param, this);
115
+ // Set initial value so that production rules in shaders can be generated, etc.
116
+ setter(defaultValue);
126
117
  }
127
118
 
128
119
  this.#paramConfigs.set(name, param);
@@ -189,6 +180,34 @@ export default class ParamMediator {
189
180
  return this.#paramValues.get(paramName);
190
181
  }
191
182
 
183
+ /**
184
+ * Subscribe to changes of a parameter's value. The listener is called only
185
+ * when the stored value changes. For expression parameters, the listener is
186
+ * called when upstream changes re-evaluate to a different value.
187
+ *
188
+ * @param {string} paramName
189
+ * @param {() => void} listener
190
+ * @returns {() => void}
191
+ */
192
+ subscribe(paramName, listener) {
193
+ validateParameterName(paramName);
194
+ const mediator = this.findMediatorForParam(paramName);
195
+ if (!mediator) {
196
+ throw new Error("Parameter not found: " + paramName);
197
+ }
198
+
199
+ const listeners = mediator.paramListeners.get(paramName) ?? new Set();
200
+ mediator.paramListeners.set(paramName, listeners);
201
+ listeners.add(listener);
202
+
203
+ return () => {
204
+ listeners.delete(listener);
205
+ if (!listeners.size) {
206
+ mediator.paramListeners.delete(paramName);
207
+ }
208
+ };
209
+ }
210
+
192
211
  /**
193
212
  * Get the value of a parameter from this mediator or the ancestors.
194
213
  * @param {string} paramName
@@ -391,6 +410,54 @@ export function isSelectionParameter(param) {
391
410
  return !("expr" in param || "bind" in param) && "select" in param;
392
411
  }
393
412
 
413
+ /**
414
+ * Computes the default value for a parameter specification.
415
+ *
416
+ * @param {Parameter} param
417
+ * @param {ParamMediator} [paramMediator]
418
+ * @param {ExprRefFunction} [exprFn]
419
+ * @returns {any}
420
+ */
421
+ export function getDefaultParamValue(param, paramMediator, exprFn) {
422
+ if ("select" in param) {
423
+ const select = asSelectionConfig(param.select);
424
+ if (isPointSelectionConfig(select)) {
425
+ return select.toggle
426
+ ? createMultiPointSelection()
427
+ : createSinglePointSelection(null);
428
+ }
429
+ if (isIntervalSelectionConfig(select)) {
430
+ if (!select.encodings) {
431
+ throw new Error(
432
+ `Interval selection "${param.name}" must have encodings defined!`
433
+ );
434
+ }
435
+ return createIntervalSelection(select.encodings);
436
+ }
437
+ throw new Error(
438
+ `Unknown selection config for parameter "${param.name}".`
439
+ );
440
+ }
441
+
442
+ if ("expr" in param) {
443
+ const expr =
444
+ exprFn ??
445
+ paramMediator?.createExpression(/** @type {string} */ (param.expr));
446
+ if (!expr) {
447
+ throw new Error(
448
+ `Cannot evaluate expression for parameter "${param.name}".`
449
+ );
450
+ }
451
+ return expr(null);
452
+ }
453
+
454
+ if ("value" in param) {
455
+ return param.value;
456
+ }
457
+
458
+ return null;
459
+ }
460
+
394
461
  /**
395
462
  * Takes a record of properties that may have ExprRefs as values. Converts the
396
463
  * ExprRefs to getters and setups a listener that is called when any of the
@@ -1 +1 @@
1
- {"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../../../src/view/testUtils.js"],"names":[],"mappings":"AAoBA;;;GAGG;AACH,2DAHW,OAAO,kBAAkB,EAAE,kBAAkB,6CA+DvD;AAGS,uBAAC,CAAC,SAAS,OAAO,WAAW,EAAE,OAAO,QAAQ,QAAQ,aAAa;IAAE,KAAI,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;CAAE,uBAAuB,OAAO,kBAAkB,EAAE,kBAAkB,GAAK,OAAO,CAAC,CAAC,CAAC,CAAA;AAgBhL,oCAAC,CAAC,SAAS,OAAO,WAAW,EAAE,OAAO,QAAQ,QAAQ,aAAa;IAAE,KAAI,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;CAAE,YAAY,WAAW,YAAY;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAC,GAAK,OAAO,CAAC,CAAC,CAAC,CAAA;;;;;uBAnGpL,OAAO,iBAAiB,EAAE,QAAQ;;;;;0BAClC,OAAO,yBAAyB,EAAE,OAAO"}
1
+ {"version":3,"file":"testUtils.d.ts","sourceRoot":"","sources":["../../../src/view/testUtils.js"],"names":[],"mappings":"AAoBA;;;GAGG;AACH,2DAHW,OAAO,kBAAkB,EAAE,kBAAkB,6CAoEvD;AAGS,uBAAC,CAAC,SAAS,OAAO,WAAW,EAAE,OAAO,QAAQ,QAAQ,aAAa;IAAE,KAAI,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;CAAE,uBAAuB,OAAO,kBAAkB,EAAE,kBAAkB,GAAK,OAAO,CAAC,CAAC,CAAC,CAAA;AAgBhL,oCAAC,CAAC,SAAS,OAAO,WAAW,EAAE,OAAO,QAAQ,QAAQ,aAAa;IAAE,KAAI,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA;CAAE,YAAY,WAAW,YAAY;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAC,GAAK,OAAO,CAAC,CAAC,CAAC,CAAA;;;;;uBAxGpL,OAAO,iBAAiB,EAAE,QAAQ;;;;;0BAClC,OAAO,yBAAyB,EAAE,OAAO"}
@@ -38,6 +38,8 @@ export function createTestViewContext(viewFactoryOptions = {}) {
38
38
  ],
39
39
  });
40
40
 
41
+ const dataFlow = new DataFlow();
42
+
41
43
  // @ts-expect-error
42
44
  const c = /** @type {ViewContext} */ ({
43
45
  createOrImportView: async function (
@@ -55,7 +57,7 @@ export function createTestViewContext(viewFactoryOptions = {}) {
55
57
  );
56
58
  },
57
59
 
58
- dataFlow: new DataFlow(),
60
+ dataFlow,
59
61
  genomeStore,
60
62
 
61
63
  fontManager: new BmFontManager(),
@@ -78,6 +80,9 @@ export function createTestViewContext(viewFactoryOptions = {}) {
78
80
 
79
81
  isViewConfiguredVisible: () => true,
80
82
 
83
+ addBroadcastListener: () => undefined,
84
+ removeBroadcastListener: () => undefined,
85
+
81
86
  //...partialContext,
82
87
  });
83
88