@genome-spy/core 0.45.0 → 0.46.1

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 (79) 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 +3624 -3516
  11. package/dist/bundle/index.js +101 -92
  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 +156 -15
  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 +19 -3
  18. package/dist/src/data/flow.test.js +4 -0
  19. package/dist/src/data/flowNode.d.ts +31 -14
  20. package/dist/src/data/flowNode.d.ts.map +1 -1
  21. package/dist/src/data/flowNode.js +53 -20
  22. package/dist/src/data/flowTestUtils.d.ts +5 -0
  23. package/dist/src/data/flowTestUtils.d.ts.map +1 -1
  24. package/dist/src/data/flowTestUtils.js +11 -0
  25. package/dist/src/data/sources/dataSource.d.ts +17 -0
  26. package/dist/src/data/sources/dataSource.d.ts.map +1 -1
  27. package/dist/src/data/sources/dataSource.js +34 -0
  28. package/dist/src/data/sources/inlineSource.js +1 -1
  29. package/dist/src/data/sources/lazy/bigBedSource.d.ts.map +1 -1
  30. package/dist/src/data/sources/lazy/bigBedSource.js +6 -0
  31. package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
  32. package/dist/src/data/sources/lazy/bigWigSource.js +28 -15
  33. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts +0 -8
  34. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
  35. package/dist/src/data/sources/lazy/singleAxisLazySource.js +1 -13
  36. package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +3 -3
  37. package/dist/src/data/sources/namedSource.js +1 -1
  38. package/dist/src/data/sources/sequenceSource.d.ts.map +1 -1
  39. package/dist/src/data/sources/sequenceSource.js +2 -1
  40. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  41. package/dist/src/data/sources/urlSource.js +9 -5
  42. package/dist/src/data/transforms/clone.d.ts +1 -0
  43. package/dist/src/data/transforms/clone.d.ts.map +1 -1
  44. package/dist/src/data/transforms/coverage.d.ts +4 -1
  45. package/dist/src/data/transforms/coverage.d.ts.map +1 -1
  46. package/dist/src/data/transforms/coverage.js +44 -31
  47. package/dist/src/data/transforms/filter.d.ts +4 -3
  48. package/dist/src/data/transforms/filter.d.ts.map +1 -1
  49. package/dist/src/data/transforms/filter.js +7 -8
  50. package/dist/src/data/transforms/filter.test.js +4 -2
  51. package/dist/src/data/transforms/formula.d.ts +4 -3
  52. package/dist/src/data/transforms/formula.d.ts.map +1 -1
  53. package/dist/src/data/transforms/formula.js +7 -5
  54. package/dist/src/data/transforms/formula.test.js +13 -7
  55. package/dist/src/genomeSpy.d.ts +5 -2
  56. package/dist/src/genomeSpy.d.ts.map +1 -1
  57. package/dist/src/genomeSpy.js +24 -8
  58. package/dist/src/marks/mark.d.ts.map +1 -1
  59. package/dist/src/marks/mark.js +11 -0
  60. package/dist/src/spec/view.d.ts +52 -9
  61. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  62. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  63. package/dist/src/styles/genome-spy.css.js +4 -0
  64. package/dist/src/styles/genome-spy.scss +5 -0
  65. package/dist/src/types/viewContext.d.ts +9 -2
  66. package/dist/src/utils/expression.js +1 -1
  67. package/dist/src/view/paramMediator.d.ts +2 -1
  68. package/dist/src/view/paramMediator.d.ts.map +1 -1
  69. package/dist/src/view/paramMediator.js +3 -2
  70. package/dist/src/view/paramMediator.test.js +13 -0
  71. package/dist/src/view/viewFactory.d.ts +4 -1
  72. package/dist/src/view/viewFactory.d.ts.map +1 -1
  73. package/dist/src/view/viewFactory.js +52 -24
  74. package/dist/src/view/viewUtils.d.ts +2 -7
  75. package/dist/src/view/viewUtils.d.ts.map +1 -1
  76. package/dist/src/view/viewUtils.js +21 -30
  77. package/package.json +2 -2
  78. /package/dist/bundle/{__vite-browser-external-ENoMJThg.js → __vite-browser-external-C--ziKoh.js} +0 -0
  79. /package/dist/bundle/{_commonjsHelpers-QtkX90xp.js → _commonjsHelpers-BIiJCwQW.js} +0 -0
@@ -16,6 +16,10 @@ export default class CoverageTransform extends FlowNode {
16
16
  return BEHAVIOR_CLONES;
17
17
  }
18
18
 
19
+ /**
20
+ * @typedef {import("../flowNode.js").Datum} Datum
21
+ */
22
+
19
23
  /**
20
24
  * @param {import("../../spec/transform.js").CoverageParams} params
21
25
  */
@@ -34,30 +38,30 @@ export default class CoverageTransform extends FlowNode {
34
38
  this.weightAccessor = params.weight ? field(params.weight) : (d) => 1;
35
39
 
36
40
  this.as = {
37
- coverage: params.as || "coverage",
38
- start: params.asStart || params.start,
39
- end: params.asEnd || params.end,
40
- chrom: params.asChrom || params.chrom,
41
+ coverage: params.as ?? "coverage",
42
+ start: params.asStart ?? params.start,
43
+ end: params.asEnd ?? params.end,
44
+ chrom: params.asChrom ?? params.chrom,
41
45
  };
42
46
 
43
- // eslint-disable-next-line no-new-func
44
- this.createSegment = /** @type {function} */ (
45
- new Function(
46
- "start",
47
- "end",
48
- "coverage",
49
- "chrom",
50
- "return {" +
51
- Object.entries(this.as)
52
- .filter(([param, prop]) => prop)
53
- .map(
54
- ([param, prop]) =>
55
- `${JSON.stringify(prop)}: ${param}`
56
- )
57
- .join(", ") +
58
- "};"
59
- )
60
- );
47
+ this.createSegment =
48
+ /** @type {(start: Number, end: Number, coverage: Number, chrom?: string) => Datum} */ (
49
+ new Function(
50
+ "start",
51
+ "end",
52
+ "coverage",
53
+ "chrom",
54
+ "return {" +
55
+ Object.entries(this.as)
56
+ .filter(([param, prop]) => prop)
57
+ .map(
58
+ ([param, prop]) =>
59
+ `${JSON.stringify(prop)}: ${param}`
60
+ )
61
+ .join(", ") +
62
+ "};"
63
+ )
64
+ );
61
65
 
62
66
  /**
63
67
  * End pos as priority, weight as value
@@ -82,7 +86,7 @@ export default class CoverageTransform extends FlowNode {
82
86
  const chromAccessor = this.chromAccessor;
83
87
  const weightAccessor = this.weightAccessor;
84
88
 
85
- /** @type {import("../flowNode.js").Datum} used for merging adjacent segment */
89
+ /** @type {Datum} used for merging adjacent segment */
86
90
  let bufferedSegment;
87
91
 
88
92
  /** @type {string} */
@@ -96,12 +100,20 @@ export default class CoverageTransform extends FlowNode {
96
100
  let coverage = 0;
97
101
 
98
102
  /** @type {number} */
99
- let prevEdge;
103
+ let prevEdge = NaN;
100
104
 
101
105
  /** End pos as priority, weight as value */
102
106
  const ends = this.ends;
103
107
  ends.clear();
104
108
 
109
+ /**
110
+ * @param {Datum} segment
111
+ */
112
+ const propagate = (segment) => {
113
+ this._propagate(segment);
114
+ bufferedSegment = null;
115
+ };
116
+
105
117
  /**
106
118
  * @param {number} start
107
119
  * @param {number} end
@@ -119,7 +131,7 @@ export default class CoverageTransform extends FlowNode {
119
131
  bufferedSegment[asEnd] = end;
120
132
  extended = true;
121
133
  } else if (bufferedSegment[asCoverage] != 0) {
122
- this._propagate(bufferedSegment);
134
+ propagate(bufferedSegment);
123
135
  }
124
136
  }
125
137
 
@@ -140,15 +152,16 @@ export default class CoverageTransform extends FlowNode {
140
152
  prevEdge = edge;
141
153
  coverage -= ends.pop();
142
154
  }
143
- prevEdge = undefined;
155
+ prevEdge = NaN;
144
156
 
145
157
  if (bufferedSegment) {
146
- this._propagate(bufferedSegment);
147
- bufferedSegment = undefined;
158
+ propagate(bufferedSegment);
148
159
  }
149
160
  };
150
161
 
151
- /** @param {Record<string, any>} datum */
162
+ /**
163
+ * @param {Datum} datum
164
+ */
152
165
  this.handle = (datum) => {
153
166
  const start = startAccessor(datum);
154
167
 
@@ -168,7 +181,7 @@ export default class CoverageTransform extends FlowNode {
168
181
  }
169
182
  }
170
183
 
171
- if (prevEdge !== undefined) {
184
+ if (!isNaN(prevEdge)) {
172
185
  pushSegment(prevEdge, start, coverage);
173
186
  }
174
187
  prevEdge = start;
@@ -189,7 +202,7 @@ export default class CoverageTransform extends FlowNode {
189
202
  */
190
203
  this.beginBatch = (flowBatch) => {
191
204
  flushQueue();
192
- prevChrom = undefined;
205
+ prevChrom = null;
193
206
  super.beginBatch(flowBatch);
194
207
  };
195
208
  }
@@ -2,11 +2,12 @@ export default class FilterTransform extends FlowNode {
2
2
  /**
3
3
  *
4
4
  * @param {import("../../spec/transform.js").FilterParams} params
5
+ * @param {import("../flowNode.js").ParamMediatorProvider} paramMediatorProvider
5
6
  */
6
- constructor(params: import("../../spec/transform.js").FilterParams);
7
+ constructor(params: import("../../spec/transform.js").FilterParams, paramMediatorProvider: import("../flowNode.js").ParamMediatorProvider);
7
8
  params: import("../../spec/transform.js").FilterParams;
8
- /** @type {(datum: any) => boolean} */
9
- predicate: (datum: any) => boolean;
9
+ /** @type {import("../../view/paramMediator.js").ExprRefFunction} */
10
+ predicate: import("../../view/paramMediator.js").ExprRefFunction;
10
11
  }
11
12
  import FlowNode from "../flowNode.js";
12
13
  //# sourceMappingURL=filter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../../../src/data/transforms/filter.js"],"names":[],"mappings":"AAGA;IACI;;;OAGG;IACH,oBAFW,OAAO,yBAAyB,EAAE,YAAY,EAQxD;IAJG,uDAAoB;IAEpB,sCAAsC;IACtC,mBADmB,GAAG,KAAK,OAAO,CACR;CAmBjC;qBA/BoB,gBAAgB"}
1
+ {"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../../../src/data/transforms/filter.js"],"names":[],"mappings":"AAEA;IACI;;;;OAIG;IACH,oBAHW,OAAO,yBAAyB,EAAE,YAAY,yBAC9C,OAAO,gBAAgB,EAAE,qBAAqB,EASxD;IAJG,uDAAoB;IAEpB,oEAAoE;IACpE,WADW,OAAO,6BAA6B,EAAE,eAAe,CACtC;CAiBjC;qBA/BoB,gBAAgB"}
@@ -1,24 +1,23 @@
1
- import createFunction from "../../utils/expression.js";
2
1
  import FlowNode from "../flowNode.js";
3
2
 
4
3
  export default class FilterTransform extends FlowNode {
5
4
  /**
6
5
  *
7
6
  * @param {import("../../spec/transform.js").FilterParams} params
7
+ * @param {import("../flowNode.js").ParamMediatorProvider} paramMediatorProvider
8
8
  */
9
- constructor(params) {
10
- super();
9
+ constructor(params, paramMediatorProvider) {
10
+ super(paramMediatorProvider);
11
+
11
12
  this.params = params;
12
13
 
13
- /** @type {(datum: any) => boolean} */
14
+ /** @type {import("../../view/paramMediator.js").ExprRefFunction} */
14
15
  this.predicate = undefined;
15
16
  }
16
17
 
17
18
  initialize() {
18
- this.predicate = createFunction(
19
- this.params.expr,
20
- this.getGlobalObject()
21
- );
19
+ this.predicate = this.paramMediator.createExpression(this.params.expr);
20
+ this.predicate.addListener(() => this.repropagate());
22
21
  }
23
22
 
24
23
  /**
@@ -1,7 +1,9 @@
1
1
  import { expect, test } from "vitest";
2
- import { processData } from "../flowTestUtils.js";
2
+ import { makeParamMediatorProvider, processData } from "../flowTestUtils.js";
3
3
  import FilterTransform from "./filter.js";
4
4
 
5
+ test.todo("Implement stub for ParamMediator");
6
+
5
7
  test("FilterTransform filter rows", () => {
6
8
  const data = [1, 2, 3, 4, 5, 6].map((x) => ({ x }));
7
9
 
@@ -11,7 +13,7 @@ test("FilterTransform filter rows", () => {
11
13
  expr: "datum.x > 3 && datum.x != 5",
12
14
  };
13
15
 
14
- const t = new FilterTransform(filterParams);
16
+ const t = new FilterTransform(filterParams, makeParamMediatorProvider());
15
17
  t.initialize();
16
18
 
17
19
  expect(processData(t, data)).toEqual([4, 6].map((x) => ({ x })));
@@ -2,12 +2,13 @@ export default class FormulaTransform extends FlowNode {
2
2
  /**
3
3
  *
4
4
  * @param {import("../../spec/transform.js").FormulaParams} params
5
+ * @param {import("../flowNode.js").ParamMediatorProvider} paramMediatorProvider
5
6
  */
6
- constructor(params: import("../../spec/transform.js").FormulaParams);
7
+ constructor(params: import("../../spec/transform.js").FormulaParams, paramMediatorProvider: import("../flowNode.js").ParamMediatorProvider);
7
8
  params: import("../../spec/transform.js").FormulaParams;
8
9
  as: string;
9
- /** @type {(datum: any) => any} */
10
- fn: (datum: any) => any;
10
+ /** @type {import("../../view/paramMediator.js").ExprRefFunction} */
11
+ fn: import("../../view/paramMediator.js").ExprRefFunction;
11
12
  }
12
13
  import FlowNode from "../flowNode.js";
13
14
  //# sourceMappingURL=formula.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"formula.d.ts","sourceRoot":"","sources":["../../../../src/data/transforms/formula.js"],"names":[],"mappings":"AAGA;IAKI;;;OAGG;IACH,oBAFW,OAAO,yBAAyB,EAAE,aAAa,EAUzD;IANG,wDAAoB;IAEpB,WAAmB;IAEnB,kCAAkC;IAClC,YADmB,GAAG,KAAK,GAAG,CACX;CAe1B;qBAjC2C,gBAAgB"}
1
+ {"version":3,"file":"formula.d.ts","sourceRoot":"","sources":["../../../../src/data/transforms/formula.js"],"names":[],"mappings":"AAEA;IAKI;;;;OAIG;IACH,oBAHW,OAAO,yBAAyB,EAAE,aAAa,yBAC/C,OAAO,gBAAgB,EAAE,qBAAqB,EAWxD;IANG,wDAAoB;IAEpB,WAAmB;IAEnB,oEAAoE;IACpE,IADW,OAAO,6BAA6B,EAAE,eAAe,CAC7C;CAgB1B;qBApC2C,gBAAgB"}
@@ -1,4 +1,3 @@
1
- import createFunction from "../../utils/expression.js";
2
1
  import FlowNode, { BEHAVIOR_MODIFIES } from "../flowNode.js";
3
2
 
4
3
  export default class FormulaTransform extends FlowNode {
@@ -9,19 +8,22 @@ export default class FormulaTransform extends FlowNode {
9
8
  /**
10
9
  *
11
10
  * @param {import("../../spec/transform.js").FormulaParams} params
11
+ * @param {import("../flowNode.js").ParamMediatorProvider} paramMediatorProvider
12
12
  */
13
- constructor(params) {
14
- super();
13
+ constructor(params, paramMediatorProvider) {
14
+ super(paramMediatorProvider);
15
+
15
16
  this.params = params;
16
17
 
17
18
  this.as = params.as;
18
19
 
19
- /** @type {(datum: any) => any} */
20
+ /** @type {import("../../view/paramMediator.js").ExprRefFunction} */
20
21
  this.fn = undefined;
21
22
  }
22
23
 
23
24
  initialize() {
24
- this.fn = createFunction(this.params.expr, this.getGlobalObject());
25
+ this.fn = this.paramMediator.createExpression(this.params.expr);
26
+ this.fn.addListener(() => this.repropagate());
25
27
  }
26
28
 
27
29
  /**
@@ -1,15 +1,21 @@
1
- import { expect, test } from "vitest";
2
- import { processData } from "../flowTestUtils.js";
1
+ import { expect, test, test } from "vitest";
2
+ import { makeParamMediatorProvider, processData } from "../flowTestUtils.js";
3
3
  import FormulaTransform from "./formula.js";
4
4
 
5
+ test.todo("Implement stub for ParamMediator");
6
+
5
7
  test("FormulaTransform", () => {
6
8
  const data = [{ a: 2 }, { a: 3 }];
7
9
 
8
- const t = new FormulaTransform({
9
- type: "formula",
10
- expr: "datum.a * 2",
11
- as: "b",
12
- });
10
+ const t = new FormulaTransform(
11
+ {
12
+ type: "formula",
13
+ expr: "datum.a * 2",
14
+ as: "b",
15
+ },
16
+ makeParamMediatorProvider()
17
+ );
18
+
13
19
  t.initialize();
14
20
 
15
21
  expect(processData(t, data)).toEqual([
@@ -73,9 +73,12 @@ export default class GenomeSpy {
73
73
  /**
74
74
  * Views that are currently loading data using lazy sources.
75
75
  *
76
- * @type {Map<View, boolean>}
76
+ * @type {Map<View, { status: import("./types/viewContext.js").DataLoadingStatus, detail?: string }>}
77
77
  */
78
- _loadingViews: Map<import("./view/view.js").default, boolean>;
78
+ _loadingViews: Map<import("./view/view.js").default, {
79
+ status: import("./types/viewContext.js").DataLoadingStatus;
80
+ detail?: string;
81
+ }>;
79
82
  /**
80
83
  * @type {HTMLElement}
81
84
  */
@@ -1 +1 @@
1
- {"version":3,"file":"genomeSpy.d.ts","sourceRoot":"","sources":["../../src/genomeSpy.js"],"names":[],"mappings":"AA8CA;IACI;;;;;OAKG;IAEH;;;;;OAKG;IACH,uBAJW,WAAW,qDAEX,OAAO,qBAAqB,EAAE,YAAY,EA2FpD;IAxFG,uBAA0B;IAC1B,oDAAsB;IAItB,6BAA6B;IAC7B,uBADW,CAAC,MAAM,IAAI,CAAC,EAAE,CACM;IAE/B,sCAAsC;IACtC,wCAAgB;IAEhB,iCAA4C;IAC5C,yBAAoC;IAEpC,4CAA4C;IAC5C,oBADW,QAAU,MAAM,KAAE,MAAM,EAAE,CAAC,EAAE,CACZ;IAE5B,mBAAoD;IAEpD,0BAA0B;IAC1B,aADW,WAAW,CACM;IAE5B;;;;;OAKG;IACH,qEAF0B,OAAO,CAE8B;IAE/D,2CAA2C;IAC3C,mBADW,4BAA4B,CACL;IAClC,2CAA2C;IAC3C,iBADW,4BAA4B,CACP;IAEhC,oDAAoD;IACpD,6BAAgC;IAEhC;;;OAGG;IACH,eAFU;QAAE,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,oBAAoB,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAEpF;IAE9B,uBAA+C;IAE/C;;;OAGG;IACH,oBAFU,IAAI,MAAM,EAAE,QAAU,aAAa,KAAE,IAAI,CAAC,EAAE,CAAC,CAEpB;IAEnC;;;;;;OAMG;IACH,yCAFkC,GAAG,KAAK,IAAI,GAEd;IAEhC;;;OAGG;IACH,kDAFkC,GAAG,KAAK,IAAI,GAEL;IAEzC,oFAAoF;IACpF,iBADW,OAAO,MAAM,EAAE,OAAO,6BAA6B,EAAE,cAAc,CAAC,CAK9E;IAED,mBAAmB;IACnB,2CAAyB;IAEzB;;;;OAIG;IACH,8DAA8B;IAE9B;;OAEG;IACH,wBAFU,WAAW,CAEkB;IA2C3C;;;OAGG;IACH,2CAFkB,MAAM,KAAK,GAAG,EAAE,QAIjC;IAED;;OAEG;IACH,+BAFW,MAAM,YAShB;IAED;;;;OAIG;IACH,sBAHW,MAAM,QACN,GAAG,EAAE,QAaf;IAED;;;;;OAKG;IACH,gBAHW,kBAAkB,YAClB,GAAG,QAQb;IAED;;;OAGG;IACH,iCAyCC;IA0DG,uBAOC;IAGD,mCAGE;IAOF,sCAEE;IAGF,iBAAyC;IAW7C;;OAEG;IACH,gBAuBC;IAED,sCA8MC;IAED;;;OAGG;IACH,UAFa,QAAQ,OAAO,CAAC,CA6B5B;IAED,4BA6IC;IAjIe,iCAAoC;IAmIpD;;;OAGG;IACH,kBAHW,MAAM,KACN,MAAM,QAiEhB;IAED;;;;;;;OAOG;IACH,oDAHuB,QAAQ,MAAM,GAAG,WAAW,GAAG,OAAO,KAAK,EAAE,cAAc,CAAC,QAYlF;IAED,sBAyCC;IAED,kBAIC;IAED,iCAOC;IAED,iCASC;IAED,qFAWC;;CACJ;;;;iCA15BY,eAAe,GAAG,YAAY,GAAG,QAAQ,GAAG,gBAAgB;4BAjC7C,uBAAuB;4BA2BP,uBAAuB;qBAZ9C,qBAAqB;wBAIlB,yBAAyB;yCARR,yDAAyD;oBAYvD,oBAAoB;wBAdvC,qBAAqB;oBAXzB,uBAAuB;qBAStB,oBAAoB"}
1
+ {"version":3,"file":"genomeSpy.d.ts","sourceRoot":"","sources":["../../src/genomeSpy.js"],"names":[],"mappings":"AA8CA;IACI;;;;;OAKG;IAEH;;;;;OAKG;IACH,uBAJW,WAAW,qDAEX,OAAO,qBAAqB,EAAE,YAAY,EA2FpD;IAxFG,uBAA0B;IAC1B,oDAAsB;IAItB,6BAA6B;IAC7B,uBADW,CAAC,MAAM,IAAI,CAAC,EAAE,CACM;IAE/B,sCAAsC;IACtC,wCAAgB;IAEhB,iCAA4C;IAC5C,yBAAoC;IAEpC,4CAA4C;IAC5C,oBADW,QAAU,MAAM,KAAE,MAAM,EAAE,CAAC,EAAE,CACZ;IAE5B,mBAAoD;IAEpD,0BAA0B;IAC1B,aADW,WAAW,CACM;IAE5B;;;;;OAKG;IACH,qEAF0B,OAAO,CAE8B;IAE/D,2CAA2C;IAC3C,mBADW,4BAA4B,CACL;IAClC,2CAA2C;IAC3C,iBADW,4BAA4B,CACP;IAEhC,oDAAoD;IACpD,6BAAgC;IAEhC;;;OAGG;IACH,eAFU;QAAE,IAAI,EAAE,OAAO,iBAAiB,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,oBAAoB,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAEpF;IAE9B,uBAA+C;IAE/C;;;OAGG;IACH,oBAFU,IAAI,MAAM,EAAE,QAAU,aAAa,KAAE,IAAI,CAAC,EAAE,CAAC,CAEpB;IAEnC;;;;;;OAMG;IACH,yCAFkC,GAAG,KAAK,IAAI,GAEd;IAEhC;;;OAGG;IACH,kDAFkC,GAAG,KAAK,IAAI,GAEL;IAEzC,oFAAoF;IACpF,iBADW,OAAO,MAAM,EAAE,OAAO,6BAA6B,EAAE,cAAc,CAAC,CAK9E;IAED,mBAAmB;IACnB,2CAAyB;IAEzB;;;;OAIG;IACH;gBAF8B,OAAO,wBAAwB,EAAE,iBAAiB;iBAAW,MAAM;OAEnE;IAE9B;;OAEG;IACH,wBAFU,WAAW,CAEkB;IA2C3C;;;OAGG;IACH,2CAFkB,MAAM,KAAK,GAAG,EAAE,QAIjC;IAED;;OAEG;IACH,+BAFW,MAAM,YAShB;IAED;;;;OAIG;IACH,sBAHW,MAAM,QACN,GAAG,EAAE,QAaf;IAED;;;;;OAKG;IACH,gBAHW,kBAAkB,YAClB,GAAG,QAQb;IAED;;;OAGG;IACH,iCAqDC;IA0DG,uBAOC;IAGD,mCAGE;IAOF,sCAEE;IAGF,iBAAyC;IAW7C;;OAEG;IACH,gBAuBC;IAED,sCA8MC;IAED;;;OAGG;IACH,UAFa,QAAQ,OAAO,CAAC,CA6B5B;IAED,4BA6IC;IAjIe,iCAAoC;IAmIpD;;;OAGG;IACH,kBAHW,MAAM,KACN,MAAM,QAiEhB;IAED;;;;;;;OAOG;IACH,oDAHuB,QAAQ,MAAM,GAAG,WAAW,GAAG,OAAO,KAAK,EAAE,cAAc,CAAC,QAYlF;IAED,sBA6CC;IAED,kBAIC;IAED,iCAOC;IAED,iCASC;IAED,qFAWC;;CACJ;;;;iCA16BY,eAAe,GAAG,YAAY,GAAG,QAAQ,GAAG,gBAAgB;4BAjC7C,uBAAuB;4BA2BP,uBAAuB;qBAZ9C,qBAAqB;wBAIlB,yBAAyB;yCARR,yDAAyD;oBAYvD,oBAAoB;wBAdvC,qBAAqB;oBAXzB,uBAAuB;qBAStB,oBAAoB"}
@@ -1,5 +1,5 @@
1
1
  import { formats as vegaFormats } from "vega-loader";
2
- import { html, render } from "lit";
2
+ import { html, nothing, render } from "lit";
3
3
  import { styleMap } from "lit/directives/style-map.js";
4
4
  import SPINNER from "./img/90-ring-with-bg.svg";
5
5
 
@@ -139,7 +139,7 @@ export default class GenomeSpy {
139
139
  /**
140
140
  * Views that are currently loading data using lazy sources.
141
141
  *
142
- * @type {Map<View, boolean>}
142
+ * @type {Map<View, { status: import("./types/viewContext.js").DataLoadingStatus, detail?: string }>}
143
143
  */
144
144
  this._loadingViews = new Map();
145
145
 
@@ -250,7 +250,9 @@ export default class GenomeSpy {
250
250
  const indicators = [];
251
251
 
252
252
  const isSomethingVisible = () =>
253
- [...this._loadingViews.values()].some((v) => v);
253
+ [...this._loadingViews.values()].some(
254
+ (v) => v.status == "loading" || v.status == "error"
255
+ );
254
256
 
255
257
  for (const [view, status] of this._loadingViews) {
256
258
  const c = view.coords;
@@ -263,9 +265,18 @@ export default class GenomeSpy {
263
265
  };
264
266
  indicators.push(
265
267
  html`<div style=${styleMap(style)}>
266
- <div class=${status ? "loading" : ""}>
267
- <img src="${SPINNER}" alt="" />
268
- <span>Loading...</span>
268
+ <div class=${status.status}>
269
+ ${status.status == "error"
270
+ ? html`<span
271
+ >Loading
272
+ failed${status.detail
273
+ ? html`: ${status.detail}`
274
+ : nothing}</span
275
+ >`
276
+ : html`
277
+ <img src="${SPINNER}" alt="" />
278
+ <span>Loading...</span>
279
+ `}
269
280
  </div>
270
281
  </div>`
271
282
  );
@@ -274,6 +285,7 @@ export default class GenomeSpy {
274
285
 
275
286
  // Do some hacks to stop css animations of the loading indicators.
276
287
  // Otherwise they fire animation frames even when their opacity is zero.
288
+ // TODO: Instead of this, replace the animated spinners with static images.
277
289
  if (isSomethingVisible()) {
278
290
  this.loadingIndicatorsElement.style.display = "block";
279
291
  } else {
@@ -437,8 +449,8 @@ export default class GenomeSpy {
437
449
  getNamedDataFromProvider: this.getNamedDataFromProvider.bind(this),
438
450
  getCurrentHover: () => this._currentHover,
439
451
 
440
- setDataLoadingStatus: (view, status) => {
441
- this._loadingViews.set(view, status);
452
+ setDataLoadingStatus: (view, status, detail) => {
453
+ this._loadingViews.set(view, { status, detail });
442
454
  this._updateLoadingIndicators();
443
455
  },
444
456
 
@@ -920,6 +932,10 @@ export default class GenomeSpy {
920
932
  Rectangle.create(0, 0, canvasSize.width, canvasSize.height)
921
933
  );
922
934
 
935
+ // The view coordinates may have not been known during the initial data loading.
936
+ // Thus, update them so that possible error messages are shown in the correct place.
937
+ this._updateLoadingIndicators();
938
+
923
939
  this.broadcast("layoutComputed");
924
940
  }
925
941
 
@@ -1 +1 @@
1
- {"version":3,"file":"mark.d.ts","sourceRoot":"","sources":["../../../src/marks/mark.js"],"names":[],"mappings":";AA4CA,0DAA2D;AAC3D,0DAA2D;AAE3D;;;;;;;;;;;;GAYG;AACH;IAgBI;;OAEG;IACH,sBAFW,OAAO,qBAAqB,EAAE,OAAO,EAkF/C;IA/EG,gDAAwB;IAExB,oEAAoE;IACpE,UADW,OAAO,MAAM,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAC,CACvC;IAIzB,0FAA0F;IAC1F,YADW,OAAO,SAAS,EAAE,UAAU,GAAG;QAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE,CAC7C;IAE3B,0DAA0D;IAC1D,aADW,OAAO,SAAS,EAAE,WAAW,CACZ;IAE5B,8DAA8D;IAC9D,iBADW,OAAO,SAAS,EAAE,eAAe,CACZ;IAEhC,+DAA+D;IAC/D,mBADW,OAAO,SAAS,EAAE,gBAAgB,CACX;IAElC,+DAA+D;IAC/D,iBADW,OAAO,SAAS,EAAE,gBAAgB,CACb;IAEhC;;;OAGG;IACH,iBAFU,OAAO,SAAS,EAAE,gBAAgB,CAEZ;IAEhC;;;;;OAKG;IACH,uCAA+B;IAE/B,kFAAkF;IAClF,UADW,SAAS,GAAG,CAAC,CACM;IAG9B,wBAAwB;IACxB,uDAqBC;IAED;;;;;;;;OAQG;IACH,kEAKC;IAGL,sBAEC;IAED;;;;;;;OAOG;IACH,iBAFa,MAAM,EAAE,CAKpB;IAED;;OAEG;IACH,+DAWC;IAED;;OAEG;IACH,oEAcC;IAED;;;;;OAKG;IACH,oHAEC;IAED;;;;;;OAMG;IACH,yGAkCC;IAED;;;;OAIG;IACH,8DAiDC;IAED,wDAEC;IAED,8CAEC;IAED,uBAEC;IAED;;;OAGG;IACH,2BAEC;IAED;;OAEG;IACH,oCAEC;IAED;;OAEG;IACH,2BAEC;IAED,sEAeC;IAED;;;;;;OAMG;IAEH,6CANW,MAAM,kBACN,MAAM,iBACN,MAAM,EAAE,QA8NlB;IALG;;;;;;MAIC;IAGL;;;;;;OAMG;IACH,uCA6CC;IAED;;;;;;OAMG;IACH,+CAHW,MAAM,UACK,GAAG,KAAE,IAAI,CAe9B;IAED;;;;;;;;;;OAUG;IACH,mDAJW,MAAM,sFAEsB,GAAG,QA6BzC;IAED;;OAEG;IACH,2BAiBC;IAED;;;OAGG;IACH,6BAFW,GAAG,QAoCb;IAED,yBAAyB;IACzB,uDAEC;IAED,yBAAyB;IACzB,iCAEC;IAED,gCAEC;IAED,+BAEC;IAED,yCAEC;IAED;;;;;OAKG;IACH,gCAcC;IAED;;OAEG;IACH,4CAOC;IAED;;;;;;;;OAQG;IAEH,uBAJW,OAAO,uBAAuB,EAAE,sBAAsB,GACpD,CAAC,MAAM,IAAI,CAAC,EAAE,CAsF1B;IAED;;;;;;OAMG;IACH,qCAJW,oBAAoB,GAClB,OAAO,CAqCnB;IAED;;;;;;;OAOG;IACH,gBAJW,oBAAoB,SACP,IAAI,CAM3B;IAED;;;OAGG;IACH,2BAHW,YAAY,WACZ,OAAO,WAAW,EAAE,oBAAoB,cAiElD;IAED;;;;;;OAMG;IACH,oBAJW,OAAO,6BAA6B,EAAE,OAAO,aAC7C,OAAO,6BAA6B,EAAE,OAAO,GAC3C,OAAO,CAuHnB;IAED;;;;;;;;;OASG;IACH,qBAJW,MAAM,KACN,OAAO,oBAAoB,EAAE,MAAM,GACjC,GAAG,CAIf;;CACJ;+BAznCY,OAAO,uBAAuB,EAAE,gBAAgB;;;;;;wBAEnD,OAAO;;mCAEJ,gBAAgB,GAAG,qBAAqB;oCAG1C,MAAM,SACN,MAAM;AAmnCjB;;;GAGG;AACH;IACI,cAEC;IAkBD;;;OAGG;IACH,2BAFW,IAAI,CAAC,EAAE,OAAO,yBAAyB,EAAE,UAAU,CAAC,QAM9D;CACJ;0BAtqCyB,WAAW"}
1
+ {"version":3,"file":"mark.d.ts","sourceRoot":"","sources":["../../../src/marks/mark.js"],"names":[],"mappings":";AA4CA,0DAA2D;AAC3D,0DAA2D;AAE3D;;;;;;;;;;;;GAYG;AACH;IAgBI;;OAEG;IACH,sBAFW,OAAO,qBAAqB,EAAE,OAAO,EAkF/C;IA/EG,gDAAwB;IAExB,oEAAoE;IACpE,UADW,OAAO,MAAM,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAC,CACvC;IAIzB,0FAA0F;IAC1F,YADW,OAAO,SAAS,EAAE,UAAU,GAAG;QAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;KAAE,CAC7C;IAE3B,0DAA0D;IAC1D,aADW,OAAO,SAAS,EAAE,WAAW,CACZ;IAE5B,8DAA8D;IAC9D,iBADW,OAAO,SAAS,EAAE,eAAe,CACZ;IAEhC,+DAA+D;IAC/D,mBADW,OAAO,SAAS,EAAE,gBAAgB,CACX;IAElC,+DAA+D;IAC/D,iBADW,OAAO,SAAS,EAAE,gBAAgB,CACb;IAEhC;;;OAGG;IACH,iBAFU,OAAO,SAAS,EAAE,gBAAgB,CAEZ;IAEhC;;;;;OAKG;IACH,uCAA+B;IAE/B,kFAAkF;IAClF,UADW,SAAS,GAAG,CAAC,CACM;IAG9B,wBAAwB;IACxB,uDAqBC;IAED;;;;;;;;OAQG;IACH,kEAKC;IAGL,sBAEC;IAED;;;;;;;OAOG;IACH,iBAFa,MAAM,EAAE,CAKpB;IAED;;OAEG;IACH,+DAWC;IAED;;OAEG;IACH,oEAcC;IAED;;;;;OAKG;IACH,oHAEC;IAED;;;;;;OAMG;IACH,yGAkCC;IAED;;;;OAIG;IACH,8DAiDC;IAED,wDAEC;IAED,8CAEC;IAED,uBAEC;IAED;;;OAGG;IACH,2BAEC;IAED;;OAEG;IACH,oCAEC;IAED;;OAEG;IACH,2BAEC;IAED,sEAeC;IAED;;;;;;OAMG;IAEH,6CANW,MAAM,kBACN,MAAM,iBACN,MAAM,EAAE,QA8NlB;IALG;;;;;;MAIC;IAGL;;;;;;OAMG;IACH,uCA6CC;IAED;;;;;;OAMG;IACH,+CAHW,MAAM,UACK,GAAG,KAAE,IAAI,CAe9B;IAED;;;;;;;;;;OAUG;IACH,mDAJW,MAAM,sFAEsB,GAAG,QA6BzC;IAED;;OAEG;IACH,2BAiBC;IAED;;;OAGG;IACH,6BAFW,GAAG,QAoCb;IAED,yBAAyB;IACzB,uDAEC;IAED,yBAAyB;IACzB,iCAEC;IAED,gCAEC;IAED,+BAEC;IAED,yCAEC;IAED;;;;;OAKG;IACH,gCAcC;IAED;;OAEG;IACH,4CAOC;IAED;;;;;;;;OAQG;IAEH,uBAJW,OAAO,uBAAuB,EAAE,sBAAsB,GACpD,CAAC,MAAM,IAAI,CAAC,EAAE,CAsF1B;IAED;;;;;;OAMG;IACH,qCAJW,oBAAoB,GAClB,OAAO,CAqCnB;IAED;;;;;;;OAOG;IACH,gBAJW,oBAAoB,SACP,IAAI,CAM3B;IAED;;;OAGG;IACH,2BAHW,YAAY,WACZ,OAAO,WAAW,EAAE,oBAAoB,cAiElD;IAED;;;;;;OAMG;IACH,oBAJW,OAAO,6BAA6B,EAAE,OAAO,aAC7C,OAAO,6BAA6B,EAAE,OAAO,GAC3C,OAAO,CAuHnB;IAED;;;;;;;;;OASG;IACH,qBAJW,MAAM,KACN,OAAO,oBAAoB,EAAE,MAAM,GACjC,GAAG,CAIf;;CACJ;+BAznCY,OAAO,uBAAuB,EAAE,gBAAgB;;;;;;wBAEnD,OAAO;;mCAEJ,gBAAgB,GAAG,qBAAqB;oCAG1C,MAAM,SACN,MAAM;AAmnCjB;;;GAGG;AACH;IACI,cAEC;IAkBD;;;OAGG;IACH,2BAFW,IAAI,CAAC,EAAE,OAAO,yBAAyB,EAAE,UAAU,CAAC,QAiB9D;CACJ;0BAjrCyB,WAAW"}
@@ -1224,6 +1224,17 @@ class RangeMap extends InternMap {
1224
1224
  * @param {Map<K, import("../gl/dataToVertices.js").RangeEntry>} anotherMap
1225
1225
  */
1226
1226
  migrateEntries(anotherMap) {
1227
+ for (const [key, value] of this.entries()) {
1228
+ // Buffered draw calls maintain direct references to the range entries.
1229
+ // Thus, they cannot just be deleted, but instead, their counts and offsets
1230
+ // must be zeroed.
1231
+ if (!anotherMap.has(key)) {
1232
+ value.offset = 0;
1233
+ value.count = 0;
1234
+ value.xIndex = undefined;
1235
+ }
1236
+ }
1237
+
1227
1238
  for (const [key, value] of anotherMap.entries()) {
1228
1239
  Object.assign(this.get(key), value);
1229
1240
  }
@@ -124,6 +124,11 @@ export interface ViewSpecBase extends ResolveSpec {
124
124
  */
125
125
  description?: string | string[];
126
126
 
127
+ /**
128
+ * Optional base URL for constructing request URLs. When set, all views
129
+ * deeper in the hierarchy inherit this base URL, using it for importing
130
+ * loading data and importing specifications.
131
+ */
127
132
  baseUrl?: string;
128
133
 
129
134
  /**
@@ -151,6 +156,12 @@ export interface ViewSpecBase extends ResolveSpec {
151
156
  * **Default:** `false` for children of `layer`, `true` for others.
152
157
  */
153
158
  configurableVisibility?: boolean;
159
+
160
+ /**
161
+ * Templates that can be reused within the view specification by importing
162
+ * them with the template key.
163
+ */
164
+ templates?: Record<string, ViewSpec>;
154
165
  }
155
166
 
156
167
  export interface UnitSpec extends ViewSpecBase, AggregateSamplesSpec {
@@ -165,7 +176,7 @@ export interface AggregateSamplesSpec {
165
176
 
166
177
  export interface LayerSpec extends ViewSpecBase, AggregateSamplesSpec {
167
178
  view?: ViewBackground;
168
- layer: (LayerSpec | UnitSpec)[];
179
+ layer: (LayerSpec | UnitSpec | ImportSpec)[];
169
180
  }
170
181
 
171
182
  export interface FacetSpec extends ViewSpecBase {
@@ -218,39 +229,71 @@ export type ViewSpec =
218
229
  | ConcatSpec
219
230
  | SampleSpec;
220
231
 
221
- export interface ImportParams {
222
- url?: string;
232
+ export interface UrlImport {
233
+ /**
234
+ * Imports a specification from the specified URL.
235
+ */
236
+ url: string;
237
+ }
238
+
239
+ export interface TemplateImport {
240
+ /**
241
+ * Imports a specification from the current view hierarchy, searching
242
+ * first in the current view, then ascending through ancestors.
243
+ */
244
+ template: string;
245
+ }
223
246
 
247
+ export interface ImportSpec {
224
248
  /**
225
- * Name for the imported view. Overrides the name defined in the imported spec.
249
+ * The name given to the imported view. This property overrides the name
250
+ * specified in the imported specification.
226
251
  */
227
252
  name?: string;
228
253
 
229
254
  /**
230
- * Dynamic variables that parameterize a visualization. Parameters defined here
231
- * override the parameters defined in the imported spec.
255
+ * Dynamic variables that parameterize a visualization. Parameters defined
256
+ * here override the parameters defined in the imported specification.
232
257
  */
233
258
  params?: VariableParameter[] | Record<string, any>;
234
- }
235
259
 
236
- export interface ImportSpec {
237
- import: ImportParams;
260
+ /**
261
+ * The method to import a specification.
262
+ */
263
+ import: UrlImport | TemplateImport;
238
264
  }
239
265
 
240
266
  export interface ConcatBase extends ViewSpecBase {
267
+ /**
268
+ * The gap between the views, in pixels.
269
+ */
241
270
  spacing?: number;
242
271
  }
243
272
 
244
273
  export interface VConcatSpec extends ConcatBase {
274
+ /**
275
+ * Specifies views that will be concatenated vertically.
276
+ */
245
277
  vconcat: (ViewSpec | ImportSpec)[];
246
278
  }
247
279
 
248
280
  export interface HConcatSpec extends ConcatBase {
281
+ /**
282
+ * Specifies views that will be concatenated horizontally.
283
+ */
249
284
  hconcat: (ViewSpec | ImportSpec)[];
250
285
  }
251
286
 
252
287
  export interface ConcatSpec extends ConcatBase {
288
+ /**
289
+ * Specifies views that will be concatenated into a grid that wraps when
290
+ * the specified number of columns are used.
291
+ */
253
292
  concat: (ViewSpec | ImportSpec)[];
293
+
294
+ /**
295
+ * The number of columns in the grid.
296
+ */
254
297
  columns: number;
255
298
  }
256
299
 
@@ -1,3 +1,3 @@
1
1
  export default css;
2
- declare const css: "\n.genome-spy {\n font-family: system-ui, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n position: relative;\n display: flex;\n flex-direction: column;\n}\n.genome-spy .canvas-wrapper {\n position: relative;\n flex-grow: 1;\n overflow: hidden;\n}\n.genome-spy canvas {\n transform: scale(1, 1);\n opacity: 1;\n transition: transform 0.6s, opacity 0.6s;\n}\n.genome-spy .loading-message {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-message .message {\n color: #666;\n opacity: 0;\n transition: opacity 0.7s;\n}\n.genome-spy .loading > canvas {\n transform: scale(0.95, 0.95);\n opacity: 0;\n}\n.genome-spy .loading > .loading-message .message {\n opacity: 1;\n}\n.genome-spy .loading > .loading-message .message .ellipsis {\n animation: blinker 1s linear infinite;\n}\n@keyframes blinker {\n 50% {\n opacity: 0;\n }\n}\n.genome-spy .loading-indicators {\n position: absolute;\n inset: 0;\n user-select: none;\n pointer-events: none;\n}\n.genome-spy .loading-indicators div {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-indicators div > div {\n font-size: 11px;\n transition: opacity 0.2s;\n background: white;\n padding: 2px 5px;\n display: flex;\n border-radius: 3px;\n gap: 0.5em;\n opacity: 0;\n}\n.genome-spy .loading-indicators div > div.loading {\n opacity: 0.5;\n}\n.genome-spy .loading-indicators div > div > * {\n display: block;\n}\n.genome-spy .loading-indicators div > div img {\n width: 1.5em;\n height: 1.5em;\n}\n.genome-spy .tooltip {\n position: absolute;\n max-width: 450px;\n overflow: hidden;\n background: #f6f6f6;\n padding: 10px;\n font-size: 13px;\n box-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);\n pointer-events: none;\n z-index: 100;\n}\n.genome-spy .tooltip > :last-child {\n margin-bottom: 0;\n}\n.genome-spy .tooltip > .title {\n padding-bottom: 5px;\n margin-bottom: 5px;\n border-bottom: 1px dashed #b6b6b6;\n}\n.genome-spy .tooltip .summary {\n font-size: 12px;\n}\n.genome-spy .tooltip table {\n border-collapse: collapse;\n}\n.genome-spy .tooltip table:first-child {\n margin-top: 0;\n}\n.genome-spy .tooltip table th,\n.genome-spy .tooltip table td {\n padding: 2px 0.4em;\n vertical-align: top;\n}\n.genome-spy .tooltip table th:first-child,\n.genome-spy .tooltip table td:first-child {\n padding-left: 0;\n}\n.genome-spy .tooltip table th {\n text-align: left;\n font-weight: bold;\n}\n.genome-spy .tooltip .color-legend {\n display: inline-block;\n width: 0.8em;\n height: 0.8em;\n margin-left: 0.4em;\n box-shadow: 0px 0px 3px 1px white;\n}\n.genome-spy .tooltip .attributes .hovered {\n background-color: #e0e0e0;\n}\n.genome-spy .tooltip .na {\n color: #aaa;\n font-style: italic;\n font-size: 80%;\n}\n.genome-spy .gene-track-tooltip .summary {\n font-size: 90%;\n}\n.genome-spy .message-box {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 0;\n height: 100%;\n width: 100%;\n}\n.genome-spy .message-box > div {\n border: 1px solid red;\n padding: 10px;\n background: #fff0f0;\n}\n\n.gs-input-binding {\n display: grid;\n grid-template-columns: max-content max-content;\n column-gap: 1em;\n row-gap: 0.3em;\n justify-items: start;\n}\n.gs-input-binding > select,\n.gs-input-binding > input:not([type=checkbox]) {\n width: 100%;\n}\n.gs-input-binding input[type=range] + span {\n display: inline-block;\n margin-left: 0.3em;\n min-width: 2.2em;\n font-variant-numeric: tabular-nums;\n}\n.gs-input-binding input[type=range],\n.gs-input-binding input[type=radio] {\n vertical-align: text-bottom;\n}\n.gs-input-binding .radio-group {\n display: flex;\n align-items: center;\n}\n.gs-input-binding .description {\n max-width: 26em;\n grid-column: 1/-1;\n color: #777;\n font-size: 90%;\n margin-top: -0.5em;\n}\n\n.gs-input-bindings {\n flex-basis: content;\n font-size: 14px;\n padding: 10px;\n}\n";
2
+ declare const css: "\n.genome-spy {\n font-family: system-ui, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n position: relative;\n display: flex;\n flex-direction: column;\n}\n.genome-spy .canvas-wrapper {\n position: relative;\n flex-grow: 1;\n overflow: hidden;\n}\n.genome-spy canvas {\n transform: scale(1, 1);\n opacity: 1;\n transition: transform 0.6s, opacity 0.6s;\n}\n.genome-spy .loading-message {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-message .message {\n color: #666;\n opacity: 0;\n transition: opacity 0.7s;\n}\n.genome-spy .loading > canvas {\n transform: scale(0.95, 0.95);\n opacity: 0;\n}\n.genome-spy .loading > .loading-message .message {\n opacity: 1;\n}\n.genome-spy .loading > .loading-message .message .ellipsis {\n animation: blinker 1s linear infinite;\n}\n@keyframes blinker {\n 50% {\n opacity: 0;\n }\n}\n.genome-spy .loading-indicators {\n position: absolute;\n inset: 0;\n user-select: none;\n pointer-events: none;\n}\n.genome-spy .loading-indicators div {\n position: absolute;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.genome-spy .loading-indicators div > div {\n font-size: 11px;\n transition: opacity 0.2s;\n background: white;\n padding: 2px 5px;\n display: flex;\n border-radius: 3px;\n gap: 0.5em;\n opacity: 0;\n}\n.genome-spy .loading-indicators div > div.loading {\n opacity: 0.5;\n}\n.genome-spy .loading-indicators div > div.error {\n opacity: 0.8;\n color: firebrick;\n}\n.genome-spy .loading-indicators div > div > * {\n display: block;\n}\n.genome-spy .loading-indicators div > div img {\n width: 1.5em;\n height: 1.5em;\n}\n.genome-spy .tooltip {\n position: absolute;\n max-width: 450px;\n overflow: hidden;\n background: #f6f6f6;\n padding: 10px;\n font-size: 13px;\n box-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);\n pointer-events: none;\n z-index: 100;\n}\n.genome-spy .tooltip > :last-child {\n margin-bottom: 0;\n}\n.genome-spy .tooltip > .title {\n padding-bottom: 5px;\n margin-bottom: 5px;\n border-bottom: 1px dashed #b6b6b6;\n}\n.genome-spy .tooltip .summary {\n font-size: 12px;\n}\n.genome-spy .tooltip table {\n border-collapse: collapse;\n}\n.genome-spy .tooltip table:first-child {\n margin-top: 0;\n}\n.genome-spy .tooltip table th,\n.genome-spy .tooltip table td {\n padding: 2px 0.4em;\n vertical-align: top;\n}\n.genome-spy .tooltip table th:first-child,\n.genome-spy .tooltip table td:first-child {\n padding-left: 0;\n}\n.genome-spy .tooltip table th {\n text-align: left;\n font-weight: bold;\n}\n.genome-spy .tooltip .color-legend {\n display: inline-block;\n width: 0.8em;\n height: 0.8em;\n margin-left: 0.4em;\n box-shadow: 0px 0px 3px 1px white;\n}\n.genome-spy .tooltip .attributes .hovered {\n background-color: #e0e0e0;\n}\n.genome-spy .tooltip .na {\n color: #aaa;\n font-style: italic;\n font-size: 80%;\n}\n.genome-spy .gene-track-tooltip .summary {\n font-size: 90%;\n}\n.genome-spy .message-box {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 0;\n height: 100%;\n width: 100%;\n}\n.genome-spy .message-box > div {\n border: 1px solid red;\n padding: 10px;\n background: #fff0f0;\n}\n\n.gs-input-binding {\n display: grid;\n grid-template-columns: max-content max-content;\n column-gap: 1em;\n row-gap: 0.3em;\n justify-items: start;\n}\n.gs-input-binding > select,\n.gs-input-binding > input:not([type=checkbox]) {\n width: 100%;\n}\n.gs-input-binding input[type=range] + span {\n display: inline-block;\n margin-left: 0.3em;\n min-width: 2.2em;\n font-variant-numeric: tabular-nums;\n}\n.gs-input-binding input[type=range],\n.gs-input-binding input[type=radio] {\n vertical-align: text-bottom;\n}\n.gs-input-binding .radio-group {\n display: flex;\n align-items: center;\n}\n.gs-input-binding .description {\n max-width: 26em;\n grid-column: 1/-1;\n color: #777;\n font-size: 90%;\n margin-top: -0.5em;\n}\n\n.gs-input-bindings {\n flex-basis: content;\n font-size: 14px;\n padding: 10px;\n}\n";
3
3
  //# sourceMappingURL=genome-spy.css.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"genome-spy.css.d.ts","sourceRoot":"","sources":["../../../src/styles/genome-spy.css.js"],"names":[],"mappings":";AAAA,6iIA4LE"}
1
+ {"version":3,"file":"genome-spy.css.d.ts","sourceRoot":"","sources":["../../../src/styles/genome-spy.css.js"],"names":[],"mappings":";AAAA,yoIAgME"}
@@ -67,6 +67,10 @@ const css = `
67
67
  .genome-spy .loading-indicators div > div.loading {
68
68
  opacity: 0.5;
69
69
  }
70
+ .genome-spy .loading-indicators div > div.error {
71
+ opacity: 0.8;
72
+ color: firebrick;
73
+ }
70
74
  .genome-spy .loading-indicators div > div > * {
71
75
  display: block;
72
76
  }
@@ -88,6 +88,11 @@ $font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
88
88
  opacity: 0.5;
89
89
  }
90
90
 
91
+ &.error {
92
+ opacity: 0.8;
93
+ color: firebrick;
94
+ }
95
+
91
96
  > * {
92
97
  display: block;
93
98
  }
@@ -18,6 +18,8 @@ export interface Hover {
18
18
  datum: Datum;
19
19
  }
20
20
 
21
+ export type DataLoadingStatus = "loading" | "complete" | "error";
22
+
21
23
  /**
22
24
  * ViewContext provides essential data and interfaces to View classes.
23
25
  */
@@ -74,9 +76,14 @@ export default interface ViewContext {
74
76
  * The status is shown in the UI somewhere within or near the view.
75
77
  *
76
78
  * @param view The view where the data source is located.
77
- * @param status true if loading, false if not loading.
79
+ * @param status
80
+ * @param detail Details about the error, if any.
78
81
  */
79
- setDataLoadingStatus: (view: View, status: boolean) => void;
82
+ setDataLoadingStatus: (
83
+ view: View,
84
+ status: DataLoadingStatus,
85
+ detail?: string
86
+ ) => void;
80
87
 
81
88
  /**
82
89
  * Returns true if the view is configured to be visible.
@@ -49,7 +49,7 @@ function buildFunctions(codegen) {
49
49
 
50
50
  const cg = codegenExpression({
51
51
  forbidden: [],
52
- allowed: ["datum"],
52
+ allowed: ["datum", "undefined"],
53
53
  globalvar: "globalObject",
54
54
  fieldvar: "datum",
55
55
  functions: buildFunctions,