@genome-spy/core 0.44.0 → 0.45.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 (96) hide show
  1. package/dist/bundle/index.es.js +6123 -5482
  2. package/dist/bundle/index.js +211 -148
  3. package/dist/schema.json +329 -79
  4. package/dist/src/data/collector.d.ts.map +1 -1
  5. package/dist/src/data/collector.js +4 -2
  6. package/dist/src/data/flowOptimizer.test.js +12 -3
  7. package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
  8. package/dist/src/data/sources/dataUtils.js +3 -1
  9. package/dist/src/data/sources/lazy/bigBedSource.d.ts +1 -1
  10. package/dist/src/data/sources/lazy/bigBedSource.d.ts.map +1 -1
  11. package/dist/src/data/sources/lazy/bigBedSource.js +52 -20
  12. package/dist/src/data/sources/lazy/bigWigSource.d.ts +0 -1
  13. package/dist/src/data/sources/lazy/bigWigSource.d.ts.map +1 -1
  14. package/dist/src/data/sources/lazy/bigWigSource.js +30 -4
  15. package/dist/src/data/sources/lazy/singleAxisLazySource.d.ts.map +1 -1
  16. package/dist/src/data/sources/lazy/singleAxisLazySource.js +0 -2
  17. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts +6 -2
  18. package/dist/src/data/sources/lazy/singleAxisWindowedSource.d.ts.map +1 -1
  19. package/dist/src/data/sources/lazy/singleAxisWindowedSource.js +38 -20
  20. package/dist/src/data/sources/sequenceSource.d.ts.map +1 -1
  21. package/dist/src/data/sources/sequenceSource.js +14 -5
  22. package/dist/src/data/sources/sequenceSource.test.js +23 -5
  23. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  24. package/dist/src/data/sources/urlSource.js +15 -2
  25. package/dist/src/data/transforms/aggregate.d.ts.map +1 -1
  26. package/dist/src/data/transforms/aggregate.js +5 -2
  27. package/dist/src/encoder/encoder.d.ts +2 -4
  28. package/dist/src/encoder/encoder.d.ts.map +1 -1
  29. package/dist/src/encoder/encoder.js +8 -8
  30. package/dist/src/encoder/encoder.test.js +3 -0
  31. package/dist/src/genomeSpy.d.ts +7 -5
  32. package/dist/src/genomeSpy.d.ts.map +1 -1
  33. package/dist/src/genomeSpy.js +109 -132
  34. package/dist/src/gl/glslScaleGenerator.js +1 -1
  35. package/dist/src/index.d.ts +1 -1
  36. package/dist/src/index.d.ts.map +1 -1
  37. package/dist/src/index.js +1 -1
  38. package/dist/src/marks/mark.d.ts +8 -5
  39. package/dist/src/marks/mark.d.ts.map +1 -1
  40. package/dist/src/marks/mark.js +56 -12
  41. package/dist/src/marks/point.common.glsl.js +1 -1
  42. package/dist/src/marks/point.d.ts +1 -4
  43. package/dist/src/marks/point.d.ts.map +1 -1
  44. package/dist/src/marks/point.js +31 -23
  45. package/dist/src/marks/point.vertex.glsl.js +1 -1
  46. package/dist/src/marks/text.d.ts.map +1 -1
  47. package/dist/src/marks/text.js +15 -7
  48. package/dist/src/spec/data.d.ts +11 -10
  49. package/dist/src/spec/mark.d.ts +11 -21
  50. package/dist/src/spec/parameter.d.ts +11 -7
  51. package/dist/src/spec/root.d.ts +0 -8
  52. package/dist/src/spec/title.d.ts +5 -4
  53. package/dist/src/spec/view.d.ts +19 -4
  54. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  55. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  56. package/dist/src/styles/genome-spy.css.js +52 -5
  57. package/dist/src/styles/genome-spy.scss +63 -10
  58. package/dist/src/styles/update.sh +6 -0
  59. package/dist/src/tooltip/dataTooltipHandler.js +1 -1
  60. package/dist/src/tooltip/refseqGeneTooltipHandler.js +1 -1
  61. package/dist/src/tooltip/tooltipHandler.d.ts +1 -1
  62. package/dist/src/tooltip/tooltipHandler.d.ts.map +1 -1
  63. package/dist/src/tooltip/tooltipHandler.ts +1 -1
  64. package/dist/src/types/embedApi.d.ts +6 -0
  65. package/dist/src/types/viewContext.d.ts +2 -3
  66. package/dist/src/utils/debounce.d.ts +2 -2
  67. package/dist/src/utils/debounce.d.ts.map +1 -1
  68. package/dist/src/utils/debounce.js +5 -2
  69. package/dist/src/utils/expression.d.ts +2 -2
  70. package/dist/src/utils/expression.d.ts.map +1 -1
  71. package/dist/src/utils/expression.js +1 -1
  72. package/dist/src/utils/formatObject.d.ts +2 -2
  73. package/dist/src/utils/formatObject.d.ts.map +1 -1
  74. package/dist/src/utils/formatObject.js +2 -2
  75. package/dist/src/utils/inputBinding.d.ts +5 -0
  76. package/dist/src/utils/inputBinding.d.ts.map +1 -0
  77. package/dist/src/utils/inputBinding.js +115 -0
  78. package/dist/src/utils/ui/tooltip.js +1 -1
  79. package/dist/src/view/paramMediator.d.ts +108 -0
  80. package/dist/src/view/paramMediator.d.ts.map +1 -0
  81. package/dist/src/view/paramMediator.js +337 -0
  82. package/dist/src/view/paramMediator.test.js +211 -0
  83. package/dist/src/view/scaleResolution.d.ts.map +1 -1
  84. package/dist/src/view/scaleResolution.js +11 -6
  85. package/dist/src/view/view.d.ts +4 -1
  86. package/dist/src/view/view.d.ts.map +1 -1
  87. package/dist/src/view/view.js +19 -5
  88. package/dist/src/view/viewFactory.d.ts.map +1 -1
  89. package/dist/src/view/viewFactory.js +45 -0
  90. package/dist/src/view/viewUtils.d.ts +5 -1
  91. package/dist/src/view/viewUtils.d.ts.map +1 -1
  92. package/dist/src/view/viewUtils.js +9 -4
  93. package/package.json +16 -17
  94. package/dist/src/paramBroker.d.ts +0 -46
  95. package/dist/src/paramBroker.d.ts.map +0 -1
  96. package/dist/src/paramBroker.js +0 -118
@@ -1,13 +1,11 @@
1
1
  import { isDiscrete } from "vega-scale";
2
2
  import createIndexer from "../utils/indexer.js";
3
3
  import scaleNull from "../utils/scaleNull.js";
4
- import { isExprRef } from "../marks/mark.js";
4
+ import { isExprRef } from "../view/paramMediator.js";
5
5
 
6
6
  /**
7
7
  * Creates an object that contains encoders for every channel of a mark
8
8
  *
9
- * TODO: This should actually receive the mark as parameter
10
- *
11
9
  * TODO: This method should have a test. But how to mock Mark...
12
10
  *
13
11
  * @param {import("../marks/mark.js").default} mark
@@ -41,6 +39,7 @@ export default function createEncoders(mark, encoding) {
41
39
  const resolution = mark.unitView.getScaleResolution(channelWithScale);
42
40
 
43
41
  encoders[channel] = createEncoder(
42
+ mark,
44
43
  encoding[channel],
45
44
  resolution?.scale,
46
45
  mark.unitView.getAccessor(channel),
@@ -52,14 +51,14 @@ export default function createEncoders(mark, encoding) {
52
51
  }
53
52
 
54
53
  /**
55
- *
54
+ * @param {import("../marks/mark.js").default} mark
56
55
  * @param {import("../spec/channel.js").ChannelDef} channelDef
57
56
  * @param {any} scale
58
57
  * @param {Accessor} accessor
59
58
  * @param {Channel} channel
60
59
  * @returns {Encoder}
61
60
  */
62
- export function createEncoder(channelDef, scale, accessor, channel) {
61
+ export function createEncoder(mark, channelDef, scale, accessor, channel) {
63
62
  /**
64
63
  * @typedef {import("../spec/channel.js").Channel} Channel
65
64
  * @typedef {import("../types/encoder.js").Encoder} Encoder
@@ -71,9 +70,10 @@ export function createEncoder(channelDef, scale, accessor, channel) {
71
70
 
72
71
  if (isValueDef(channelDef)) {
73
72
  if (isExprRef(channelDef.value)) {
74
- // TODO: Should get the value expression as the accessor
75
- encoder = /** @type {Encoder} */ ((datum) => undefined);
76
- //encoder = /** @type {Encoder} */ (/** @type {any} */ (accessor));
73
+ const fn = mark.unitView.paramMediator.createExpression(
74
+ channelDef.value.expr
75
+ );
76
+ encoder = /** @type {Encoder} */ ((datum) => fn(null));
77
77
  encoder.constant = true;
78
78
  encoder.constantValue = false;
79
79
  encoder.accessor = accessor;
@@ -29,6 +29,7 @@ describe("Encoder", () => {
29
29
  const encoders = {};
30
30
  for (const [channel, channelDef] of Object.entries(encoding)) {
31
31
  encoders[channel] = createEncoder(
32
+ null, // TODO: stub the mark
32
33
  channelDef,
33
34
  scales[channel],
34
35
  accessorFactory.createAccessor(encodingSpecs[channel]),
@@ -95,4 +96,6 @@ describe("Encoder", () => {
95
96
  });
96
97
 
97
98
  // TODO: Test indexer
99
+
100
+ // TODO: Text ExprRef
98
101
  });
@@ -13,6 +13,7 @@ export default class GenomeSpy {
13
13
  */
14
14
  constructor(container: HTMLElement, spec: import("./spec/root.js").RootSpec, options?: import("./types/embedApi.js").EmbedOptions);
15
15
  container: HTMLElement;
16
+ options: import("./types/embedApi.js").EmbedOptions;
16
17
  /** @type {(() => void)[]} */
17
18
  _destructionCallbacks: (() => void)[];
18
19
  /** Root level configuration object */
@@ -69,13 +70,16 @@ export default class GenomeSpy {
69
70
  tooltipHandlers: Record<string, import("./tooltip/tooltipHandler.js").TooltipHandler>;
70
71
  /** @type {View} */
71
72
  viewRoot: import("./view/view.js").default;
72
- _paramBroker: ParamBroker;
73
73
  /**
74
74
  * Views that are currently loading data using lazy sources.
75
75
  *
76
76
  * @type {Map<View, boolean>}
77
77
  */
78
78
  _loadingViews: Map<import("./view/view.js").default, boolean>;
79
+ /**
80
+ * @type {HTMLElement}
81
+ */
82
+ _inputBindingContainer: HTMLElement;
79
83
  /**
80
84
  *
81
85
  * @param {(name: string) => any[]} provider
@@ -103,10 +107,9 @@ export default class GenomeSpy {
103
107
  * animations with html elements than with WebGL.
104
108
  */
105
109
  _updateLoadingIndicators(): void;
106
- _prepareContainer(): void;
107
110
  _glHelper: WebGLHelper;
108
- loadingMessageElement: HTMLDivElement;
109
- loadingIndicatorsElement: HTMLDivElement;
111
+ loadingMessageElement: HTMLElement;
112
+ loadingIndicatorsElement: HTMLElement;
110
113
  tooltip: Tooltip;
111
114
  /**
112
115
  * Unregisters all listeners, removes all created dom elements, removes all css classes from the container
@@ -151,7 +154,6 @@ import Animator from "./utils/animator.js";
151
154
  import GenomeStore from "./genome/genomeStore.js";
152
155
  import BufferedViewRenderingContext from "./view/renderingContext/bufferedViewRenderingContext.js";
153
156
  import Inertia from "./utils/inertia.js";
154
- import ParamBroker from "./paramBroker.js";
155
157
  import WebGLHelper from "./gl/webGLHelper.js";
156
158
  import Tooltip from "./utils/ui/tooltip.js";
157
159
  import UnitView from "./view/unitView.js";
@@ -1 +1 @@
1
- {"version":3,"file":"genomeSpy.d.ts","sourceRoot":"","sources":["../../src/genomeSpy.js"],"names":[],"mappings":"AA+CA;IACI;;;;;OAKG;IAEH;;;;;OAKG;IACH,uBAJW,WAAW,qDAEX,OAAO,qBAAqB,EAAE,YAAY,EA0FpD;IAvFG,uBAA0B;IAE1B,6BAA6B;IAC7B,uBADW,CAAC,MAAM,IAAI,CAAC,EAAE,CACM;IAM/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,0BAAqC;IAGrC;;;;OAIG;IACH,8DAA8B;IA4GlC;;;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;IAED,0BA0EC;IAtEG,uBAOC;IAyCD,sCAA0D;IAS1D,yCAA6D;IAI7D,iBAA0C;IAW9C;;OAEG;IACH,gBAmBC;IAED,sCAwMC;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;;;;iCAh8BY,eAAe,GAAG,YAAY,GAAG,QAAQ,GAAG,gBAAgB;4BAlC7C,uBAAuB;4BA0BP,uBAAuB;qBAZ9C,qBAAqB;wBAIlB,yBAAyB;yCARR,yDAAyD;oBAYvD,oBAAoB;wBAMvC,kBAAkB;wBApBlB,qBAAqB;oBAVzB,uBAAuB;qBAQtB,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,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,5 +1,5 @@
1
1
  import { formats as vegaFormats } from "vega-loader";
2
- import { html, render } from "lit-html";
2
+ import { html, 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
 
@@ -11,6 +11,7 @@ import {
11
11
  checkForDuplicateScaleNames,
12
12
  setImplicitScaleNames,
13
13
  calculateCanvasSize,
14
+ calculateViewRootSize,
14
15
  } from "./view/viewUtils.js";
15
16
  import UnitView from "./view/unitView.js";
16
17
 
@@ -34,9 +35,7 @@ import dataTooltipHandler from "./tooltip/dataTooltipHandler.js";
34
35
  import { invalidatePrefix } from "./utils/propertyCacher.js";
35
36
  import { VIEW_ROOT_NAME, ViewFactory } from "./view/viewFactory.js";
36
37
  import { reconfigureScales } from "./view/scaleResolution.js";
37
- import ParamBroker from "./paramBroker.js";
38
- import { debounce } from "./utils/debounce.js";
39
- import { tickStep } from "d3-array";
38
+ import createBindingInputs from "./utils/inputBinding.js";
40
39
 
41
40
  /**
42
41
  * Events that are broadcasted to all views.
@@ -61,14 +60,13 @@ export default class GenomeSpy {
61
60
  */
62
61
  constructor(container, spec, options = {}) {
63
62
  this.container = container;
63
+ this.options = options;
64
+
65
+ options.inputBindingContainer ??= "default";
64
66
 
65
67
  /** @type {(() => void)[]} */
66
68
  this._destructionCallbacks = [];
67
69
 
68
- const styleElement = document.createElement("style");
69
- styleElement.innerHTML = css;
70
- container.appendChild(styleElement);
71
-
72
70
  /** Root level configuration object */
73
71
  this.spec = spec;
74
72
 
@@ -138,118 +136,55 @@ export default class GenomeSpy {
138
136
  /** @type {View} */
139
137
  this.viewRoot = undefined;
140
138
 
141
- this._paramBroker = new ParamBroker();
142
- this.#initializeParameters();
143
-
144
139
  /**
145
140
  * Views that are currently loading data using lazy sources.
146
141
  *
147
142
  * @type {Map<View, boolean>}
148
143
  */
149
144
  this._loadingViews = new Map();
145
+
146
+ /**
147
+ * @type {HTMLElement}
148
+ */
149
+ this._inputBindingContainer = undefined;
150
150
  }
151
151
 
152
- #initializeParameters() {
153
- /** @type {import("lit-html").TemplateResult[]} */
154
- const inputs = [];
152
+ get #canvasWrapper() {
153
+ return /** @type {HTMLElement} */ (
154
+ this.container.querySelector(".canvas-wrapper")
155
+ );
156
+ }
155
157
 
156
- for (const param of this.spec.params ?? []) {
157
- const { name, value, bind } = param;
158
- const setter = this._paramBroker.allocateSetter(name);
158
+ #initializeParameterBindings() {
159
+ /** @type {import("lit").TemplateResult[]} */
160
+ const inputs = [];
159
161
 
160
- if (value != null) {
161
- setter(value);
162
- }
162
+ this.viewRoot.visit((view) => {
163
+ const mediator = view.paramMediator;
164
+ inputs.push(...createBindingInputs(mediator));
165
+ });
166
+ const ibc = this.options.inputBindingContainer;
163
167
 
164
- // TODO: Implement two-way data binding, e.g. when an external agent changes
165
- // the parameter value, the UI components should be updated.
166
-
167
- if (bind && "input" in bind) {
168
- const debouncedSetter = bind.debounce
169
- ? debounce(setter, bind.debounce, false)
170
- : setter;
171
-
172
- if (bind.input == "range") {
173
- // TODO: Show the value next to the slider
174
- inputs.push(
175
- html`<label
176
- >${bind.name ?? name}
177
- <input
178
- type="range"
179
- min=${bind.min ?? 0}
180
- max=${bind.max ?? 100}
181
- step=${bind.step ??
182
- tickStep(bind.min, bind.max, 100)}
183
- .value=${value}
184
- @input=${(/** @type {any} */ e) =>
185
- debouncedSetter(e.target.valueAsNumber)}
186
- /></label>`
187
- );
188
- } else if (bind.input == "checkbox") {
189
- inputs.push(
190
- html`<label
191
- >${bind.name ?? name}
192
- <input
193
- type="checkbox"
194
- ?checked=${value}
195
- @input=${(/** @type {any} */ e) =>
196
- debouncedSetter(e.target.checked)}
197
- /></label>`
198
- );
199
- } else if (bind.input == "radio") {
200
- inputs.push(
201
- html`<span class="label">${bind.name ?? name}</span>
202
- ${bind.options.map(
203
- (option, i) => html`<label>
204
- <input
205
- type="radio"
206
- name=${name}
207
- value=${option}
208
- .checked=${value == option}
209
- @input=${(/** @type {any} */ e) =>
210
- debouncedSetter(e.target.value)}
211
- />${bind.labels?.[i] ?? option}</label
212
- >`
213
- )}`
214
- );
215
- } else if (bind.input == "select") {
216
- inputs.push(
217
- html`<label
218
- >${bind.name ?? name}
219
- <select
220
- @input=${(/** @type {any} */ e) =>
221
- debouncedSetter(e.target.value)}
222
- >
223
- ${bind.options.map(
224
- (option, i) => html`<option
225
- value=${option}
226
- ?selected=${value == option}
227
- >
228
- ${bind.labels?.[i] ?? option}
229
- </option>`
230
- )}
231
- </select></label
232
- >`
233
- );
234
- } else {
235
- // TODO: Support other types: "text", "number", "color".
236
- throw new Error("Unsupported input type: " + bind.input);
237
- }
238
- }
168
+ if (!ibc || ibc == "none" || !inputs.length) {
169
+ return;
239
170
  }
240
171
 
241
- if (inputs.length) {
242
- const inputsDiv = document.createElement("div");
243
- this.container.appendChild(inputsDiv);
172
+ this._inputBindingContainer = element("div", {
173
+ className: "gs-input-bindings",
174
+ });
244
175
 
245
- // TODO: Move to css
246
- // @ts-ignore
247
- inputsDiv.style =
248
- "position: absolute; bottom: 10px; right: 10px; background: rgba(255, 255, 255, 0.8); padding: 10px; z-index: 1; border: 1px solid lightgray";
176
+ if (ibc == "default") {
177
+ this.container.appendChild(this._inputBindingContainer);
178
+ } else if (ibc instanceof HTMLElement) {
179
+ ibc.appendChild(this._inputBindingContainer);
180
+ } else {
181
+ throw new Error("Invalid inputBindingContainer");
182
+ }
249
183
 
184
+ if (inputs.length) {
250
185
  render(
251
- html`${inputs.map((input) => html`<div>${input}</div>`)}`,
252
- inputsDiv
186
+ html`<div class="gs-input-binding">${inputs}</div>`,
187
+ this._inputBindingContainer
253
188
  );
254
189
  }
255
190
  }
@@ -311,7 +246,7 @@ export default class GenomeSpy {
311
246
  * animations with html elements than with WebGL.
312
247
  */
313
248
  _updateLoadingIndicators() {
314
- /** @type {import("lit-html").TemplateResult[]} */
249
+ /** @type {import("lit").TemplateResult[]} */
315
250
  const indicators = [];
316
251
 
317
252
  const isSomethingVisible = () =>
@@ -353,17 +288,10 @@ export default class GenomeSpy {
353
288
  render(indicators, this.loadingIndicatorsElement);
354
289
  }
355
290
 
356
- _prepareContainer() {
357
- this.container.classList.add("genome-spy");
358
- this.container.classList.add("loading");
359
-
360
- this._glHelper = new WebGLHelper(
361
- this.container,
362
- () =>
363
- this.viewRoot
364
- ? calculateCanvasSize(this.viewRoot)
365
- : { width: undefined, height: undefined },
366
- this.spec.background
291
+ #setupDpr() {
292
+ const dprSetter = this.viewRoot.paramMediator.allocateSetter(
293
+ "devicePixelRatio",
294
+ window.devicePixelRatio
367
295
  );
368
296
 
369
297
  const resizeCallback = () => {
@@ -379,9 +307,6 @@ export default class GenomeSpy {
379
307
  resizeObserver.observe(this.container);
380
308
  this._destructionCallbacks.push(() => resizeObserver.disconnect());
381
309
 
382
- const dprSetter = this._paramBroker.allocateSetter("devicePixelRatio");
383
- dprSetter(window.devicePixelRatio);
384
-
385
310
  /** @type {() => void} */
386
311
  let remove = null;
387
312
 
@@ -403,22 +328,48 @@ export default class GenomeSpy {
403
328
  if (remove) {
404
329
  this._destructionCallbacks.push(remove);
405
330
  }
331
+ }
332
+
333
+ #prepareContainer() {
334
+ this.container.classList.add("genome-spy");
335
+
336
+ const styleElement = document.createElement("style");
337
+ styleElement.innerHTML = css;
338
+ this.container.appendChild(styleElement);
339
+
340
+ const canvasWrapper = element("div", {
341
+ class: "canvas-wrapper",
342
+ });
343
+ this.container.appendChild(canvasWrapper);
344
+
345
+ canvasWrapper.classList.add("loading");
346
+
347
+ this._glHelper = new WebGLHelper(
348
+ canvasWrapper,
349
+ () =>
350
+ this.viewRoot
351
+ ? calculateCanvasSize(calculateViewRootSize(this.viewRoot))
352
+ : { width: undefined, height: undefined },
353
+ this.spec.background
354
+ );
406
355
 
407
356
  // The initial loading message that is shown until the first frame is rendered
408
- this.loadingMessageElement = document.createElement("div");
409
- this.loadingMessageElement.className = "loading-message";
410
- this.loadingMessageElement.innerHTML = `<div class="message">Loading<span class="ellipsis">...</span></div>`;
411
- this.container.appendChild(this.loadingMessageElement);
357
+ this.loadingMessageElement = element("div", {
358
+ class: "loading-message",
359
+ innerHTML: `<div class="message">Loading<span class="ellipsis">...</span></div>`,
360
+ });
361
+ canvasWrapper.appendChild(this.loadingMessageElement);
412
362
 
413
363
  // A container for loading indicators (for lazy data sources.)
414
364
  // These could alternatively be included in the view hierarchy,
415
365
  // but it's easier this way – particularly if we want to show
416
366
  // some fancy animated spinners.
417
- this.loadingIndicatorsElement = document.createElement("div");
418
- this.loadingIndicatorsElement.className = "loading-indicators";
419
- this.container.appendChild(this.loadingIndicatorsElement);
367
+ this.loadingIndicatorsElement = element("div", {
368
+ class: "loading-indicators",
369
+ });
370
+ canvasWrapper.appendChild(this.loadingIndicatorsElement);
420
371
 
421
- this.tooltip = new Tooltip(this.container);
372
+ this.tooltip = new Tooltip(canvasWrapper);
422
373
 
423
374
  this.loadingMessageElement
424
375
  .querySelector(".message")
@@ -435,8 +386,10 @@ export default class GenomeSpy {
435
386
  destroy() {
436
387
  // TODO: There's a memory leak somewhere
437
388
 
389
+ const canvasWrapper = this.#canvasWrapper;
390
+
438
391
  this.container.classList.remove("genome-spy");
439
- this.container.classList.remove("loading");
392
+ canvasWrapper.classList.remove("loading");
440
393
 
441
394
  for (const [type, listeners] of this._keyboardListeners) {
442
395
  for (const listener of listeners) {
@@ -448,6 +401,8 @@ export default class GenomeSpy {
448
401
 
449
402
  this._glHelper.finalize();
450
403
 
404
+ this._inputBindingContainer?.remove();
405
+
451
406
  while (this.container.firstChild) {
452
407
  this.container.firstChild.remove();
453
408
  }
@@ -475,8 +430,6 @@ export default class GenomeSpy {
475
430
  return self._glHelper.dpr;
476
431
  },
477
432
 
478
- paramBroker: this._paramBroker,
479
-
480
433
  requestLayoutReflow: () => {
481
434
  // placeholder
482
435
  },
@@ -571,6 +524,11 @@ export default class GenomeSpy {
571
524
  VIEW_ROOT_NAME
572
525
  );
573
526
 
527
+ this.#canvasWrapper.style.flexGrow =
528
+ calculateViewRootSize(this.viewRoot).height.grow > 0 ? "1" : "0";
529
+
530
+ this.#initializeParameterBindings();
531
+
574
532
  checkForDuplicateScaleNames(this.viewRoot);
575
533
 
576
534
  setImplicitScaleNames(this.viewRoot);
@@ -585,6 +543,7 @@ export default class GenomeSpy {
585
543
  // We should now have a complete view hierarchy. Let's update the canvas size
586
544
  // and ensure that the loading message is visible.
587
545
  this._glHelper.invalidateSize();
546
+ this.#setupDpr();
588
547
 
589
548
  // Collect all unit views to a list because they need plenty of initialization
590
549
  const unitViews = /** @type {UnitView[]} */ (
@@ -614,12 +573,14 @@ export default class GenomeSpy {
614
573
  view.mark.initializeData();
615
574
  // Update WebGL buffers
616
575
  view.mark.updateGraphicsData();
576
+ context.animator.requestRender();
617
577
  }, view);
618
578
  }
619
579
 
620
580
  // Have to wait until asynchronous font loading is complete.
621
581
  // Text mark's geometry builder needs font metrics before data can be
622
582
  // converted into geometries.
583
+ // TODO: Make updateGraphicsData async and await font loading there.
623
584
  await context.fontManager.waitUntilReady();
624
585
 
625
586
  // Find all data sources and initiate loading
@@ -661,7 +622,7 @@ export default class GenomeSpy {
661
622
  */
662
623
  async launch() {
663
624
  try {
664
- this._prepareContainer();
625
+ this.#prepareContainer();
665
626
 
666
627
  await this._prepareViewsAndData();
667
628
 
@@ -680,7 +641,7 @@ export default class GenomeSpy {
680
641
 
681
642
  return false;
682
643
  } finally {
683
- this.container.classList.remove("loading");
644
+ this.#canvasWrapper.classList.remove("loading");
684
645
  // Transition listener doesn't appear to work on observablehq
685
646
  window.setTimeout(() => {
686
647
  this.loadingMessageElement.style.display = "none";
@@ -1016,3 +977,19 @@ function createMessageBox(container, message) {
1016
977
  messageBox.appendChild(messageText);
1017
978
  container.appendChild(messageBox);
1018
979
  }
980
+
981
+ /**
982
+ * @param {string} tag
983
+ * @param {Record<string, any>} attrs
984
+ */
985
+ function element(tag, attrs) {
986
+ const el = document.createElement(tag);
987
+ for (const [key, value] of Object.entries(attrs)) {
988
+ if (["innerHTML", "innerText", "className"].includes(key)) {
989
+ // @ts-ignore
990
+ el[key] = value;
991
+ }
992
+ el.setAttribute(key, value);
993
+ }
994
+ return el;
995
+ }
@@ -18,7 +18,7 @@ import {
18
18
  } from "../encoder/encoder.js";
19
19
  import { asArray, peek } from "../utils/arrayUtils.js";
20
20
  import { InternMap } from "internmap";
21
- import { isExprRef } from "../marks/mark.js";
21
+ import { isExprRef } from "../view/paramMediator.js";
22
22
  import scaleNull from "../utils/scaleNull.js";
23
23
 
24
24
  export const ATTRIBUTE_PREFIX = "attr_";
@@ -8,7 +8,7 @@ export function embed(el: string | HTMLElement, spec: string | import("./spec/ro
8
8
  */
9
9
  export function loadSpec(url: string): Promise<any>;
10
10
  import GenomeSpy from "./genomeSpy.js";
11
- import { html } from "lit-html";
11
+ import { html } from "lit";
12
12
  import icon from "./img/bowtie.svg";
13
13
  import favIcon from "./img/genomespy-favicon.svg";
14
14
  export { GenomeSpy, html, icon, favIcon };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.js"],"names":[],"mappings":";AAamB,mMAGnB;AAyFA;;;;;GAKG;AACH,8BAFW,MAAM,gBAmBhB;sBA5HqB,gBAAgB;qBAFjB,UAAU;iBAGd,kBAAkB;oBACf,6BAA6B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.js"],"names":[],"mappings":";AAawB,mMAGnB;AAyFL;;;;;GAKG;AACH,8BAFW,MAAM,gBAmBhB;sBA5HqB,gBAAgB;qBAFjB,KAAK;iBAGT,kBAAkB;oBACf,6BAA6B"}
package/dist/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { isObject, isString } from "vega-util";
2
2
  import { loader as vegaLoader } from "vega-loader";
3
- import { html } from "lit-html";
3
+ import { html } from "lit";
4
4
 
5
5
  import GenomeSpy from "./genomeSpy.js";
6
6
  import icon from "./img/bowtie.svg";
@@ -1,9 +1,4 @@
1
1
  /// <reference types="external-typings/internmap.js" />
2
- /**
3
- * @param {any} x
4
- * @returns {x is import("../spec/parameter.js").ExprRef}
5
- */
6
- export function isExprRef(x: any): x is import("../spec/parameter.js").ExprRef;
7
2
  export const SAMPLE_FACET_UNIFORM: "SAMPLE_FACET_UNIFORM";
8
3
  export const SAMPLE_FACET_TEXTURE: "SAMPLE_FACET_TEXTURE";
9
4
  /**
@@ -90,6 +85,14 @@ export default class Mark {
90
85
  * @returns {Encoding}
91
86
  */
92
87
  fixEncoding(encoding: import("../spec/channel.js").Encoding<string>): import("../spec/channel.js").Encoding<string>;
88
+ /**
89
+ * Handles dynamic properties that are not bound to uniforms but need
90
+ * to trigger a graphics update, i.e., rebuild the vertex buffer.
91
+ *
92
+ * @param {(keyof MarkProps)[]} props
93
+ * @protected
94
+ */
95
+ protected setupExprRefsNeedingGraphicsUpdate(props: (keyof import("../spec/mark.js").MarkProps)[]): void;
93
96
  /**
94
97
  * Returns the encoding spec supplemented with mark's default encodings
95
98
  *
@@ -1 +1 @@
1
- {"version":3,"file":"mark.d.ts","sourceRoot":"","sources":["../../../src/marks/mark.js"],"names":[],"mappings":"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"}
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"}