@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
@@ -38,6 +38,7 @@ export function createAccessor(channel, channelDef, paramMediator) {
38
38
  a.constant = a.fields.length === 0;
39
39
  a.channelDef = channelDef;
40
40
  a.channel = channel;
41
+ a.sourceKey = buildAccessorSourceKey(channelDef);
41
42
 
42
43
  a.scaleChannel =
43
44
  ((isChannelDefWithScale(channelDef) &&
@@ -45,6 +46,13 @@ export function createAccessor(channel, channelDef, paramMediator) {
45
46
  (isChannelWithScale(channel) && channel)) ||
46
47
  undefined;
47
48
 
49
+ if (a.scaleChannel !== undefined) {
50
+ a.domainKeyBase = buildDomainKey({
51
+ scaleChannel: a.scaleChannel,
52
+ source: getDomainKeySource(channelDef),
53
+ }).domainKeyBase;
54
+ }
55
+
48
56
  if ("param" in channelDef) {
49
57
  // TODO: Figure out how to fix it. Interval selection depends on FIELDS!
50
58
  /*
@@ -62,6 +70,18 @@ export function createAccessor(channel, channelDef, paramMediator) {
62
70
  a.predicate.empty = false;
63
71
  }
64
72
 
73
+ a.equals = (other) => {
74
+ if (!other) {
75
+ return false;
76
+ } else {
77
+ return (
78
+ a === other ||
79
+ (a.sourceKey !== undefined &&
80
+ a.sourceKey === other.sourceKey)
81
+ );
82
+ }
83
+ };
84
+
65
85
  a.asNumberAccessor = () =>
66
86
  /** @type {import("../types/encoder.js").Accessor<number>} */ (a);
67
87
 
@@ -152,3 +172,147 @@ export function createConditionalAccessors(channel, channelDef, paramMediator) {
152
172
  }
153
173
  return conditionalAccessors;
154
174
  }
175
+
176
+ /**
177
+ * @param {import("../types/encoder.js").Accessor} accessor
178
+ * @returns {accessor is import("../types/encoder.js").ScaleAccessor}
179
+ */
180
+ export function isScaleAccessor(accessor) {
181
+ return accessor.scaleChannel !== undefined;
182
+ }
183
+
184
+ /**
185
+ * @typedef {{
186
+ * kind: "field",
187
+ * value: string,
188
+ * } | {
189
+ * kind: "expr",
190
+ * value: string,
191
+ * } | {
192
+ * kind: "datum",
193
+ * value: import("../spec/channel.js").Scalar | import("../spec/parameter.js").ExprRef,
194
+ * } | {
195
+ * kind: "value",
196
+ * value: import("../spec/channel.js").Scalar | import("../spec/parameter.js").ExprRef,
197
+ * }} DomainKeySource
198
+ */
199
+
200
+ /**
201
+ * @param {import("../spec/channel.js").ChannelDef} channelDef
202
+ * @returns {DomainKeySource}
203
+ */
204
+ function getDomainKeySource(channelDef) {
205
+ if (isFieldDef(channelDef)) {
206
+ return { kind: "field", value: channelDef.field };
207
+ }
208
+
209
+ if (isExprDef(channelDef)) {
210
+ return { kind: "expr", value: channelDef.expr };
211
+ }
212
+
213
+ if (isDatumDef(channelDef)) {
214
+ return { kind: "datum", value: channelDef.datum };
215
+ }
216
+
217
+ if (isValueDef(channelDef)) {
218
+ return { kind: "value", value: channelDef.value };
219
+ }
220
+
221
+ throw new Error(
222
+ "Cannot derive a domain key from channel definition: " +
223
+ JSON.stringify(channelDef)
224
+ );
225
+ }
226
+
227
+ /**
228
+ * Builds a key for accessor equality. This is a structural heuristic based on
229
+ * the data source definition and ignores channel, predicate, and scale.
230
+ *
231
+ * @param {import("../spec/channel.js").ChannelDef} channelDef
232
+ * @returns {string}
233
+ */
234
+ function buildAccessorSourceKey(channelDef) {
235
+ const source = getDomainKeySource(channelDef);
236
+ if (source.kind === "datum" || source.kind === "value") {
237
+ return "constant|" + stringifyDomainValue(source.value);
238
+ }
239
+
240
+ return source.kind + "|" + stringifyDomainSource(source);
241
+ }
242
+
243
+ /**
244
+ * Builds domain key strings in the format:
245
+ * - domainKeyBase: <scaleChannel>|<kind>|<value>
246
+ * - domainKey: <type>|<domainKeyBase>
247
+ *
248
+ * @param {object} options
249
+ * @param {import("../spec/channel.js").ChannelWithScale} options.scaleChannel
250
+ * @param {DomainKeySource} options.source
251
+ * @param {import("../spec/channel.js").Type} [options.type]
252
+ * @returns {{ domainKeyBase: string, domainKey?: string }}
253
+ */
254
+ export function buildDomainKey({ scaleChannel, source, type }) {
255
+ if (!scaleChannel) {
256
+ throw new Error("Cannot build a domain key without a scale channel.");
257
+ }
258
+
259
+ const domainKeyBase =
260
+ scaleChannel + "|" + source.kind + "|" + stringifyDomainSource(source);
261
+ const domainKey = type ? type + "|" + domainKeyBase : undefined;
262
+
263
+ return { domainKeyBase, domainKey };
264
+ }
265
+
266
+ /**
267
+ * @param {import("../types/encoder.js").ScaleAccessor} accessor
268
+ * @param {import("../spec/channel.js").Type} type
269
+ * @returns {string}
270
+ */
271
+ export function getAccessorDomainKey(accessor, type) {
272
+ const { domainKey, domainKeyBase } = buildDomainKey({
273
+ scaleChannel: accessor.scaleChannel,
274
+ source: getDomainKeySource(accessor.channelDef),
275
+ type,
276
+ });
277
+ if (!domainKey) {
278
+ throw new Error(
279
+ "Cannot finalize a domain key without a resolved type."
280
+ );
281
+ }
282
+ accessor.domainKeyBase = domainKeyBase;
283
+ accessor.domainKey = domainKey;
284
+ return domainKey;
285
+ }
286
+
287
+ /**
288
+ * @param {DomainKeySource} source
289
+ * @returns {string}
290
+ */
291
+ function stringifyDomainSource(source) {
292
+ switch (source.kind) {
293
+ case "field":
294
+ case "expr":
295
+ return source.value;
296
+ case "datum":
297
+ case "value":
298
+ return stringifyDomainValue(source.value);
299
+ default:
300
+ throw new Error("Unknown domain key source.");
301
+ }
302
+ }
303
+
304
+ /**
305
+ * @param {import("../spec/channel.js").Scalar | import("../spec/parameter.js").ExprRef} value
306
+ * @returns {string}
307
+ */
308
+ function stringifyDomainValue(value) {
309
+ if (isExprRef(value)) {
310
+ return "expr:" + value.expr;
311
+ }
312
+
313
+ if (value === undefined) {
314
+ return "undefined";
315
+ }
316
+
317
+ return JSON.stringify(value);
318
+ }
@@ -8,6 +8,15 @@
8
8
  * @returns {Partial<Record<Channel, Encoder>>}
9
9
  */
10
10
  export default function createEncoders(unitView: import("../view/unitView.js").default, encoding: import("../spec/channel.js").Encoding): Partial<Record<import("../spec/channel.js").Channel, import("../types/encoder.js").Encoder>>;
11
+ /**
12
+ * Channels that are present in encoding but are not direct mark properties.
13
+ * Keep this centralized so metadata channels (for example tooltip) can reuse
14
+ * the same handling path.
15
+ *
16
+ * @param {import("../spec/channel.js").Channel} channel
17
+ * @returns {boolean}
18
+ */
19
+ export function isNonMarkPropertyChannel(channel: import("../spec/channel.js").Channel): boolean;
11
20
  /**
12
21
  * @param {import("../types/encoder.js").Accessor[]} accessors
13
22
  * @param {(channel: import("../spec/channel.js").ChannelWithScale) => import("../types/encoder.js").VegaScale} scaleSource
@@ -45,12 +54,12 @@ export function isChannelDefWithScale(channelDef: import("../spec/channel.js").C
45
54
  /**
46
55
  * @param {import("../spec/channel.js").ChannelDef} channelDef
47
56
  */
48
- export function findChannelDefWithScale(channelDef: import("../spec/channel.js").ChannelDef): import("../spec/channel.js").FieldOrDatumDefWithCondition<import("../spec/channel.js").MarkPropFieldDef<import("../spec/channel.js").Type>, string> | import("../spec/channel.js").PositionFieldDef | import("../spec/channel.js").PositionDatumDef | import("../spec/channel.js").ChromPosDef | import("../spec/channel.js").FieldOrDatumDefWithCondition<import("../spec/channel.js").MarkPropFieldDef<import("../spec/channel.js").Type>, number> | (import("../spec/channel.js").ParameterPredicate & import("../spec/channel.js").FieldDefBase & import("../spec/channel.js").TitleMixins & import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type>) | (import("../spec/channel.js").ParameterPredicate & import("../spec/channel.js").DatumDef<import("../spec/parameter.js").ExprRef | import("../spec/channel.js").Scalar> & import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type>) | (import("../spec/channel.js").ParameterPredicate & import("../spec/channel.js").ValueDefBase<string | import("../spec/parameter.js").ExprRef> & import("../spec/channel.js").TitleMixins & import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type>) | (import("../spec/channel.js").ParameterPredicate & import("../spec/channel.js").ValueDefBase<number | import("../spec/parameter.js").ExprRef> & import("../spec/channel.js").TitleMixins & import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type>);
57
+ export function findChannelDefWithScale(channelDef: import("../spec/channel.js").ChannelDef): import("../spec/channel.js").FieldOrDatumDefWithCondition<import("../spec/channel.js").MarkPropFieldDef<import("../spec/channel.js").Type>, string> | import("../spec/channel.js").PositionFieldDef | import("../spec/channel.js").PositionDatumDef | import("../spec/channel.js").ChromPosDef | import("../spec/channel.js").FieldOrDatumDefWithCondition<import("../spec/channel.js").MarkPropFieldDef<import("../spec/channel.js").Type>, number> | (import("../spec/channel.js").ParameterPredicate & import("../spec/channel.js").FieldDefBase & import("../spec/channel.js").TitleMixins & import("../spec/channel.js").DomainContributionMixins & import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type>) | (import("../spec/channel.js").ParameterPredicate & import("../spec/channel.js").DatumDef<import("../spec/parameter.js").ExprRef | import("../spec/channel.js").Scalar> & import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type> & import("../spec/channel.js").DomainContributionMixins) | (import("../spec/channel.js").ParameterPredicate & import("../spec/channel.js").ValueDefBase<string | import("../spec/parameter.js").ExprRef> & import("../spec/channel.js").TitleMixins & import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type> & import("../spec/channel.js").DomainContributionMixins) | (import("../spec/channel.js").ParameterPredicate & import("../spec/channel.js").ValueDefBase<number | import("../spec/parameter.js").ExprRef> & import("../spec/channel.js").TitleMixins & import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type> & import("../spec/channel.js").DomainContributionMixins);
49
58
  /**
50
59
  * @param {import("../view/unitView.js").default} view
51
60
  * @param {import("../spec/channel.js").Channel} channel
52
61
  */
53
- export function getChannelDefWithScale(view: import("../view/unitView.js").default, channel: import("../spec/channel.js").Channel): import("../spec/channel.js").FieldOrDatumDefWithCondition<import("../spec/channel.js").MarkPropFieldDef<import("../spec/channel.js").Type>, string> | import("../spec/channel.js").PositionFieldDef | import("../spec/channel.js").PositionDatumDef | import("../spec/channel.js").ChromPosDef | import("../spec/channel.js").FieldOrDatumDefWithCondition<import("../spec/channel.js").MarkPropFieldDef<import("../spec/channel.js").Type>, number> | import("../spec/channel.js").FieldOrDatumDefWithCondition<import("../spec/channel.js").MarkPropFieldDef<import("../spec/channel.js").TypeForShape>, string> | (import("../spec/channel.js").FieldDefBase & import("../spec/channel.js").TitleMixins & import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type> & import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").PositionMixins & import("../spec/channel.js").XIndexDef) | (import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").DatumDef<import("../spec/parameter.js").ExprRef | import("../spec/channel.js").Scalar> & import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type> & import("../spec/channel.js").PositionMixins & import("../spec/channel.js").XIndexDef) | (import("../spec/channel.js").ChromPosDefBase & import("../spec/channel.js").TitleMixins & import("../spec/channel.js").PositionMixins & import("../spec/channel.js").TypeMixins<"locus"> & import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").XIndexDef);
62
+ export function getChannelDefWithScale(view: import("../view/unitView.js").default, channel: import("../spec/channel.js").Channel): import("../spec/channel.js").FieldOrDatumDefWithCondition<import("../spec/channel.js").MarkPropFieldDef<import("../spec/channel.js").Type>, string> | import("../spec/channel.js").PositionFieldDef | import("../spec/channel.js").PositionDatumDef | import("../spec/channel.js").ChromPosDef | import("../spec/channel.js").FieldOrDatumDefWithCondition<import("../spec/channel.js").MarkPropFieldDef<import("../spec/channel.js").Type>, number> | import("../spec/channel.js").FieldOrDatumDefWithCondition<import("../spec/channel.js").MarkPropFieldDef<import("../spec/channel.js").TypeForShape>, string> | (import("../spec/channel.js").FieldDefBase & import("../spec/channel.js").TitleMixins & import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type> & import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").DomainContributionMixins & import("../spec/channel.js").PositionMixins & import("../spec/channel.js").XIndexDef) | (import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").DatumDef<import("../spec/parameter.js").ExprRef | import("../spec/channel.js").Scalar> & import("../spec/channel.js").DomainContributionMixins & import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type> & import("../spec/channel.js").PositionMixins & import("../spec/channel.js").XIndexDef) | (import("../spec/channel.js").ChromPosDefBase & import("../spec/channel.js").TitleMixins & import("../spec/channel.js").PositionMixins & import("../spec/channel.js").DomainContributionMixins & import("../spec/channel.js").TypeMixins<"locus"> & import("../spec/channel.js").ScaleMixins & import("../spec/channel.js").XIndexDef);
54
63
  /**
55
64
  * @param {import("../spec/channel.js").ChannelDef} channelDef
56
65
  * @returns {channelDef is import("../spec/channel.js").TypeMixins<import("../spec/channel.js").Type>}
@@ -1 +1 @@
1
- {"version":3,"file":"encoder.d.ts","sourceRoot":"","sources":["../../../src/encoder/encoder.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"encoder.d.ts","sourceRoot":"","sources":["../../../src/encoder/encoder.js"],"names":[],"mappings":"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"}
@@ -82,10 +82,18 @@ export default function createEncoders(unitView, encoding) {
82
82
  continue;
83
83
  }
84
84
 
85
- encoders[channel] = createSimpleOrConditionalEncoder(
85
+ /** @type {Channel} */
86
+ const typedChannel = /** @type {Channel} */ (channel);
87
+ if (isNonMarkPropertyChannel(typedChannel)) {
88
+ continue;
89
+ }
90
+
91
+ const typedChannelDef =
92
+ /** @type {import("../spec/channel.js").ChannelDef} */ (channelDef);
93
+ encoders[typedChannel] = createSimpleOrConditionalEncoder(
86
94
  createConditionalAccessors(
87
- channel,
88
- channelDef,
95
+ typedChannel,
96
+ typedChannelDef,
89
97
  unitView.paramMediator
90
98
  ),
91
99
  scaleSource
@@ -95,6 +103,18 @@ export default function createEncoders(unitView, encoding) {
95
103
  return encoders;
96
104
  }
97
105
 
106
+ /**
107
+ * Channels that are present in encoding but are not direct mark properties.
108
+ * Keep this centralized so metadata channels (for example tooltip) can reuse
109
+ * the same handling path.
110
+ *
111
+ * @param {import("../spec/channel.js").Channel} channel
112
+ * @returns {boolean}
113
+ */
114
+ export function isNonMarkPropertyChannel(channel) {
115
+ return channel === "key" || channel === "search";
116
+ }
117
+
98
118
  /**
99
119
  * @param {import("../types/encoder.js").Accessor[]} accessors
100
120
  * @param {(channel: import("../spec/channel.js").ChannelWithScale) => import("../types/encoder.js").VegaScale} scaleSource
@@ -237,7 +257,7 @@ export function findChannelDefWithScale(channelDef) {
237
257
  */
238
258
  export function getChannelDefWithScale(view, channel) {
239
259
  const channelDef = view.mark.encoding[channel];
240
- if (isChannelDefWithScale(channelDef)) {
260
+ if (!Array.isArray(channelDef) && isChannelDefWithScale(channelDef)) {
241
261
  return channelDef;
242
262
  } else {
243
263
  throw new Error("Not a channel def with scale!");
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Returns key fields used for point selections, if configured.
3
+ *
4
+ * @param {import("../spec/channel.js").Encoding} encoding
5
+ * @returns {string[] | undefined}
6
+ */
7
+ export function getEncodingKeyFields(encoding: import("../spec/channel.js").Encoding): string[] | undefined;
8
+ /**
9
+ * Returns search fields configured in encoding.search, if any.
10
+ *
11
+ * @param {import("../spec/channel.js").Encoding} encoding
12
+ * @returns {string[] | undefined}
13
+ */
14
+ export function getEncodingSearchFields(encoding: import("../spec/channel.js").Encoding): string[] | undefined;
15
+ //# sourceMappingURL=metadataChannels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadataChannels.d.ts","sourceRoot":"","sources":["../../../src/encoder/metadataChannels.js"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,+CAHW,OAAO,oBAAoB,EAAE,QAAQ,GACnC,MAAM,EAAE,GAAG,SAAS,CAIhC;AAED;;;;;GAKG;AACH,kDAHW,OAAO,oBAAoB,EAAE,QAAQ,GACnC,MAAM,EAAE,GAAG,SAAS,CAIhC"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Returns key fields used for point selections, if configured.
3
+ *
4
+ * @param {import("../spec/channel.js").Encoding} encoding
5
+ * @returns {string[] | undefined}
6
+ */
7
+ export function getEncodingKeyFields(encoding) {
8
+ return getNormalizedFieldsFromChannel(encoding?.key, "key");
9
+ }
10
+
11
+ /**
12
+ * Returns search fields configured in encoding.search, if any.
13
+ *
14
+ * @param {import("../spec/channel.js").Encoding} encoding
15
+ * @returns {string[] | undefined}
16
+ */
17
+ export function getEncodingSearchFields(encoding) {
18
+ return getNormalizedFieldsFromChannel(encoding?.search, "search");
19
+ }
20
+
21
+ /**
22
+ * @param {import("../spec/channel.js").FieldDefWithoutScale | import("../spec/channel.js").FieldDefWithoutScale[] | undefined} channelDef
23
+ * @param {"key" | "search"} channelName
24
+ * @returns {string[] | undefined}
25
+ */
26
+ function getNormalizedFieldsFromChannel(channelDef, channelName) {
27
+ if (!channelDef) {
28
+ return;
29
+ }
30
+
31
+ const definitions = Array.isArray(channelDef) ? channelDef : [channelDef];
32
+ if (definitions.length === 0) {
33
+ throw new Error(
34
+ "The " + channelName + " channel array must not be empty."
35
+ );
36
+ }
37
+
38
+ /** @type {string[]} */
39
+ const fields = [];
40
+ for (const definition of definitions) {
41
+ if (
42
+ !definition ||
43
+ typeof definition !== "object" ||
44
+ !("field" in definition)
45
+ ) {
46
+ throw new Error(
47
+ "The " +
48
+ channelName +
49
+ " channel must be a field definition or an array of field definitions."
50
+ );
51
+ }
52
+
53
+ const fieldName = definition.field;
54
+ if (typeof fieldName !== "string") {
55
+ throw new Error(
56
+ "The " +
57
+ channelName +
58
+ " channel field definition must include a string field name."
59
+ );
60
+ }
61
+ fields.push(fieldName);
62
+ }
63
+
64
+ return fields;
65
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=metadataChannels.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadataChannels.test.d.ts","sourceRoot":"","sources":["../../../src/encoder/metadataChannels.test.js"],"names":[],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"scaleLocus.d.ts","sourceRoot":"","sources":["../../../src/genome/scaleLocus.js"],"names":[],"mappings":"AAMA,2EAyFC;;AASD;;;;GAIG;AACH,8CAJW,OAAO,aAAa,EAAE,OAAO,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,OAAO,aAAa,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,SAC5F,MAAM,GACJ,MAAM,GAAG,OAAO,aAAa,EAAE,gBAAgB,CAK3D;AAED;;;;GAIG;AACH,gDAJW,OAAO,aAAa,EAAE,OAAO,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,OAAO,aAAa,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,WAC5F,MAAM,GAAG,OAAO,aAAa,EAAE,gBAAgB,GAC7C,MAAM,CAQlB;AAED;;;;GAIG;AACH,mDAJW,OAAO,aAAa,EAAE,OAAO,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,OAAO,aAAa,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,YAC5F,OAAO,kBAAkB,EAAE,YAAY,GAAG,OAAO,kBAAkB,EAAE,aAAa,GAChF,MAAM,EAAE,CAQpB;AAED;;;;GAIG;AACH,iDAJW,OAAO,aAAa,EAAE,OAAO,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,OAAO,aAAa,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,YAC5F,OAAO,kBAAkB,EAAE,YAAY,GAAG,OAAO,kBAAkB,EAAE,aAAa,GAChF,OAAO,kBAAkB,EAAE,YAAY,GAAG,OAAO,kBAAkB,EAAE,aAAa,CAO9F;AAED;;;GAGG;AACH,+CAHW,OAAO,aAAa,EAAE,OAAO,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,OAAO,aAAa,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GAC1F,MAAM,EAAE,CAQpB"}
1
+ {"version":3,"file":"scaleLocus.d.ts","sourceRoot":"","sources":["../../../src/genome/scaleLocus.js"],"names":[],"mappings":"AAMA,2EAsGC;;AASD;;;;GAIG;AACH,8CAJW,OAAO,aAAa,EAAE,OAAO,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,OAAO,aAAa,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,SAC5F,MAAM,GACJ,MAAM,GAAG,OAAO,aAAa,EAAE,gBAAgB,CAK3D;AAED;;;;GAIG;AACH,gDAJW,OAAO,aAAa,EAAE,OAAO,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,OAAO,aAAa,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,WAC5F,MAAM,GAAG,OAAO,aAAa,EAAE,gBAAgB,GAC7C,MAAM,CAQlB;AAED;;;;GAIG;AACH,mDAJW,OAAO,aAAa,EAAE,OAAO,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,OAAO,aAAa,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,YAC5F,OAAO,kBAAkB,EAAE,YAAY,GAAG,OAAO,kBAAkB,EAAE,aAAa,GAChF,MAAM,EAAE,CAQpB;AAED;;;;GAIG;AACH,iDAJW,OAAO,aAAa,EAAE,OAAO,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,OAAO,aAAa,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,YAC5F,OAAO,kBAAkB,EAAE,YAAY,GAAG,OAAO,kBAAkB,EAAE,aAAa,GAChF,OAAO,kBAAkB,EAAE,YAAY,GAAG,OAAO,kBAAkB,EAAE,aAAa,CAO9F;AAED;;;GAGG;AACH,+CAHW,OAAO,aAAa,EAAE,OAAO,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,OAAO,aAAa,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GAC1F,MAAM,EAAE,CAQpB"}
@@ -90,7 +90,20 @@ export default function scaleLocus() {
90
90
 
91
91
  const originalCopy = scale.copy;
92
92
 
93
- scale.copy = () => originalCopy().genome(genome);
93
+ scale.copy = () => {
94
+ const copied = originalCopy();
95
+ let copiedGenome = genome;
96
+ // @ts-expect-error
97
+ copied.genome = function (_) {
98
+ if (arguments.length) {
99
+ copiedGenome = _;
100
+ return copied;
101
+ } else {
102
+ return copiedGenome;
103
+ }
104
+ };
105
+ return copied.genome(genome);
106
+ };
94
107
 
95
108
  return scale;
96
109
  }
@@ -3,7 +3,6 @@
3
3
  */
4
4
  export function createContainerUi(container: HTMLElement): {
5
5
  canvasWrapper: HTMLElement;
6
- loadingMessageElement: HTMLElement;
7
6
  loadingIndicatorsElement: HTMLElement;
8
7
  tooltip: Tooltip;
9
8
  styleElement: HTMLStyleElement;
@@ -1 +1 @@
1
- {"version":3,"file":"containerUi.d.ts","sourceRoot":"","sources":["../../../src/genomeSpy/containerUi.js"],"names":[],"mappings":"AAGA;;GAEG;AACH,6CAFW,WAAW;;;;;;EA2CrB;AAED;;;GAGG;AACH,4CAHW,WAAW,WACX,MAAM,QAUhB;oBA5DmB,wBAAwB"}
1
+ {"version":3,"file":"containerUi.d.ts","sourceRoot":"","sources":["../../../src/genomeSpy/containerUi.js"],"names":[],"mappings":"AAGA;;GAEG;AACH,6CAFW,WAAW;;;;;EA6BrB;AAED;;;GAGG;AACH,4CAHW,WAAW,WACX,MAAM,QAUhB;oBA9CmB,wBAAwB"}
@@ -18,12 +18,6 @@ export function createContainerUi(container) {
18
18
 
19
19
  canvasWrapper.classList.add("loading");
20
20
 
21
- const loadingMessageElement = element("div", {
22
- class: "loading-message",
23
- innerHTML: `<div class="message">Loading<span class="ellipsis">...</span></div>`,
24
- });
25
- canvasWrapper.appendChild(loadingMessageElement);
26
-
27
21
  const loadingIndicatorsElement = element("div", {
28
22
  class: "loading-indicators",
29
23
  });
@@ -31,16 +25,8 @@ export function createContainerUi(container) {
31
25
 
32
26
  const tooltip = new Tooltip(container);
33
27
 
34
- loadingMessageElement
35
- .querySelector(".message")
36
- .addEventListener("transitionend", () => {
37
- /** @type {HTMLElement} */ (loadingMessageElement).style.display =
38
- "none";
39
- });
40
-
41
28
  return {
42
29
  canvasWrapper,
43
- loadingMessageElement,
44
30
  loadingIndicatorsElement,
45
31
  tooltip,
46
32
  styleElement,
@@ -1,14 +1,10 @@
1
1
  export default class LoadingIndicatorManager {
2
2
  /**
3
3
  * @param {HTMLElement} loadingIndicatorsElement
4
+ * @param {import("./loadingStatusRegistry.js").default} loadingStatusRegistry
4
5
  */
5
- constructor(loadingIndicatorsElement: HTMLElement);
6
- /**
7
- * @param {import("../view/view.js").default} view
8
- * @param {import("../types/viewContext.js").DataLoadingStatus} status
9
- * @param {string} [detail]
10
- */
11
- setDataLoadingStatus(view: import("../view/view.js").default, status: import("../types/viewContext.js").DataLoadingStatus, detail?: string): void;
6
+ constructor(loadingIndicatorsElement: HTMLElement, loadingStatusRegistry: import("./loadingStatusRegistry.js").default);
7
+ destroy(): void;
12
8
  updateLayout(): void;
13
9
  #private;
14
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"loadingIndicatorManager.d.ts","sourceRoot":"","sources":["../../../src/genomeSpy/loadingIndicatorManager.js"],"names":[],"mappings":"AAIA;IAQI;;OAEG;IACH,sCAFW,WAAW,EASrB;IAED;;;;OAIG;IACH,2BAJW,OAAO,iBAAiB,EAAE,OAAO,UACjC,OAAO,yBAAyB,EAAE,iBAAiB,WACnD,MAAM,QAKhB;IAED,qBAwDC;;CACJ"}
1
+ {"version":3,"file":"loadingIndicatorManager.d.ts","sourceRoot":"","sources":["../../../src/genomeSpy/loadingIndicatorManager.js"],"names":[],"mappings":"AAIA;IAYI;;;OAGG;IACH,sCAHW,WAAW,yBACX,OAAO,4BAA4B,EAAE,OAAO,EAWtD;IAED,gBAKC;IAED,qBAoGC;;CACJ"}
@@ -5,45 +5,67 @@ import SPINNER from "../img/90-ring-with-bg.svg";
5
5
  export default class LoadingIndicatorManager {
6
6
  /** @type {HTMLElement} */
7
7
  #loadingIndicatorsElement;
8
+
8
9
  /**
9
- * @type {Map<import("../view/view.js").default, { status: import("../types/viewContext.js").DataLoadingStatus, detail?: string }>}
10
+ * @type {import("./loadingStatusRegistry.js").default}
10
11
  */
11
- #loadingViews;
12
+ #loadingStatusRegistry;
13
+
14
+ /** @type {(() => void) | null} */
15
+ #unsubscribe = null;
12
16
 
13
17
  /**
14
18
  * @param {HTMLElement} loadingIndicatorsElement
19
+ * @param {import("./loadingStatusRegistry.js").default} loadingStatusRegistry
15
20
  */
16
- constructor(loadingIndicatorsElement) {
21
+ constructor(loadingIndicatorsElement, loadingStatusRegistry) {
17
22
  this.#loadingIndicatorsElement = loadingIndicatorsElement;
18
23
 
19
- /**
20
- * @type {Map<import("../view/view.js").default, { status: import("../types/viewContext.js").DataLoadingStatus, detail?: string }>}
21
- */
22
- this.#loadingViews = new Map();
23
- }
24
+ this.#loadingStatusRegistry = loadingStatusRegistry;
24
25
 
25
- /**
26
- * @param {import("../view/view.js").default} view
27
- * @param {import("../types/viewContext.js").DataLoadingStatus} status
28
- * @param {string} [detail]
29
- */
30
- setDataLoadingStatus(view, status, detail) {
31
- this.#loadingViews.set(view, { status, detail });
26
+ this.#unsubscribe = this.#loadingStatusRegistry.subscribe(() =>
27
+ this.updateLayout()
28
+ );
32
29
  this.updateLayout();
33
30
  }
34
31
 
32
+ destroy() {
33
+ if (this.#unsubscribe) {
34
+ this.#unsubscribe();
35
+ this.#unsubscribe = null;
36
+ }
37
+ }
38
+
35
39
  updateLayout() {
36
40
  /** @type {import("lit").TemplateResult[]} */
37
41
  const indicators = [];
38
42
 
39
- const isSomethingVisible = () =>
40
- [...this.#loadingViews.values()].some(
41
- (v) => v.status == "loading" || v.status == "error"
42
- );
43
+ const isSomethingVisible = () => {
44
+ for (const [, status] of this.#loadingStatusRegistry.entries()) {
45
+ if (status.status == "loading" || status.status == "error") {
46
+ return true;
47
+ }
48
+ }
49
+ return false;
50
+ };
51
+
52
+ /** @type {{ status: import("../types/viewContext.js").DataLoadingStatus, detail?: string } | undefined} */
53
+ let fallbackStatus;
54
+ let hasVisibleWithCoords = false;
55
+
56
+ for (const [view, status] of this.#loadingStatusRegistry.entries()) {
57
+ const isVisible =
58
+ status.status == "loading" || status.status == "error";
43
59
 
44
- for (const [view, status] of this.#loadingViews) {
45
60
  const c = view.coords;
61
+ if (!c && isVisible && !fallbackStatus) {
62
+ fallbackStatus = status;
63
+ }
46
64
  if (c) {
65
+ if (isVisible) {
66
+ hasVisibleWithCoords = true;
67
+ }
68
+
47
69
  const style = {
48
70
  left: `${c.x}px`,
49
71
  top: `${c.y}px`,
@@ -70,6 +92,32 @@ export default class LoadingIndicatorManager {
70
92
  }
71
93
  }
72
94
 
95
+ if (fallbackStatus && !hasVisibleWithCoords) {
96
+ const style = {
97
+ left: "0px",
98
+ top: "0px",
99
+ width: "100%",
100
+ height: "100%",
101
+ };
102
+ indicators.push(
103
+ html`<div style=${styleMap(style)}>
104
+ <div class=${fallbackStatus.status}>
105
+ ${fallbackStatus.status == "error"
106
+ ? html`<span
107
+ >Loading
108
+ failed${fallbackStatus.detail
109
+ ? html`: ${fallbackStatus.detail}`
110
+ : nothing}</span
111
+ >`
112
+ : html`
113
+ <img src="${SPINNER}" alt="" />
114
+ <span>Loading...</span>
115
+ `}
116
+ </div>
117
+ </div>`
118
+ );
119
+ }
120
+
73
121
  // Do some hacks to stop css animations of the loading indicators.
74
122
  // Otherwise they fire animation frames even when their opacity is zero.
75
123
  // TODO: Instead of this, replace the animated spinners with static images.