@genome-spy/core 0.30.0 → 0.30.2

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 (234) hide show
  1. package/dist/index.es.js +16373 -0
  2. package/dist/index.js +43 -43
  3. package/package.json +10 -7
  4. package/src/data/collector.js +0 -183
  5. package/src/data/collector.test.js +0 -84
  6. package/src/data/dataFlow.js +0 -148
  7. package/src/data/dataFlow.test.js +0 -5
  8. package/src/data/facetNode.js +0 -17
  9. package/src/data/flow.test.js +0 -72
  10. package/src/data/flowBatch.d.ts +0 -40
  11. package/src/data/flowNode.js +0 -283
  12. package/src/data/flowNode.test.js +0 -50
  13. package/src/data/flowOptimizer.js +0 -123
  14. package/src/data/flowOptimizer.test.js +0 -193
  15. package/src/data/flowTestUtils.js +0 -63
  16. package/src/data/formats/fasta.js +0 -32
  17. package/src/data/formats/fasta.test.js +0 -27
  18. package/src/data/sources/dataSource.js +0 -22
  19. package/src/data/sources/dataSourceFactory.js +0 -24
  20. package/src/data/sources/dataUtils.js +0 -78
  21. package/src/data/sources/dynamicCallbackSource.js +0 -57
  22. package/src/data/sources/dynamicSource.js +0 -37
  23. package/src/data/sources/inlineSource.js +0 -67
  24. package/src/data/sources/inlineSource.test.js +0 -56
  25. package/src/data/sources/namedSource.js +0 -79
  26. package/src/data/sources/sequenceSource.js +0 -46
  27. package/src/data/sources/sequenceSource.test.js +0 -46
  28. package/src/data/sources/urlSource.js +0 -74
  29. package/src/data/transforms/aggregate.js +0 -70
  30. package/src/data/transforms/clone.js +0 -40
  31. package/src/data/transforms/clone.test.js +0 -11
  32. package/src/data/transforms/coverage.js +0 -187
  33. package/src/data/transforms/coverage.test.js +0 -123
  34. package/src/data/transforms/filter.js +0 -37
  35. package/src/data/transforms/filter.test.js +0 -18
  36. package/src/data/transforms/filterScoredLabels.js +0 -134
  37. package/src/data/transforms/flattenCompressedExons.js +0 -57
  38. package/src/data/transforms/flattenDelimited.js +0 -74
  39. package/src/data/transforms/flattenDelimited.test.js +0 -87
  40. package/src/data/transforms/flattenSequence.js +0 -39
  41. package/src/data/transforms/flattenSequence.test.js +0 -34
  42. package/src/data/transforms/formula.js +0 -39
  43. package/src/data/transforms/formula.test.js +0 -19
  44. package/src/data/transforms/identifier.js +0 -108
  45. package/src/data/transforms/identifier.test.js +0 -83
  46. package/src/data/transforms/linearizeGenomicCoordinate.js +0 -101
  47. package/src/data/transforms/measureText.js +0 -44
  48. package/src/data/transforms/pileup.js +0 -128
  49. package/src/data/transforms/pileup.test.js +0 -70
  50. package/src/data/transforms/project.js +0 -41
  51. package/src/data/transforms/project.test.js +0 -32
  52. package/src/data/transforms/regexExtract.js +0 -61
  53. package/src/data/transforms/regexExtract.test.js +0 -67
  54. package/src/data/transforms/regexFold.js +0 -141
  55. package/src/data/transforms/regexFold.test.js +0 -160
  56. package/src/data/transforms/sample.js +0 -101
  57. package/src/data/transforms/sample.test.js +0 -38
  58. package/src/data/transforms/stack.js +0 -137
  59. package/src/data/transforms/stack.test.js +0 -91
  60. package/src/data/transforms/transformFactory.js +0 -60
  61. package/src/embedApi.d.ts +0 -67
  62. package/src/encoder/accessor.js +0 -82
  63. package/src/encoder/accessor.test.js +0 -47
  64. package/src/encoder/encoder.js +0 -394
  65. package/src/encoder/encoder.test.js +0 -98
  66. package/src/fonts/Lato-Regular.json +0 -1267
  67. package/src/fonts/Lato-Regular.png +0 -0
  68. package/src/fonts/OFL.txt +0 -93
  69. package/src/fonts/README.md +0 -3
  70. package/src/fonts/bmFont.d.ts +0 -58
  71. package/src/fonts/bmFontManager.js +0 -357
  72. package/src/fonts/bmFontMetrics.js +0 -108
  73. package/src/genome/genome.js +0 -317
  74. package/src/genome/genome.test.js +0 -188
  75. package/src/genome/genomeStore.js +0 -54
  76. package/src/genome/locusFormat.js +0 -31
  77. package/src/genome/scaleIndex.d.ts +0 -38
  78. package/src/genome/scaleIndex.js +0 -166
  79. package/src/genome/scaleIndex.test.js +0 -78
  80. package/src/genome/scaleLocus.d.ts +0 -11
  81. package/src/genome/scaleLocus.js +0 -108
  82. package/src/genome/scaleLocus.test.js +0 -4
  83. package/src/genomeSpy.js +0 -785
  84. package/src/gl/arrayBuilder.js +0 -199
  85. package/src/gl/dataToVertices.js +0 -636
  86. package/src/gl/includes/common.glsl +0 -63
  87. package/src/gl/includes/picking.fragment.glsl +0 -1
  88. package/src/gl/includes/picking.vertex.glsl +0 -27
  89. package/src/gl/includes/sampleFacet.glsl +0 -107
  90. package/src/gl/includes/scales.glsl +0 -112
  91. package/src/gl/link.fragment.glsl +0 -18
  92. package/src/gl/link.vertex.glsl +0 -111
  93. package/src/gl/point.fragment.glsl +0 -123
  94. package/src/gl/point.vertex.glsl +0 -129
  95. package/src/gl/rect.fragment.glsl +0 -51
  96. package/src/gl/rect.vertex.glsl +0 -114
  97. package/src/gl/rule.fragment.glsl +0 -52
  98. package/src/gl/rule.vertex.glsl +0 -89
  99. package/src/gl/text.fragment.glsl +0 -31
  100. package/src/gl/text.vertex.glsl +0 -246
  101. package/src/gl/webGLHelper.js +0 -504
  102. package/src/img/bowtie.svg +0 -1
  103. package/src/img/genomespy-favicon.svg +0 -34
  104. package/src/index.html +0 -11
  105. package/src/index.js +0 -128
  106. package/src/marks/link.js +0 -175
  107. package/src/marks/mark.js +0 -975
  108. package/src/marks/markUtils.js +0 -125
  109. package/src/marks/pointMark.js +0 -251
  110. package/src/marks/rectMark.js +0 -241
  111. package/src/marks/rule.js +0 -250
  112. package/src/marks/text.js +0 -278
  113. package/src/node_modules/.vitest/results.json +0 -1
  114. package/src/scale/colorUtils.js +0 -184
  115. package/src/scale/glslScaleGenerator.js +0 -502
  116. package/src/scale/scale.js +0 -451
  117. package/src/scale/scale.test.js +0 -324
  118. package/src/scale/ticks.js +0 -203
  119. package/src/scale/ticks.test.js +0 -40
  120. package/src/singlePageApp.js +0 -13
  121. package/src/spec/axis.d.ts +0 -296
  122. package/src/spec/channel.d.ts +0 -430
  123. package/src/spec/data.d.ts +0 -196
  124. package/src/spec/font.d.ts +0 -15
  125. package/src/spec/genome.d.ts +0 -35
  126. package/src/spec/mark.d.ts +0 -429
  127. package/src/spec/root.d.ts +0 -17
  128. package/src/spec/sampleView.d.ts +0 -180
  129. package/src/spec/scale.d.ts +0 -273
  130. package/src/spec/title.d.ts +0 -102
  131. package/src/spec/tooltip.d.ts +0 -9
  132. package/src/spec/transform.d.ts +0 -479
  133. package/src/spec/view.d.ts +0 -201
  134. package/src/styles/genome-spy.scss +0 -153
  135. package/src/tooltip/dataTooltipHandler.js +0 -64
  136. package/src/tooltip/refseqGeneTooltipHandler.js +0 -78
  137. package/src/tooltip/tooltipHandler.ts +0 -12
  138. package/src/types/filetypes.d.ts +0 -14
  139. package/src/types/flatqueue.d.ts +0 -53
  140. package/src/types/glsl.d.ts +0 -4
  141. package/src/types/internmap.d.ts +0 -22
  142. package/src/types/object.d.ts +0 -21
  143. package/src/types/vega-loader.d.ts +0 -1
  144. package/src/types/vega-scale.d.ts +0 -60
  145. package/src/utils/addBaseUrl.js +0 -19
  146. package/src/utils/addBaseUrl.test.js +0 -22
  147. package/src/utils/animator.js +0 -83
  148. package/src/utils/arrayUtils.js +0 -61
  149. package/src/utils/binnedIndex.js +0 -167
  150. package/src/utils/binnedIndex.test.js +0 -155
  151. package/src/utils/clamp.js +0 -8
  152. package/src/utils/cloner.js +0 -34
  153. package/src/utils/cloner.test.js +0 -24
  154. package/src/utils/coalesce.js +0 -11
  155. package/src/utils/coalesce.test.js +0 -16
  156. package/src/utils/concatIterables.js +0 -26
  157. package/src/utils/concatIterables.test.js +0 -8
  158. package/src/utils/debounce.js +0 -37
  159. package/src/utils/domainArray.js +0 -216
  160. package/src/utils/domainArray.test.js +0 -130
  161. package/src/utils/eerp.js +0 -13
  162. package/src/utils/expression.js +0 -32
  163. package/src/utils/field.js +0 -28
  164. package/src/utils/formatObject.js +0 -31
  165. package/src/utils/indexer.js +0 -43
  166. package/src/utils/indexer.test.js +0 -47
  167. package/src/utils/inertia.js +0 -124
  168. package/src/utils/interactionEvent.js +0 -33
  169. package/src/utils/iterateNestedMaps.js +0 -21
  170. package/src/utils/iterateNestedMaps.test.js +0 -33
  171. package/src/utils/kWayMerge.js +0 -42
  172. package/src/utils/kWayMerge.test.js +0 -26
  173. package/src/utils/layout/flexLayout.js +0 -368
  174. package/src/utils/layout/flexLayout.test.js +0 -311
  175. package/src/utils/layout/grid.js +0 -95
  176. package/src/utils/layout/grid.test.js +0 -71
  177. package/src/utils/layout/padding.js +0 -120
  178. package/src/utils/layout/point.js +0 -23
  179. package/src/utils/layout/rectangle.js +0 -288
  180. package/src/utils/layout/rectangle.test.js +0 -172
  181. package/src/utils/mergeObjects.js +0 -99
  182. package/src/utils/mergeObjects.test.js +0 -42
  183. package/src/utils/numberExtractor.js +0 -24
  184. package/src/utils/numberExtractor.test.js +0 -6
  185. package/src/utils/point.js +0 -14
  186. package/src/utils/propertyCacher.js +0 -70
  187. package/src/utils/propertyCacher.test.js +0 -85
  188. package/src/utils/propertyCoalescer.js +0 -42
  189. package/src/utils/propertyCoalescer.test.js +0 -22
  190. package/src/utils/reservationMap.js +0 -103
  191. package/src/utils/reservationMap.test.js +0 -20
  192. package/src/utils/scaleNull.js +0 -19
  193. package/src/utils/setOperations.js +0 -75
  194. package/src/utils/smoothstep.js +0 -10
  195. package/src/utils/throttle.js +0 -34
  196. package/src/utils/topK.js +0 -76
  197. package/src/utils/topK.test.js +0 -64
  198. package/src/utils/transition.js +0 -74
  199. package/src/utils/ui/tooltip.js +0 -189
  200. package/src/utils/url.js +0 -22
  201. package/src/utils/variableTools.js +0 -24
  202. package/src/utils/variableTools.test.js +0 -13
  203. package/src/view/axisResolution.js +0 -140
  204. package/src/view/axisResolution.test.js +0 -201
  205. package/src/view/axisView.js +0 -747
  206. package/src/view/concatView.js +0 -45
  207. package/src/view/containerView.js +0 -159
  208. package/src/view/facetView.js +0 -491
  209. package/src/view/flowBuilder.js +0 -367
  210. package/src/view/flowBuilder.test.js +0 -125
  211. package/src/view/gridView.js +0 -786
  212. package/src/view/implicitRootView.js +0 -14
  213. package/src/view/importView.js +0 -19
  214. package/src/view/layerView.js +0 -74
  215. package/src/view/rendering.d.ts +0 -44
  216. package/src/view/renderingContext/compositeViewRenderingContext.js +0 -51
  217. package/src/view/renderingContext/deferredViewRenderingContext.js +0 -176
  218. package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +0 -128
  219. package/src/view/renderingContext/simpleViewRenderingContext.js +0 -64
  220. package/src/view/renderingContext/svgViewRenderingContext.js +0 -125
  221. package/src/view/renderingContext/viewRenderingContext.js +0 -41
  222. package/src/view/scaleResolution.js +0 -797
  223. package/src/view/scaleResolution.test.js +0 -572
  224. package/src/view/scaleResolutionApi.d.ts +0 -40
  225. package/src/view/testUtils.js +0 -51
  226. package/src/view/title.js +0 -165
  227. package/src/view/unitView.js +0 -382
  228. package/src/view/view.js +0 -612
  229. package/src/view/view.test.js +0 -214
  230. package/src/view/viewContext.d.ts +0 -62
  231. package/src/view/viewFactory.js +0 -181
  232. package/src/view/viewFactory.test.js +0 -17
  233. package/src/view/viewUtils.js +0 -327
  234. package/src/view/zoom.js +0 -89
@@ -1,45 +0,0 @@
1
- import { isConcatSpec, isVConcatSpec } from "./viewFactory";
2
- import GridView from "./gridView";
3
-
4
- /**
5
- * Creates a vertically or horizontally concatenated layout for children.
6
- */
7
- export default class ConcatView extends GridView {
8
- /**
9
- *
10
- * @param {import("./viewUtils").AnyConcatSpec} spec
11
- * @param {import("./viewUtils").ViewContext} context
12
- * @param {import("./containerView").default} parent
13
- * @param {string} name
14
- */
15
- constructor(spec, context, parent, name) {
16
- super(
17
- spec,
18
- context,
19
- parent,
20
- name,
21
- isConcatSpec(spec)
22
- ? spec.columns
23
- : isVConcatSpec(spec)
24
- ? 1
25
- : Infinity
26
- );
27
-
28
- this.spec = spec;
29
- }
30
-
31
- _createChildren() {
32
- const spec = this.spec;
33
- const childSpecs = isConcatSpec(spec)
34
- ? spec.concat
35
- : isVConcatSpec(spec)
36
- ? spec.vconcat
37
- : spec.hconcat;
38
-
39
- this.setChildren(
40
- childSpecs.map((childSpec, i) =>
41
- this.context.createView(childSpec, this, "grid" + i)
42
- )
43
- );
44
- }
45
- }
@@ -1,159 +0,0 @@
1
- import View, { VISIT_STOP, VISIT_SKIP } from "./view";
2
-
3
- /**
4
- * Compositor view represents a non-leaf node in the view hierarchy.
5
- * @typedef {import("../spec/view").ResolutionTarget} ResolutionTarget
6
- */
7
- export default class ContainerView extends View {
8
- /**
9
- *
10
- * @param {import("./viewUtils").ContainerSpec} spec
11
- * @param {import("./view").ViewContext} context
12
- * @param {ContainerView} parent
13
- * @param {string} name
14
- */
15
- constructor(spec, context, parent, name) {
16
- super(spec, context, parent, name);
17
-
18
- this.spec = spec;
19
- }
20
-
21
- /**
22
- * @returns {IterableIterator<View>}
23
- */
24
- *[Symbol.iterator]() {
25
- // override
26
- }
27
-
28
- /**
29
- * Replaces a child view with another one. Does not alter the old or new child.
30
- *
31
- * @param {import("./view").default} child
32
- * @param {import("./view").default} replacement
33
- */
34
- replaceChild(child, replacement) {
35
- throw new Error("Not implemented");
36
- }
37
-
38
- /**
39
- * Visits child views in depth-first pre-order. Terminates the search and returns
40
- * the value if the visitor returns a defined value. The `afterChildren` callback
41
- * allows for post-order traversal
42
- *
43
- * @param {import("./view").Visitor} visitor
44
- * @returns {import("./view").VisitResult}
45
- */
46
- visit(visitor) {
47
- /** @type {import("./view").VisitResult}*/
48
- let result;
49
- try {
50
- result = visitor(this);
51
- } catch (e) {
52
- // Augment the exception with the view
53
- e.view = this;
54
- throw e;
55
- }
56
-
57
- if (result === VISIT_STOP) {
58
- return result;
59
- }
60
-
61
- if (result !== VISIT_SKIP) {
62
- if (visitor.beforeChildren) {
63
- visitor.beforeChildren(this);
64
- }
65
-
66
- for (const view of this) {
67
- const result = view.visit(visitor);
68
- if (result === VISIT_STOP) {
69
- return result;
70
- }
71
- }
72
-
73
- if (visitor.afterChildren) {
74
- visitor.afterChildren(this);
75
- }
76
-
77
- if (visitor.postOrder) {
78
- visitor.postOrder(this);
79
- }
80
- }
81
- }
82
-
83
- /**
84
- * @param {string[]} path An array of view names
85
- * @returns {View}
86
- */
87
- findDescendantByPath(path) {
88
- for (const child of this) {
89
- if (child.name === path[0]) {
90
- if (path.length == 1) {
91
- return child;
92
- } else if (child instanceof ContainerView) {
93
- return child.findDescendantByPath(path.slice(1));
94
- }
95
- }
96
- }
97
- }
98
-
99
- /**
100
- *
101
- * @param {string} name
102
- */
103
- findChildByName(name) {
104
- for (const child of this) {
105
- if (child.name === name) {
106
- return child;
107
- }
108
- }
109
- }
110
-
111
- /**
112
- *
113
- * @param {string} name
114
- */
115
- findDescendantByName(name) {
116
- /** @type {View} */
117
- let view;
118
-
119
- this.visit((v) => {
120
- if (v.name == name) {
121
- view = v;
122
- return VISIT_STOP;
123
- }
124
- });
125
-
126
- return view;
127
- }
128
-
129
- /**
130
- * @param {import("../spec/channel").Channel | "default"} channel
131
- * @param {ResolutionTarget} resolutionType
132
- * @returns {import("../spec/view").ResolutionBehavior}
133
- */
134
- getConfiguredResolution(channel, resolutionType) {
135
- return this.spec.resolve?.[resolutionType]?.[channel];
136
- }
137
-
138
- /**
139
- * @param {import("../spec/channel").Channel} channel
140
- * @param {ResolutionTarget} resolutionType
141
- * @returns {import("../spec/view").ResolutionBehavior}
142
- */
143
- getDefaultResolution(channel, resolutionType) {
144
- return "shared";
145
- }
146
-
147
- /**
148
- * @param {import("../spec/channel").Channel} channel
149
- * @param {ResolutionTarget} resolutionType
150
- * @returns {import("../spec/view").ResolutionBehavior}
151
- */
152
- getConfiguredOrDefaultResolution(channel, resolutionType) {
153
- return (
154
- this.getConfiguredResolution(channel, resolutionType) ??
155
- this.getConfiguredResolution("default", resolutionType) ??
156
- this.getDefaultResolution(channel, resolutionType)
157
- );
158
- }
159
- }
@@ -1,491 +0,0 @@
1
- // @ts-nocheck
2
- // TODO: Enable when this is taken to use again
3
-
4
- import { isFacetFieldDef, isFacetMapping } from "./viewUtils";
5
- import ContainerView from "./containerView";
6
- import UnitView from "./unitView";
7
- import { cross } from "d3-array";
8
- import { mapToPixelCoords } from "../utils/layout/flexLayout";
9
- import { OrdinalDomain } from "../utils/domainArray";
10
- import Rectangle from "../utils/layout/rectangle";
11
- import coalesce from "../utils/coalesce";
12
- import { field as vegaField } from "vega-util";
13
- import DecoratorView from "./decoratorView";
14
- import Padding from "../utils/layout/padding";
15
-
16
- const DEFAULT_SPACING = 20;
17
-
18
- /**
19
- * @typedef {"column" | "row"} FacetChannel
20
- * @type {FacetChannel[]}
21
- */
22
- const FACET_CHANNELS = ["column", "row"];
23
-
24
- /**
25
- * @type {Record<FacetChannel, FacetChannel>}
26
- */
27
- // eslint-disable-next-line no-unused-vars
28
- const PERPENDICULAR_FACET_CHANNELS = {
29
- column: "row",
30
- row: "column",
31
- };
32
-
33
- // https://vega.github.io/vega-lite/docs/header.html#labels
34
- // TODO: Configurable
35
- const headerConfig = {
36
- labelFontSize: 12,
37
- labelColor: "black",
38
- };
39
-
40
- /** @type {Record<FacetChannel, any>} */
41
- const headerConfigs = {
42
- column: {
43
- ...headerConfig,
44
- labelAngle: 0,
45
- },
46
- row: {
47
- ...headerConfig,
48
- labelAngle: -90,
49
- },
50
- };
51
-
52
- /**
53
- * Implements (a subset of) the Vega-Lite's Facet-operator:
54
- * https://vega.github.io/vega-lite/docs/facet.html
55
- *
56
- * TODO:
57
- * - Facet channel titles
58
- * - Suppress redundant axes
59
- * - Make this thing configurable
60
- *
61
- * @typedef {import("./view").default} View
62
- * @typedef {import("./layerView").default} LayerView
63
- * @typedef {import("./viewUtils").FacetFieldDef} FacetFieldDef
64
- * @typedef {import("./viewUtils").FacetMapping} FacetMapping
65
- * @typedef {import("../utils/layout/flexLayout").LocSize} LocSize
66
- * @typedef {import("../utils/layout/flexLayout").SizeDef} SizeDef
67
- *
68
- * @typedef {object} FacetDimension Stuff for working with facet dimensions
69
- * @prop {function} accessor
70
- * @prop {boolean[] | string[] | number[]} factors
71
- * @prop {FacetFieldDef} facetFieldDef
72
- *
73
- */
74
- export default class FacetView extends ContainerView {
75
- /**
76
- *
77
- * @param {import("./viewUtils").FacetSpec} spec
78
- * @param {import("./viewUtils").ViewContext} context
79
- * @param {ContainerView} parent
80
- * @param {string} name
81
- */
82
- constructor(spec, context, parent, name) {
83
- super(spec, context, parent, name);
84
-
85
- this.spec = spec;
86
-
87
- this.child = /** @type { UnitView | LayerView | DecoratorView } */ (
88
- context.createView(spec.spec, this, `facet`)
89
- );
90
-
91
- /**
92
- * Faceted views for displaying the facet labels
93
- *
94
- * @type {Record<FacetChannel, UnitView>}
95
- */
96
- this._labelViews = Object.fromEntries(
97
- FACET_CHANNELS.map((channel) => [
98
- channel,
99
- new UnitView(
100
- createLabelViewSpec(headerConfigs[channel]),
101
- this.context,
102
- this,
103
- `facetLabel-${channel}`
104
- ),
105
- ])
106
- );
107
-
108
- /** @type {Record<FacetChannel, FacetDimension>} */
109
- this._facetDimensions = { column: undefined, row: undefined };
110
- }
111
-
112
- /**
113
- * @returns {IterableIterator<View>}
114
- */
115
- *[Symbol.iterator]() {
116
- yield this.child;
117
- for (const view of Object.values(this._labelViews)) {
118
- yield view;
119
- }
120
- }
121
-
122
- /**
123
- * @param {import("./view").default} child
124
- * @param {import("./view").default} replacement
125
- */
126
- replaceChild(child, replacement) {
127
- if (child !== this.child) {
128
- throw new Error("Not my child!");
129
- }
130
-
131
- this.child = /** @type {UnitView | LayerView | DecoratorView} */ (
132
- replacement
133
- );
134
- }
135
-
136
- transformData() {
137
- super.transformData();
138
- // A hacky solution for updating facets. TODO: Something more robust.
139
- this.updateFacets();
140
- }
141
-
142
- /**
143
- *
144
- * @param {"row" | "column"} channel
145
- */
146
- getAccessor(channel) {
147
- /** @type {import("./viewUtils").FacetMapping} */
148
- let facetMapping;
149
- if (isFacetMapping(this.spec.facet)) {
150
- facetMapping = this.spec.facet; // Mark provides encodings with defaults and possible modifications
151
- } else if (isFacetFieldDef(this.spec.facet)) {
152
- // TODO: Check "columns"
153
- facetMapping = {
154
- column: this.spec.facet,
155
- };
156
- } else {
157
- throw new Error(
158
- "Invalid facet specification: " +
159
- JSON.stringify(this.spec.facet)
160
- );
161
- }
162
-
163
- if (facetMapping[channel]) {
164
- return this.context.accessorFactory.createAccessor(
165
- facetMapping[channel]
166
- );
167
- }
168
- }
169
-
170
- updateFacets() {
171
- /** @type {import("./viewUtils").FacetMapping} */
172
- let facetMapping;
173
- if (isFacetMapping(this.spec.facet)) {
174
- facetMapping = this.spec.facet; // Mark provides encodings with defaults and possible modifications
175
- } else if (isFacetFieldDef(this.spec.facet)) {
176
- // TODO: Check "columns"
177
- facetMapping = {
178
- column: this.spec.facet,
179
- };
180
- } else {
181
- throw new Error(
182
- "Invalid facet specification: " +
183
- JSON.stringify(this.spec.facet)
184
- );
185
- }
186
-
187
- for (const channel of FACET_CHANNELS) {
188
- const facetFieldDef = facetMapping[channel];
189
- if (!facetFieldDef) {
190
- continue;
191
- }
192
-
193
- const accessor = this.context.accessorFactory.createAccessor(
194
- facetMapping[channel]
195
- );
196
-
197
- const factors = new OrdinalDomain().extendAllWithAccessor(
198
- this.getData(),
199
- accessor
200
- );
201
-
202
- // TODO: Configurable sorting
203
- factors.sort();
204
-
205
- this._facetDimensions[channel] = {
206
- accessor,
207
- factors,
208
- facetFieldDef,
209
- };
210
- }
211
- }
212
-
213
- updateLabels() {
214
- for (const channel of FACET_CHANNELS) {
215
- const facetDimension = this._facetDimensions[channel];
216
- this._labelViews[channel].updateData(
217
- facetDimension
218
- ? facetDimension.factors.map((d) => ({ data: d }))
219
- : []
220
- );
221
- }
222
- }
223
-
224
- /**
225
- * Returns an accessor that returns a (composite) key for partitioning the data
226
- *
227
- * @param {View} [whoIsAsking] Passed to the immediate parent. Allows for
228
- * selectively breaking the inheritance.
229
- * @return {function(object):any}
230
- */
231
- getFacetAccessor(whoIsAsking) {
232
- const { column, row } = this._facetDimensions;
233
-
234
- if (Object.values(this._labelViews).includes(whoIsAsking)) {
235
- // Label views are faceted by the facet labels
236
- return vegaField("data");
237
- } else if (column && row) {
238
- const columnField = vegaField(column.facetFieldDef.field);
239
- const rowField = vegaField(row.facetFieldDef.field);
240
- return /** @param {object} d */ (d) =>
241
- columnField(d) + "," + rowField(d);
242
- } else if (column) {
243
- return vegaField(column.facetFieldDef.field);
244
- } else if (row) {
245
- return vegaField(row.facetFieldDef.field);
246
- } else {
247
- throw new Error("updateFacets() must be called first!");
248
- }
249
- }
250
-
251
- getFacetGroups() {
252
- const { column, row } = this._facetDimensions;
253
-
254
- if (column && row) {
255
- return cross(
256
- column.factors,
257
- row.factors,
258
- (col, row) => col + "," + row
259
- );
260
- } else if (column) {
261
- return column.factors;
262
- } else if (row) {
263
- return row.factors;
264
- } else {
265
- throw new Error("updateFacets() must be called first!");
266
- }
267
- }
268
-
269
- getSize() {
270
- // TODO: IMPLEMENT!
271
- return super.getSize();
272
- }
273
-
274
- /**
275
- * @param {import("./renderingContext/viewRenderingContext").default} context
276
- * @param {import("../utils/layout/rectangle").default} coords
277
- * @param {import("./view").RenderingOptions} [options]
278
- */
279
- render(context, coords, options = {}) {
280
- if (!this.isVisible()) {
281
- return;
282
- }
283
-
284
- coords = coords.shrink(this.getPadding());
285
- context.pushView(this, coords);
286
-
287
- // Size of the view that is being repeated for all the facets
288
- const childSize = this.child.getSize();
289
-
290
- // Fugly hack. TODO: Figure out a systematic phase for doing this
291
- if (!this._labelsUpdated) {
292
- this.updateLabels();
293
- this._labelsUpdated = true;
294
- }
295
-
296
- // TODO: Validate. Columns is not compatible with row channel
297
- const wrap = this.spec.columns && this._facetDimensions.column;
298
-
299
- // We use two flexLayouts to create a grid for the facets.
300
- // Stride and offset control how the cells in the grid are allocated
301
- // for the facets and the intervening facet labels.
302
- const xStride = 1;
303
- const xOffset = wrap ? 0 : this._facetDimensions.row ? 1 : 0;
304
- const yStride = wrap ? 2 : 1;
305
- const yOffset = this._facetDimensions.column ? 1 : 0;
306
-
307
- /**
308
- * @param {SizeDef} childSize
309
- * @param {number} count
310
- * @param {number} stride
311
- * @param {number} offset
312
- */
313
- const calculateCellSizes = (childSize, count, stride, offset) => {
314
- // TODO: take the channel into account
315
- const labelSize = { px: headerConfig.labelFontSize };
316
-
317
- /** @type {SizeDef[]} */
318
- const cellSizes = [];
319
-
320
- for (let i = 0; i < offset; i++) {
321
- cellSizes.push(labelSize);
322
- }
323
-
324
- for (let i = 0; i < count; i++) {
325
- for (let j = 1; j < stride; j++) {
326
- cellSizes.push(labelSize);
327
- }
328
- cellSizes.push(childSize);
329
- }
330
-
331
- return cellSizes;
332
- };
333
-
334
- /**
335
- *
336
- * @param {FacetChannel} channel
337
- * @param {number} count Number of factors
338
- */
339
- const computeFlexCoords = (channel, count) => {
340
- const dimension = this._facetDimensions[channel];
341
-
342
- const spacing = coalesce(
343
- dimension ? dimension.facetFieldDef.spacing : undefined,
344
- this.spec.spacing,
345
- DEFAULT_SPACING
346
- );
347
-
348
- const cellSizes =
349
- channel == "column"
350
- ? calculateCellSizes(
351
- childSize.width,
352
- count,
353
- xStride,
354
- xOffset
355
- )
356
- : calculateCellSizes(
357
- childSize.height,
358
- count,
359
- yStride,
360
- wrap ? 0 : yOffset
361
- );
362
-
363
- return mapToPixelCoords(
364
- cellSizes,
365
- coords[channel == "column" ? "width" : "height"],
366
- {
367
- spacing,
368
- devicePixelRatio: window.devicePixelRatio,
369
- }
370
- );
371
- };
372
-
373
- let nCols = 0;
374
- let nRows = 0;
375
- let n = 0;
376
-
377
- if (wrap) {
378
- // Wrapping layout
379
- n = this._facetDimensions.column.factors.length;
380
- nCols = this.spec.columns;
381
- nRows = Math.ceil(n / nCols);
382
- } else {
383
- /** @param {FacetDimension} facetDimension */
384
- const getCount = (facetDimension) =>
385
- facetDimension ? facetDimension.factors.length : 1;
386
-
387
- nCols = getCount(this._facetDimensions.column);
388
- nRows = getCount(this._facetDimensions.row);
389
- n = nCols * nRows;
390
- }
391
-
392
- const columnFlexCoords = computeFlexCoords("column", nCols);
393
- const rowFlexCoords = computeFlexCoords("row", nRows);
394
-
395
- const axisSizes =
396
- this.child instanceof DecoratorView
397
- ? this.child.getAxisSizes()
398
- : Padding.createUniformPadding(0);
399
-
400
- const facetIds = this.getFacetGroups();
401
-
402
- // Render column labels
403
- if (this._facetDimensions.column) {
404
- const factors = this._facetDimensions.column.factors;
405
- for (let x = 0; x < factors.length; x++) {
406
- // Take wrapping labels into account
407
- const xCell = columnFlexCoords[(x % nCols) * xStride + xOffset];
408
- const yCell = rowFlexCoords[Math.floor(x / nCols) * yStride];
409
- this._labelViews.column.render(
410
- context,
411
- Rectangle.create(
412
- xCell.location + axisSizes.left,
413
- yCell.location,
414
- xCell.size - axisSizes.width,
415
- yCell.size
416
- ).translateBy(coords),
417
- { ...options, facetId: factors[x] }
418
- );
419
- }
420
- }
421
-
422
- // Render row labels
423
- if (this._facetDimensions.row) {
424
- const factors = this._facetDimensions.row.factors;
425
- for (let y = 0; y < factors.length; y++) {
426
- const xCell = columnFlexCoords[0];
427
- const yCell = rowFlexCoords[y * yStride + yOffset];
428
- this._labelViews.row.render(
429
- context,
430
- Rectangle.create(
431
- xCell.location,
432
- yCell.location + axisSizes.top,
433
- xCell.size,
434
- yCell.size - axisSizes.height
435
- ).translateBy(coords),
436
- { ...options, facetId: factors[y] }
437
- );
438
- }
439
- }
440
-
441
- // Render facets
442
- let i = 0;
443
- for (let x = 0; x < nCols; x++) {
444
- for (let y = 0; y < nRows; y++) {
445
- if (i >= n) break;
446
-
447
- const xCell = columnFlexCoords[x * xStride + xOffset];
448
- const yCell = rowFlexCoords[y * yStride + yOffset];
449
- this.child.render(
450
- context,
451
- new Rectangle(
452
- xCell.location,
453
- yCell.location,
454
- xCell.size,
455
- yCell.size
456
- ).translateBy(coords),
457
- { ...options, facetId: facetIds[i] }
458
- );
459
- i++;
460
- }
461
- }
462
-
463
- context.popView(this);
464
- }
465
- }
466
-
467
- /**
468
- *
469
- */
470
- function createLabelViewSpec(headerConfig) {
471
- // TODO: Support styling: https://vega.github.io/vega-lite/docs/header.html#labels
472
-
473
- /** @type {import("./viewUtils").UnitSpec} */
474
- const titleView = {
475
- data: {
476
- values: [],
477
- },
478
- mark: {
479
- type: "text",
480
- clip: false,
481
- angle: headerConfig.labelAngle,
482
- },
483
- encoding: {
484
- text: { field: "data", type: "nominal" },
485
- size: { value: headerConfig.labelFontSize },
486
- color: { value: headerConfig.labelColor },
487
- },
488
- };
489
-
490
- return titleView;
491
- }