@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.
- package/dist/index.es.js +16373 -0
- package/dist/index.js +43 -43
- package/package.json +10 -7
- package/src/data/collector.js +0 -183
- package/src/data/collector.test.js +0 -84
- package/src/data/dataFlow.js +0 -148
- package/src/data/dataFlow.test.js +0 -5
- package/src/data/facetNode.js +0 -17
- package/src/data/flow.test.js +0 -72
- package/src/data/flowBatch.d.ts +0 -40
- package/src/data/flowNode.js +0 -283
- package/src/data/flowNode.test.js +0 -50
- package/src/data/flowOptimizer.js +0 -123
- package/src/data/flowOptimizer.test.js +0 -193
- package/src/data/flowTestUtils.js +0 -63
- package/src/data/formats/fasta.js +0 -32
- package/src/data/formats/fasta.test.js +0 -27
- package/src/data/sources/dataSource.js +0 -22
- package/src/data/sources/dataSourceFactory.js +0 -24
- package/src/data/sources/dataUtils.js +0 -78
- package/src/data/sources/dynamicCallbackSource.js +0 -57
- package/src/data/sources/dynamicSource.js +0 -37
- package/src/data/sources/inlineSource.js +0 -67
- package/src/data/sources/inlineSource.test.js +0 -56
- package/src/data/sources/namedSource.js +0 -79
- package/src/data/sources/sequenceSource.js +0 -46
- package/src/data/sources/sequenceSource.test.js +0 -46
- package/src/data/sources/urlSource.js +0 -74
- package/src/data/transforms/aggregate.js +0 -70
- package/src/data/transforms/clone.js +0 -40
- package/src/data/transforms/clone.test.js +0 -11
- package/src/data/transforms/coverage.js +0 -187
- package/src/data/transforms/coverage.test.js +0 -123
- package/src/data/transforms/filter.js +0 -37
- package/src/data/transforms/filter.test.js +0 -18
- package/src/data/transforms/filterScoredLabels.js +0 -134
- package/src/data/transforms/flattenCompressedExons.js +0 -57
- package/src/data/transforms/flattenDelimited.js +0 -74
- package/src/data/transforms/flattenDelimited.test.js +0 -87
- package/src/data/transforms/flattenSequence.js +0 -39
- package/src/data/transforms/flattenSequence.test.js +0 -34
- package/src/data/transforms/formula.js +0 -39
- package/src/data/transforms/formula.test.js +0 -19
- package/src/data/transforms/identifier.js +0 -108
- package/src/data/transforms/identifier.test.js +0 -83
- package/src/data/transforms/linearizeGenomicCoordinate.js +0 -101
- package/src/data/transforms/measureText.js +0 -44
- package/src/data/transforms/pileup.js +0 -128
- package/src/data/transforms/pileup.test.js +0 -70
- package/src/data/transforms/project.js +0 -41
- package/src/data/transforms/project.test.js +0 -32
- package/src/data/transforms/regexExtract.js +0 -61
- package/src/data/transforms/regexExtract.test.js +0 -67
- package/src/data/transforms/regexFold.js +0 -141
- package/src/data/transforms/regexFold.test.js +0 -160
- package/src/data/transforms/sample.js +0 -101
- package/src/data/transforms/sample.test.js +0 -38
- package/src/data/transforms/stack.js +0 -137
- package/src/data/transforms/stack.test.js +0 -91
- package/src/data/transforms/transformFactory.js +0 -60
- package/src/embedApi.d.ts +0 -67
- package/src/encoder/accessor.js +0 -82
- package/src/encoder/accessor.test.js +0 -47
- package/src/encoder/encoder.js +0 -394
- package/src/encoder/encoder.test.js +0 -98
- package/src/fonts/Lato-Regular.json +0 -1267
- package/src/fonts/Lato-Regular.png +0 -0
- package/src/fonts/OFL.txt +0 -93
- package/src/fonts/README.md +0 -3
- package/src/fonts/bmFont.d.ts +0 -58
- package/src/fonts/bmFontManager.js +0 -357
- package/src/fonts/bmFontMetrics.js +0 -108
- package/src/genome/genome.js +0 -317
- package/src/genome/genome.test.js +0 -188
- package/src/genome/genomeStore.js +0 -54
- package/src/genome/locusFormat.js +0 -31
- package/src/genome/scaleIndex.d.ts +0 -38
- package/src/genome/scaleIndex.js +0 -166
- package/src/genome/scaleIndex.test.js +0 -78
- package/src/genome/scaleLocus.d.ts +0 -11
- package/src/genome/scaleLocus.js +0 -108
- package/src/genome/scaleLocus.test.js +0 -4
- package/src/genomeSpy.js +0 -785
- package/src/gl/arrayBuilder.js +0 -199
- package/src/gl/dataToVertices.js +0 -636
- package/src/gl/includes/common.glsl +0 -63
- package/src/gl/includes/picking.fragment.glsl +0 -1
- package/src/gl/includes/picking.vertex.glsl +0 -27
- package/src/gl/includes/sampleFacet.glsl +0 -107
- package/src/gl/includes/scales.glsl +0 -112
- package/src/gl/link.fragment.glsl +0 -18
- package/src/gl/link.vertex.glsl +0 -111
- package/src/gl/point.fragment.glsl +0 -123
- package/src/gl/point.vertex.glsl +0 -129
- package/src/gl/rect.fragment.glsl +0 -51
- package/src/gl/rect.vertex.glsl +0 -114
- package/src/gl/rule.fragment.glsl +0 -52
- package/src/gl/rule.vertex.glsl +0 -89
- package/src/gl/text.fragment.glsl +0 -31
- package/src/gl/text.vertex.glsl +0 -246
- package/src/gl/webGLHelper.js +0 -504
- package/src/img/bowtie.svg +0 -1
- package/src/img/genomespy-favicon.svg +0 -34
- package/src/index.html +0 -11
- package/src/index.js +0 -128
- package/src/marks/link.js +0 -175
- package/src/marks/mark.js +0 -975
- package/src/marks/markUtils.js +0 -125
- package/src/marks/pointMark.js +0 -251
- package/src/marks/rectMark.js +0 -241
- package/src/marks/rule.js +0 -250
- package/src/marks/text.js +0 -278
- package/src/node_modules/.vitest/results.json +0 -1
- package/src/scale/colorUtils.js +0 -184
- package/src/scale/glslScaleGenerator.js +0 -502
- package/src/scale/scale.js +0 -451
- package/src/scale/scale.test.js +0 -324
- package/src/scale/ticks.js +0 -203
- package/src/scale/ticks.test.js +0 -40
- package/src/singlePageApp.js +0 -13
- package/src/spec/axis.d.ts +0 -296
- package/src/spec/channel.d.ts +0 -430
- package/src/spec/data.d.ts +0 -196
- package/src/spec/font.d.ts +0 -15
- package/src/spec/genome.d.ts +0 -35
- package/src/spec/mark.d.ts +0 -429
- package/src/spec/root.d.ts +0 -17
- package/src/spec/sampleView.d.ts +0 -180
- package/src/spec/scale.d.ts +0 -273
- package/src/spec/title.d.ts +0 -102
- package/src/spec/tooltip.d.ts +0 -9
- package/src/spec/transform.d.ts +0 -479
- package/src/spec/view.d.ts +0 -201
- package/src/styles/genome-spy.scss +0 -153
- package/src/tooltip/dataTooltipHandler.js +0 -64
- package/src/tooltip/refseqGeneTooltipHandler.js +0 -78
- package/src/tooltip/tooltipHandler.ts +0 -12
- package/src/types/filetypes.d.ts +0 -14
- package/src/types/flatqueue.d.ts +0 -53
- package/src/types/glsl.d.ts +0 -4
- package/src/types/internmap.d.ts +0 -22
- package/src/types/object.d.ts +0 -21
- package/src/types/vega-loader.d.ts +0 -1
- package/src/types/vega-scale.d.ts +0 -60
- package/src/utils/addBaseUrl.js +0 -19
- package/src/utils/addBaseUrl.test.js +0 -22
- package/src/utils/animator.js +0 -83
- package/src/utils/arrayUtils.js +0 -61
- package/src/utils/binnedIndex.js +0 -167
- package/src/utils/binnedIndex.test.js +0 -155
- package/src/utils/clamp.js +0 -8
- package/src/utils/cloner.js +0 -34
- package/src/utils/cloner.test.js +0 -24
- package/src/utils/coalesce.js +0 -11
- package/src/utils/coalesce.test.js +0 -16
- package/src/utils/concatIterables.js +0 -26
- package/src/utils/concatIterables.test.js +0 -8
- package/src/utils/debounce.js +0 -37
- package/src/utils/domainArray.js +0 -216
- package/src/utils/domainArray.test.js +0 -130
- package/src/utils/eerp.js +0 -13
- package/src/utils/expression.js +0 -32
- package/src/utils/field.js +0 -28
- package/src/utils/formatObject.js +0 -31
- package/src/utils/indexer.js +0 -43
- package/src/utils/indexer.test.js +0 -47
- package/src/utils/inertia.js +0 -124
- package/src/utils/interactionEvent.js +0 -33
- package/src/utils/iterateNestedMaps.js +0 -21
- package/src/utils/iterateNestedMaps.test.js +0 -33
- package/src/utils/kWayMerge.js +0 -42
- package/src/utils/kWayMerge.test.js +0 -26
- package/src/utils/layout/flexLayout.js +0 -368
- package/src/utils/layout/flexLayout.test.js +0 -311
- package/src/utils/layout/grid.js +0 -95
- package/src/utils/layout/grid.test.js +0 -71
- package/src/utils/layout/padding.js +0 -120
- package/src/utils/layout/point.js +0 -23
- package/src/utils/layout/rectangle.js +0 -288
- package/src/utils/layout/rectangle.test.js +0 -172
- package/src/utils/mergeObjects.js +0 -99
- package/src/utils/mergeObjects.test.js +0 -42
- package/src/utils/numberExtractor.js +0 -24
- package/src/utils/numberExtractor.test.js +0 -6
- package/src/utils/point.js +0 -14
- package/src/utils/propertyCacher.js +0 -70
- package/src/utils/propertyCacher.test.js +0 -85
- package/src/utils/propertyCoalescer.js +0 -42
- package/src/utils/propertyCoalescer.test.js +0 -22
- package/src/utils/reservationMap.js +0 -103
- package/src/utils/reservationMap.test.js +0 -20
- package/src/utils/scaleNull.js +0 -19
- package/src/utils/setOperations.js +0 -75
- package/src/utils/smoothstep.js +0 -10
- package/src/utils/throttle.js +0 -34
- package/src/utils/topK.js +0 -76
- package/src/utils/topK.test.js +0 -64
- package/src/utils/transition.js +0 -74
- package/src/utils/ui/tooltip.js +0 -189
- package/src/utils/url.js +0 -22
- package/src/utils/variableTools.js +0 -24
- package/src/utils/variableTools.test.js +0 -13
- package/src/view/axisResolution.js +0 -140
- package/src/view/axisResolution.test.js +0 -201
- package/src/view/axisView.js +0 -747
- package/src/view/concatView.js +0 -45
- package/src/view/containerView.js +0 -159
- package/src/view/facetView.js +0 -491
- package/src/view/flowBuilder.js +0 -367
- package/src/view/flowBuilder.test.js +0 -125
- package/src/view/gridView.js +0 -786
- package/src/view/implicitRootView.js +0 -14
- package/src/view/importView.js +0 -19
- package/src/view/layerView.js +0 -74
- package/src/view/rendering.d.ts +0 -44
- package/src/view/renderingContext/compositeViewRenderingContext.js +0 -51
- package/src/view/renderingContext/deferredViewRenderingContext.js +0 -176
- package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +0 -128
- package/src/view/renderingContext/simpleViewRenderingContext.js +0 -64
- package/src/view/renderingContext/svgViewRenderingContext.js +0 -125
- package/src/view/renderingContext/viewRenderingContext.js +0 -41
- package/src/view/scaleResolution.js +0 -797
- package/src/view/scaleResolution.test.js +0 -572
- package/src/view/scaleResolutionApi.d.ts +0 -40
- package/src/view/testUtils.js +0 -51
- package/src/view/title.js +0 -165
- package/src/view/unitView.js +0 -382
- package/src/view/view.js +0 -612
- package/src/view/view.test.js +0 -214
- package/src/view/viewContext.d.ts +0 -62
- package/src/view/viewFactory.js +0 -181
- package/src/view/viewFactory.test.js +0 -17
- package/src/view/viewUtils.js +0 -327
- package/src/view/zoom.js +0 -89
package/src/view/concatView.js
DELETED
|
@@ -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
|
-
}
|
package/src/view/facetView.js
DELETED
|
@@ -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
|
-
}
|