@genome-spy/core 0.14.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.
- package/dist/index.js +224 -0
- package/dist/style.css +1 -0
- package/package.json +54 -0
- package/src/data/collector.js +178 -0
- package/src/data/collector.test.js +82 -0
- package/src/data/dataFlow.js +109 -0
- package/src/data/dataFlow.test.js +3 -0
- package/src/data/facetNode.js +17 -0
- package/src/data/flow.test.js +71 -0
- package/src/data/flowBatch.d.ts +40 -0
- package/src/data/flowNode.js +283 -0
- package/src/data/flowNode.test.js +49 -0
- package/src/data/flowOptimizer.js +117 -0
- package/src/data/flowOptimizer.test.js +192 -0
- package/src/data/flowTestUtils.js +63 -0
- package/src/data/formats/fasta.js +32 -0
- package/src/data/formats/fasta.test.js +26 -0
- package/src/data/sources/dataSource.js +22 -0
- package/src/data/sources/dataSourceFactory.js +24 -0
- package/src/data/sources/dataUtils.js +31 -0
- package/src/data/sources/dynamicCallbackSource.js +56 -0
- package/src/data/sources/dynamicSource.js +36 -0
- package/src/data/sources/inlineSource.js +69 -0
- package/src/data/sources/inlineSource.test.js +55 -0
- package/src/data/sources/namedSource.js +74 -0
- package/src/data/sources/sequenceSource.js +46 -0
- package/src/data/sources/sequenceSource.test.js +45 -0
- package/src/data/sources/urlSource.js +74 -0
- package/src/data/transforms/aggregate.js +69 -0
- package/src/data/transforms/clone.js +40 -0
- package/src/data/transforms/clone.test.js +10 -0
- package/src/data/transforms/coverage.js +187 -0
- package/src/data/transforms/coverage.test.js +122 -0
- package/src/data/transforms/filter.js +37 -0
- package/src/data/transforms/filter.test.js +17 -0
- package/src/data/transforms/filterScoredLabels.js +134 -0
- package/src/data/transforms/flattenCompressedExons.js +57 -0
- package/src/data/transforms/flattenDelimited.js +68 -0
- package/src/data/transforms/flattenDelimited.test.js +86 -0
- package/src/data/transforms/flattenSequence.js +39 -0
- package/src/data/transforms/flattenSequence.test.js +33 -0
- package/src/data/transforms/formula.js +39 -0
- package/src/data/transforms/formula.test.js +18 -0
- package/src/data/transforms/identifier.js +108 -0
- package/src/data/transforms/identifier.test.js +82 -0
- package/src/data/transforms/linearizeGenomicCoordinate.js +101 -0
- package/src/data/transforms/measureText.js +44 -0
- package/src/data/transforms/pileup.js +128 -0
- package/src/data/transforms/pileup.test.js +69 -0
- package/src/data/transforms/project.js +41 -0
- package/src/data/transforms/project.test.js +31 -0
- package/src/data/transforms/regexExtract.js +61 -0
- package/src/data/transforms/regexExtract.test.js +66 -0
- package/src/data/transforms/regexFold.js +141 -0
- package/src/data/transforms/regexFold.test.js +159 -0
- package/src/data/transforms/sample.js +101 -0
- package/src/data/transforms/sample.test.js +37 -0
- package/src/data/transforms/stack.js +137 -0
- package/src/data/transforms/stack.test.js +90 -0
- package/src/data/transforms/transformFactory.js +60 -0
- package/src/encoder/accessor.js +82 -0
- package/src/encoder/accessor.test.js +46 -0
- package/src/encoder/encoder.js +369 -0
- package/src/encoder/encoder.test.js +97 -0
- package/src/fonts/Lato-Regular.json +1267 -0
- package/src/fonts/Lato-Regular.png +0 -0
- package/src/fonts/OFL.txt +93 -0
- package/src/fonts/README.md +3 -0
- package/src/fonts/bmFont.d.ts +58 -0
- package/src/fonts/bmFontManager.js +357 -0
- package/src/fonts/bmFontMetrics.js +108 -0
- package/src/genome/genome.js +305 -0
- package/src/genome/genome.test.js +152 -0
- package/src/genome/genomeStore.js +54 -0
- package/src/genome/locusFormat.js +31 -0
- package/src/genome/scaleIndex.js +199 -0
- package/src/genome/scaleIndex.test.js +61 -0
- package/src/genome/scaleLocus.js +112 -0
- package/src/genome/scaleLocus.test.js +3 -0
- package/src/genomeSpy.js +753 -0
- package/src/gl/arrayBuilder.js +199 -0
- package/src/gl/dataToVertices.js +621 -0
- package/src/gl/includes/common.glsl +63 -0
- package/src/gl/includes/fp64-arithmetic.glsl +187 -0
- package/src/gl/includes/fp64-utils.js +132 -0
- package/src/gl/includes/picking.fragment.glsl +3 -0
- package/src/gl/includes/picking.vertex.glsl +29 -0
- package/src/gl/includes/sampleFacet.glsl +107 -0
- package/src/gl/includes/scales.glsl +79 -0
- package/src/gl/includes/scales_fp64.glsl +30 -0
- package/src/gl/link.fragment.glsl +18 -0
- package/src/gl/link.vertex.glsl +111 -0
- package/src/gl/point.fragment.glsl +123 -0
- package/src/gl/point.vertex.glsl +128 -0
- package/src/gl/rect.fragment.glsl +51 -0
- package/src/gl/rect.vertex.glsl +114 -0
- package/src/gl/rule.fragment.glsl +52 -0
- package/src/gl/rule.vertex.glsl +89 -0
- package/src/gl/text.fragment.glsl +31 -0
- package/src/gl/text.vertex.glsl +246 -0
- package/src/gl/webGLHelper.js +490 -0
- package/src/img/bowtie.svg +1 -0
- package/src/img/genomespy-favicon.svg +34 -0
- package/src/index.html +11 -0
- package/src/index.js +151 -0
- package/src/marks/link.js +189 -0
- package/src/marks/mark.js +867 -0
- package/src/marks/markUtils.js +109 -0
- package/src/marks/pointMark.js +279 -0
- package/src/marks/rectMark.js +236 -0
- package/src/marks/rule.js +231 -0
- package/src/marks/text.js +274 -0
- package/src/options.d.ts +9 -0
- package/src/scale/colorUtils.js +184 -0
- package/src/scale/glslScaleGenerator.js +462 -0
- package/src/scale/scale.js +441 -0
- package/src/scale/scale.test.js +323 -0
- package/src/scale/ticks.js +198 -0
- package/src/scale/ticks.test.js +39 -0
- package/src/singlePageApp.js +13 -0
- package/src/spec/axis.d.ts +296 -0
- package/src/spec/channel.d.ts +127 -0
- package/src/spec/data.d.ts +185 -0
- package/src/spec/font.d.ts +15 -0
- package/src/spec/genome.d.ts +35 -0
- package/src/spec/mark.d.ts +432 -0
- package/src/spec/root.d.ts +22 -0
- package/src/spec/scale.d.ts +265 -0
- package/src/spec/tooltip.d.ts +9 -0
- package/src/spec/transform.d.ts +479 -0
- package/src/spec/view.d.ts +215 -0
- package/src/styles/genome-spy.scss +153 -0
- package/src/tooltip/dataTooltipHandler.js +59 -0
- package/src/tooltip/refseqGeneTooltipHandler.js +77 -0
- package/src/tooltip/tooltipHandler.ts +12 -0
- package/src/types/filetypes.d.ts +4 -0
- package/src/types/flatqueue.d.ts +53 -0
- package/src/types/glsl.d.ts +4 -0
- package/src/types/object.d.ts +21 -0
- package/src/types/vega-scale.d.ts +60 -0
- package/src/utils/animator.js +83 -0
- package/src/utils/arrayUtils.js +55 -0
- package/src/utils/binnedRangeIndex.js +83 -0
- package/src/utils/clamp.js +8 -0
- package/src/utils/cloner.js +32 -0
- package/src/utils/cloner.test.js +23 -0
- package/src/utils/coalesce.js +11 -0
- package/src/utils/coalesce.test.js +15 -0
- package/src/utils/concatIterables.js +26 -0
- package/src/utils/concatIterables.test.js +7 -0
- package/src/utils/debounce.js +37 -0
- package/src/utils/domainArray.js +224 -0
- package/src/utils/domainArray.test.js +129 -0
- package/src/utils/eerp.js +13 -0
- package/src/utils/expression.js +32 -0
- package/src/utils/field.js +28 -0
- package/src/utils/fisheye.js +60 -0
- package/src/utils/formatObject.js +31 -0
- package/src/utils/html.js +23 -0
- package/src/utils/html.test.js +13 -0
- package/src/utils/indexer.js +43 -0
- package/src/utils/indexer.test.js +46 -0
- package/src/utils/inertia.js +124 -0
- package/src/utils/interactionEvent.js +33 -0
- package/src/utils/iterateNestedMaps.js +21 -0
- package/src/utils/iterateNestedMaps.test.js +32 -0
- package/src/utils/kWayMerge.js +42 -0
- package/src/utils/kWayMerge.test.js +25 -0
- package/src/utils/layout/flexLayout.js +336 -0
- package/src/utils/layout/flexLayout.test.js +296 -0
- package/src/utils/layout/padding.js +107 -0
- package/src/utils/layout/point.js +23 -0
- package/src/utils/layout/rectangle.js +282 -0
- package/src/utils/layout/rectangle.test.js +171 -0
- package/src/utils/mergeObjects.js +99 -0
- package/src/utils/mergeObjects.test.js +41 -0
- package/src/utils/numberExtractor.js +24 -0
- package/src/utils/numberExtractor.test.js +5 -0
- package/src/utils/point.js +14 -0
- package/src/utils/propertyCacher.js +70 -0
- package/src/utils/propertyCacher.test.js +84 -0
- package/src/utils/propertyCoalescer.js +37 -0
- package/src/utils/propertyCoalescer.test.js +21 -0
- package/src/utils/reservationMap.js +103 -0
- package/src/utils/reservationMap.test.js +19 -0
- package/src/utils/scaleNull.js +19 -0
- package/src/utils/setOperations.js +75 -0
- package/src/utils/smoothstep.js +10 -0
- package/src/utils/throttle.js +34 -0
- package/src/utils/topK.js +76 -0
- package/src/utils/topK.test.js +63 -0
- package/src/utils/transition.js +74 -0
- package/src/utils/ui/tooltip.js +189 -0
- package/src/utils/url.js +22 -0
- package/src/utils/variableTools.js +24 -0
- package/src/utils/variableTools.test.js +12 -0
- package/src/view/axisResolution.js +135 -0
- package/src/view/axisResolution.test.js +200 -0
- package/src/view/axisView.js +746 -0
- package/src/view/channel.js +5 -0
- package/src/view/concatView.js +296 -0
- package/src/view/containerView.js +141 -0
- package/src/view/decoratorView.js +510 -0
- package/src/view/facetView.js +488 -0
- package/src/view/flowBuilder.js +362 -0
- package/src/view/flowBuilder.test.js +124 -0
- package/src/view/importView.js +19 -0
- package/src/view/layerView.js +60 -0
- package/src/view/rendering.d.ts +44 -0
- package/src/view/renderingContext/compositeViewRenderingContext.js +51 -0
- package/src/view/renderingContext/deferredViewRenderingContext.js +174 -0
- package/src/view/renderingContext/layoutRecorderViewRenderingContext.js +128 -0
- package/src/view/renderingContext/simpleViewRenderingContext.js +62 -0
- package/src/view/renderingContext/svgViewRenderingContext.js +121 -0
- package/src/view/renderingContext/viewRenderingContext.js +41 -0
- package/src/view/scaleResolution.js +756 -0
- package/src/view/scaleResolution.test.js +571 -0
- package/src/view/scaleResolutionApi.d.ts +40 -0
- package/src/view/testUtils.js +48 -0
- package/src/view/unitView.js +368 -0
- package/src/view/view.js +589 -0
- package/src/view/view.test.js +213 -0
- package/src/view/viewContext.d.ts +57 -0
- package/src/view/viewFactory.js +179 -0
- package/src/view/viewFactory.test.js +16 -0
- package/src/view/viewUtils.js +420 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { isNumber } from "vega-util";
|
|
2
|
+
import { isStepSize } from "../../view/view";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* Layout calculation inspired by flexbox. The elements may have an
|
|
7
|
+
* absolute size (in pixels) and a growth component for filling the
|
|
8
|
+
* remaining space. Spacing around zero-sized items are collapsed.
|
|
9
|
+
*
|
|
10
|
+
* Read more at https://css-tricks.com/flex-grow-is-weird/
|
|
11
|
+
*
|
|
12
|
+
* @typedef {object} SizeDef Size definition inspired by CSS flexbox
|
|
13
|
+
* @prop {number} [px] Size in pixels
|
|
14
|
+
* @prop {number} [grow] Share of remaining space
|
|
15
|
+
*
|
|
16
|
+
* @typedef {object} LocSize One-dimensional location and size
|
|
17
|
+
* @prop {number} location
|
|
18
|
+
* @prop {number} size
|
|
19
|
+
*
|
|
20
|
+
* @typedef {object} FlexOptions
|
|
21
|
+
* @prop {number} [spacing] gap between items in pixels
|
|
22
|
+
* @prop {number} [devicePixelRatio] allows for snapping to "retina" pixels.
|
|
23
|
+
* Default: `undefined`, which disables the snapping.
|
|
24
|
+
* @prop {number} [offset] add the offset to all locations. Default: `0`.
|
|
25
|
+
* @prop {boolean} [reverse] fill from "right to left".
|
|
26
|
+
*
|
|
27
|
+
* @param {SizeDef[]} items
|
|
28
|
+
* @param {number} containerSize in pixels
|
|
29
|
+
* @param {FlexOptions} [options]
|
|
30
|
+
* @returns {LocSize[]}
|
|
31
|
+
*/
|
|
32
|
+
export function mapToPixelCoords(
|
|
33
|
+
items,
|
|
34
|
+
containerSize,
|
|
35
|
+
{ spacing, devicePixelRatio, offset, reverse } = {}
|
|
36
|
+
) {
|
|
37
|
+
spacing = spacing || 0;
|
|
38
|
+
offset = offset || 0;
|
|
39
|
+
|
|
40
|
+
let totalPx = 0;
|
|
41
|
+
let totalGrow = 0;
|
|
42
|
+
|
|
43
|
+
for (const size of items) {
|
|
44
|
+
totalPx += z(size.px) + (isZeroSizeDef(size) ? 0 : spacing);
|
|
45
|
+
totalGrow += z(size.grow);
|
|
46
|
+
}
|
|
47
|
+
totalPx -= spacing;
|
|
48
|
+
|
|
49
|
+
const remainingSpace = Math.max(0, containerSize - totalPx);
|
|
50
|
+
|
|
51
|
+
/** @type {function(number):number} x */
|
|
52
|
+
const round =
|
|
53
|
+
devicePixelRatio !== undefined
|
|
54
|
+
? (x) => Math.round(x * devicePixelRatio) / devicePixelRatio
|
|
55
|
+
: (x) => x;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Buffer zero-sized items so that their locations can be spread evenly.
|
|
59
|
+
* They can then be interpolated nicely.
|
|
60
|
+
* @type {SizeDef[]}
|
|
61
|
+
*/
|
|
62
|
+
const zeroBuffer = [];
|
|
63
|
+
|
|
64
|
+
/** @type {LocSize[]} */
|
|
65
|
+
const results = [];
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Spread evenly
|
|
69
|
+
*
|
|
70
|
+
* @param {boolean} inMiddle
|
|
71
|
+
*/
|
|
72
|
+
const flushZeroBuffer = (inMiddle) => {
|
|
73
|
+
const n = zeroBuffer.length;
|
|
74
|
+
if (!n) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const s = (inMiddle ? spacing : 0) * (reverse ? -1 : 1);
|
|
79
|
+
|
|
80
|
+
x -= s;
|
|
81
|
+
for (let i = 0; i < n; i++) {
|
|
82
|
+
results.push({
|
|
83
|
+
location: x + ((i + 1) / (n + 1)) * s,
|
|
84
|
+
size: 0,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
x += s;
|
|
88
|
+
|
|
89
|
+
zeroBuffer.length = 0;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
let x = reverse ? Math.max(containerSize, totalPx) : 0 + offset;
|
|
93
|
+
|
|
94
|
+
// Handle a special case
|
|
95
|
+
if (items.length == 1 && isZeroSizeDef(items[0])) {
|
|
96
|
+
return [{ location: x, size: 0 }];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (let i = 0; i < items.length; i++) {
|
|
100
|
+
const size = items[i];
|
|
101
|
+
|
|
102
|
+
if (isZeroSizeDef(size)) {
|
|
103
|
+
zeroBuffer.push(size);
|
|
104
|
+
} else {
|
|
105
|
+
flushZeroBuffer(results.length > 0);
|
|
106
|
+
|
|
107
|
+
const advance =
|
|
108
|
+
z(size.px) +
|
|
109
|
+
(totalGrow ? (z(size.grow) / totalGrow) * remainingSpace : 0);
|
|
110
|
+
|
|
111
|
+
if (reverse) {
|
|
112
|
+
x -= advance;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
results.push({ location: round(x), size: round(advance) });
|
|
116
|
+
|
|
117
|
+
if (!reverse) {
|
|
118
|
+
x += advance + spacing;
|
|
119
|
+
} else {
|
|
120
|
+
x -= spacing;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Remove the last gap
|
|
126
|
+
x += reverse ? spacing : -spacing;
|
|
127
|
+
|
|
128
|
+
flushZeroBuffer(false);
|
|
129
|
+
|
|
130
|
+
return results;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Returns the minimum size (the sum of pixels sizes) for the flex items
|
|
135
|
+
*
|
|
136
|
+
* @param {SizeDef[]} items
|
|
137
|
+
* @param {FlexOptions} [options]
|
|
138
|
+
*/
|
|
139
|
+
export function getMinimumSize(items, { spacing } = { spacing: 0 }) {
|
|
140
|
+
let minimumSize = 0;
|
|
141
|
+
for (const size of items) {
|
|
142
|
+
minimumSize += z(size.px) + (isZeroSizeDef(size) ? 0 : spacing);
|
|
143
|
+
}
|
|
144
|
+
return Math.max(0, minimumSize - spacing);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Returns true if relative (stretching) elements are present
|
|
149
|
+
* @param {SizeDef[]} items
|
|
150
|
+
*/
|
|
151
|
+
export function isStretching(items) {
|
|
152
|
+
return items.some((size) => size.grow);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export class FlexDimensions {
|
|
156
|
+
/**
|
|
157
|
+
*
|
|
158
|
+
* @param {SizeDef} width
|
|
159
|
+
* @param {SizeDef} height
|
|
160
|
+
*/
|
|
161
|
+
constructor(width, height) {
|
|
162
|
+
// TODO: Consider making immutable
|
|
163
|
+
/** @readonly */
|
|
164
|
+
this.width = width;
|
|
165
|
+
/** @readonly */
|
|
166
|
+
this.height = height;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Adds padding to absolute (px) dimensions
|
|
171
|
+
*
|
|
172
|
+
* @param {import("./padding").default} padding
|
|
173
|
+
*/
|
|
174
|
+
addPadding(padding) {
|
|
175
|
+
return new FlexDimensions(
|
|
176
|
+
{
|
|
177
|
+
px: (this.width.px || 0) + padding.width,
|
|
178
|
+
grow: this.width.grow,
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
px: (this.height.px || 0) + padding.height,
|
|
182
|
+
grow: this.height.grow,
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* A sizedef that takes no space at all.
|
|
190
|
+
*
|
|
191
|
+
* @type {SizeDef}
|
|
192
|
+
*/
|
|
193
|
+
export const ZERO_SIZEDEF = Object.freeze({
|
|
194
|
+
px: 0,
|
|
195
|
+
grow: 0,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
export const ZERO_FLEXDIMENSIONS = new FlexDimensions(
|
|
199
|
+
ZERO_SIZEDEF,
|
|
200
|
+
ZERO_SIZEDEF
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Is the sizeDef taking no space at all
|
|
205
|
+
*
|
|
206
|
+
* @param {SizeDef} sizeDef
|
|
207
|
+
*/
|
|
208
|
+
export function isZeroSizeDef(sizeDef) {
|
|
209
|
+
return !sizeDef.px && !sizeDef.grow;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Converts undefined/null to zero
|
|
214
|
+
*
|
|
215
|
+
* @param {number} value
|
|
216
|
+
*/
|
|
217
|
+
function z(value) {
|
|
218
|
+
return value || 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
*
|
|
223
|
+
* @param {*} spec
|
|
224
|
+
* @returns {spec is SizeDef}
|
|
225
|
+
*/
|
|
226
|
+
export function isSizeDef(spec) {
|
|
227
|
+
return spec && (isNumber(spec.px) || isNumber(spec.grow));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
*
|
|
232
|
+
* @param {"container" | number | SizeDef | import("../../spec/view").Step} size
|
|
233
|
+
* @returns {SizeDef}
|
|
234
|
+
*/
|
|
235
|
+
export function parseSizeDef(size) {
|
|
236
|
+
if (isStepSize(size)) {
|
|
237
|
+
throw new Error("parseSizeDef does not accept step-based sizes.");
|
|
238
|
+
} else if (isSizeDef(size)) {
|
|
239
|
+
return size;
|
|
240
|
+
} else if (isNumber(size)) {
|
|
241
|
+
return { px: size, grow: 0 };
|
|
242
|
+
} else if (size === "container") {
|
|
243
|
+
// https://vega.github.io/vega-lite/docs/size.html#specifying-responsive-width-and-height
|
|
244
|
+
return { px: 0, grow: 1 };
|
|
245
|
+
} else if (!size) {
|
|
246
|
+
return { px: 0, grow: 1 };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
throw new Error(`Invalid sizeDef: ${size}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// TODO: Find a better place for the following utilities: ////////////////////////////////////
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Interpolates between two LocSizes
|
|
256
|
+
*
|
|
257
|
+
* @param {LocSize} from
|
|
258
|
+
* @param {LocSize} to
|
|
259
|
+
* @param {function():number} ratio
|
|
260
|
+
* @returns {LocSize}
|
|
261
|
+
*/
|
|
262
|
+
export function interpolateLocSizes(from, to, ratio) {
|
|
263
|
+
return {
|
|
264
|
+
get location() {
|
|
265
|
+
const r = ratio();
|
|
266
|
+
switch (r) {
|
|
267
|
+
case 0:
|
|
268
|
+
return from.location;
|
|
269
|
+
case 1:
|
|
270
|
+
return to.location;
|
|
271
|
+
default:
|
|
272
|
+
return r * to.location + (1 - r) * from.location;
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
get size() {
|
|
277
|
+
const r = ratio();
|
|
278
|
+
switch (r) {
|
|
279
|
+
case 0:
|
|
280
|
+
return from.size;
|
|
281
|
+
case 1:
|
|
282
|
+
return to.size;
|
|
283
|
+
default:
|
|
284
|
+
return r * to.size + (1 - r) * from.size;
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Wraps a LocSize and allows scrolling.
|
|
292
|
+
*
|
|
293
|
+
* @param {LocSize} locSize
|
|
294
|
+
* @param {number | function():number} offset
|
|
295
|
+
* @returns {LocSize}
|
|
296
|
+
*/
|
|
297
|
+
export function translateLocSize(locSize, offset) {
|
|
298
|
+
const fn = isNumber(offset) ? () => offset : offset;
|
|
299
|
+
return {
|
|
300
|
+
get location() {
|
|
301
|
+
return locSize.location + fn();
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
get size() {
|
|
305
|
+
return locSize.size;
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Wraps a LocSize and allows scaling.
|
|
312
|
+
*
|
|
313
|
+
* @param {LocSize} locSize
|
|
314
|
+
* @param {number | function():number} factor
|
|
315
|
+
* @returns {LocSize}
|
|
316
|
+
*/
|
|
317
|
+
export function scaleLocSize(locSize, factor) {
|
|
318
|
+
const fn = isNumber(factor) ? () => factor : factor;
|
|
319
|
+
return {
|
|
320
|
+
get location() {
|
|
321
|
+
return locSize.location * fn();
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
get size() {
|
|
325
|
+
return locSize.size * fn();
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* @param {LocSize} locSize
|
|
332
|
+
* @param {number} value
|
|
333
|
+
*/
|
|
334
|
+
export function locSizeEncloses(locSize, value) {
|
|
335
|
+
return value >= locSize.location && value < locSize.location + locSize.size;
|
|
336
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mapToPixelCoords,
|
|
3
|
+
getMinimumSize,
|
|
4
|
+
isStretching,
|
|
5
|
+
parseSizeDef,
|
|
6
|
+
} from "./flexLayout";
|
|
7
|
+
|
|
8
|
+
test("parseSize", () => {
|
|
9
|
+
expect(parseSizeDef(10)).toEqual({ px: 10, grow: 0 });
|
|
10
|
+
expect(parseSizeDef({ px: 20, grow: 2 })).toEqual({ px: 20, grow: 2 });
|
|
11
|
+
expect(parseSizeDef(undefined)).toEqual({ px: 0, grow: 1 });
|
|
12
|
+
expect(parseSizeDef(null)).toEqual({ px: 0, grow: 1 });
|
|
13
|
+
expect(parseSizeDef("container")).toEqual({ px: 0, grow: 1 });
|
|
14
|
+
expect(() => parseSizeDef({})).toThrow();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("Basic flex functionality", () => {
|
|
18
|
+
test("Absolute sizes", () => {
|
|
19
|
+
const items = [10, 30, 20].map((x) => ({ px: x }));
|
|
20
|
+
const containerSize = 100;
|
|
21
|
+
|
|
22
|
+
const mapped = mapToPixelCoords(items, containerSize);
|
|
23
|
+
|
|
24
|
+
expect(mapped[0]).toEqual({ location: 0, size: 10 });
|
|
25
|
+
expect(mapped[1]).toEqual({ location: 10, size: 30 });
|
|
26
|
+
expect(mapped[2]).toEqual({ location: 40, size: 20 });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("Absolute sizes with spacing", () => {
|
|
30
|
+
const items = [10, 30, 20].map((x) => ({ px: x }));
|
|
31
|
+
const containerSize = 100;
|
|
32
|
+
|
|
33
|
+
const mapped = mapToPixelCoords(items, containerSize, { spacing: 10 });
|
|
34
|
+
|
|
35
|
+
expect(mapped[0]).toEqual({ location: 0, size: 10 });
|
|
36
|
+
expect(mapped[1]).toEqual({ location: 20, size: 30 });
|
|
37
|
+
expect(mapped[2]).toEqual({ location: 60, size: 20 });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("Absolute sizes with spacing, reversed", () => {
|
|
41
|
+
const items = [10, 30, 20].map((x) => ({ px: x }));
|
|
42
|
+
const containerSize = 100;
|
|
43
|
+
|
|
44
|
+
const mapped = mapToPixelCoords(items, containerSize, {
|
|
45
|
+
spacing: 10,
|
|
46
|
+
reverse: true,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(mapped[0]).toEqual({ location: 90, size: 10 });
|
|
50
|
+
expect(mapped[1]).toEqual({ location: 50, size: 30 });
|
|
51
|
+
expect(mapped[2]).toEqual({ location: 20, size: 20 });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("Absolute sizes with spacing, reversed and insufficient containerSize", () => {
|
|
55
|
+
const items = [10, 30, 20].map((x) => ({ px: x }));
|
|
56
|
+
const containerSize = 0;
|
|
57
|
+
|
|
58
|
+
const mapped = mapToPixelCoords(items, containerSize, {
|
|
59
|
+
spacing: 10,
|
|
60
|
+
reverse: true,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
expect(mapped[0]).toEqual({ location: 70, size: 10 });
|
|
64
|
+
expect(mapped[1]).toEqual({ location: 30, size: 30 });
|
|
65
|
+
expect(mapped[2]).toEqual({ location: 0, size: 20 });
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("Growing sizes", () => {
|
|
69
|
+
const items = [10, 20, 70].map((x) => ({ grow: x }));
|
|
70
|
+
const containerSize = 200;
|
|
71
|
+
|
|
72
|
+
const mapped = mapToPixelCoords(items, containerSize);
|
|
73
|
+
|
|
74
|
+
expect(mapped[0]).toEqual({ location: 0, size: 20 });
|
|
75
|
+
expect(mapped[1]).toEqual({ location: 20, size: 40 });
|
|
76
|
+
expect(mapped[2]).toEqual({ location: 60, size: 140 });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("Growing sizes with spacing", () => {
|
|
80
|
+
const items = [10, 20, 70].map((x) => ({ grow: x }));
|
|
81
|
+
const containerSize = 220;
|
|
82
|
+
const mapped = mapToPixelCoords(items, containerSize, { spacing: 10 });
|
|
83
|
+
|
|
84
|
+
expect(mapped[0]).toEqual({ location: 0, size: 20 });
|
|
85
|
+
expect(mapped[1]).toEqual({ location: 30, size: 40 });
|
|
86
|
+
expect(mapped[2]).toEqual({ location: 80, size: 140 });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("Mixed absolute and relative sizes", () => {
|
|
90
|
+
const items = [{ px: 100 }, { grow: 1 }, { grow: 9 }, { px: 200 }];
|
|
91
|
+
const containerSize = 1100;
|
|
92
|
+
const mapped = mapToPixelCoords(items, containerSize);
|
|
93
|
+
|
|
94
|
+
expect(mapped[0]).toEqual({ location: 0, size: 100 });
|
|
95
|
+
expect(mapped[1]).toEqual({ location: 100, size: 80 });
|
|
96
|
+
expect(mapped[2]).toEqual({ location: 180, size: 720 });
|
|
97
|
+
expect(mapped[3]).toEqual({ location: 900, size: 200 });
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("Sizes having both absolute and growing components", () => {
|
|
101
|
+
const items = [
|
|
102
|
+
{ px: 1 },
|
|
103
|
+
{ px: 2 },
|
|
104
|
+
{ px: 3, grow: 2 },
|
|
105
|
+
{ px: 4, grow: 1 },
|
|
106
|
+
];
|
|
107
|
+
const containerSize = 16;
|
|
108
|
+
const mapped = mapToPixelCoords(items, containerSize);
|
|
109
|
+
|
|
110
|
+
expect(mapped[0]).toEqual({ location: 0, size: 1 });
|
|
111
|
+
expect(mapped[1]).toEqual({ location: 1, size: 2 });
|
|
112
|
+
expect(mapped[2]).toEqual({ location: 3, size: 7 });
|
|
113
|
+
expect(mapped[3]).toEqual({ location: 10, size: 6 });
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("Zero sizes return zero coords", () => {
|
|
117
|
+
const items = [{ grow: 0 }, { grow: 0 }];
|
|
118
|
+
|
|
119
|
+
const mapped = mapToPixelCoords(items, 0);
|
|
120
|
+
expect(mapped[0]).toEqual({ location: 0, size: 0 });
|
|
121
|
+
expect(mapped[1]).toEqual({ location: 0, size: 0 });
|
|
122
|
+
|
|
123
|
+
const mapped2 = mapToPixelCoords(items, 1);
|
|
124
|
+
expect(mapped2[0]).toEqual({ location: 0, size: 0 });
|
|
125
|
+
expect(mapped2[1]).toEqual({ location: 0, size: 0 });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("Offset is added", () => {
|
|
129
|
+
const items = [10, 30, 20].map((x) => ({ px: x }));
|
|
130
|
+
const containerSize = 100;
|
|
131
|
+
|
|
132
|
+
const mapped = mapToPixelCoords(items, containerSize, { offset: 5 });
|
|
133
|
+
|
|
134
|
+
expect(mapped[0]).toEqual({ location: 5, size: 10 });
|
|
135
|
+
expect(mapped[1]).toEqual({ location: 15, size: 30 });
|
|
136
|
+
expect(mapped[2]).toEqual({ location: 45, size: 20 });
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("Collapse gaps when items have zero px and grow", () => {
|
|
141
|
+
test("Zero as first", () => {
|
|
142
|
+
const items = [0, 30, 20].map((x) => ({ px: x }));
|
|
143
|
+
const containerSize = 100;
|
|
144
|
+
|
|
145
|
+
const mapped = mapToPixelCoords(items, containerSize, { spacing: 10 });
|
|
146
|
+
|
|
147
|
+
expect(mapped[0]).toEqual({ location: 0, size: 0 });
|
|
148
|
+
expect(mapped[1]).toEqual({ location: 0, size: 30 });
|
|
149
|
+
expect(mapped[2]).toEqual({ location: 40, size: 20 });
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("Zero in the middle", () => {
|
|
153
|
+
const items = [10, 0, 20].map((x) => ({ px: x }));
|
|
154
|
+
const containerSize = 100;
|
|
155
|
+
|
|
156
|
+
const mapped = mapToPixelCoords(items, containerSize, { spacing: 10 });
|
|
157
|
+
|
|
158
|
+
expect(mapped[0]).toEqual({ location: 0, size: 10 });
|
|
159
|
+
expect(mapped[1]).toEqual({ location: 15, size: 0 });
|
|
160
|
+
expect(mapped[2]).toEqual({ location: 20, size: 20 });
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("Multiple zeroes in the middle", () => {
|
|
164
|
+
const items = [10, 0, 0, 0, 20].map((x) => ({ px: x }));
|
|
165
|
+
const containerSize = 100;
|
|
166
|
+
|
|
167
|
+
const mapped = mapToPixelCoords(items, containerSize, { spacing: 10 });
|
|
168
|
+
|
|
169
|
+
expect(mapped[0]).toEqual({ location: 0, size: 10 });
|
|
170
|
+
expect(mapped[1]).toEqual({ location: 12.5, size: 0 });
|
|
171
|
+
expect(mapped[2]).toEqual({ location: 15, size: 0 });
|
|
172
|
+
expect(mapped[3]).toEqual({ location: 17.5, size: 0 });
|
|
173
|
+
expect(mapped[4]).toEqual({ location: 20, size: 20 });
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("Zero as last", () => {
|
|
177
|
+
const items = [10, 30, 0].map((x) => ({ px: x }));
|
|
178
|
+
const containerSize = 100;
|
|
179
|
+
|
|
180
|
+
const mapped = mapToPixelCoords(items, containerSize, { spacing: 10 });
|
|
181
|
+
|
|
182
|
+
expect(mapped[0]).toEqual({ location: 0, size: 10 });
|
|
183
|
+
expect(mapped[1]).toEqual({ location: 20, size: 30 });
|
|
184
|
+
expect(mapped[2]).toEqual({ location: 50, size: 0 });
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("Only a zero", () => {
|
|
188
|
+
const items = [0].map((x) => ({ px: x }));
|
|
189
|
+
const containerSize = 100;
|
|
190
|
+
|
|
191
|
+
const mapped = mapToPixelCoords(items, containerSize, { spacing: 10 });
|
|
192
|
+
|
|
193
|
+
expect(mapped[0]).toEqual({ location: 0, size: 0 });
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe("Collapse gaps when items have zero px and grow, reversed", () => {
|
|
198
|
+
test("Zero as first", () => {
|
|
199
|
+
const items = [0, 30, 20].map((x) => ({ px: x }));
|
|
200
|
+
const containerSize = 100;
|
|
201
|
+
|
|
202
|
+
const mapped = mapToPixelCoords(items, containerSize, {
|
|
203
|
+
spacing: 10,
|
|
204
|
+
reverse: true,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
expect(mapped[0]).toEqual({ location: 100, size: 0 });
|
|
208
|
+
expect(mapped[1]).toEqual({ location: 70, size: 30 });
|
|
209
|
+
expect(mapped[2]).toEqual({ location: 40, size: 20 });
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("Zero in the middle", () => {
|
|
213
|
+
const items = [10, 0, 20].map((x) => ({ px: x }));
|
|
214
|
+
const containerSize = 100;
|
|
215
|
+
|
|
216
|
+
const mapped = mapToPixelCoords(items, containerSize, {
|
|
217
|
+
spacing: 10,
|
|
218
|
+
reverse: true,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
expect(mapped[0]).toEqual({ location: 90, size: 10 });
|
|
222
|
+
expect(mapped[1]).toEqual({ location: 85, size: 0 });
|
|
223
|
+
expect(mapped[2]).toEqual({ location: 60, size: 20 });
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("Multiple zeroes in the middle", () => {
|
|
227
|
+
const items = [10, 0, 0, 0, 20].map((x) => ({ px: x }));
|
|
228
|
+
const containerSize = 100;
|
|
229
|
+
|
|
230
|
+
const mapped = mapToPixelCoords(items, containerSize, {
|
|
231
|
+
spacing: 10,
|
|
232
|
+
reverse: true,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
expect(mapped[0]).toEqual({ location: 90, size: 10 });
|
|
236
|
+
expect(mapped[1]).toEqual({ location: 87.5, size: 0 });
|
|
237
|
+
expect(mapped[2]).toEqual({ location: 85, size: 0 });
|
|
238
|
+
expect(mapped[3]).toEqual({ location: 82.5, size: 0 });
|
|
239
|
+
expect(mapped[4]).toEqual({ location: 60, size: 20 });
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test("Zero as last", () => {
|
|
243
|
+
const items = [10, 30, 0].map((x) => ({ px: x }));
|
|
244
|
+
const containerSize = 100;
|
|
245
|
+
|
|
246
|
+
const mapped = mapToPixelCoords(items, containerSize, {
|
|
247
|
+
spacing: 10,
|
|
248
|
+
reverse: true,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
expect(mapped[0]).toEqual({ location: 90, size: 10 });
|
|
252
|
+
expect(mapped[1]).toEqual({ location: 50, size: 30 });
|
|
253
|
+
expect(mapped[2]).toEqual({ location: 50, size: 0 });
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("Only a zero", () => {
|
|
257
|
+
const items = [0].map((x) => ({ px: x }));
|
|
258
|
+
const containerSize = 100;
|
|
259
|
+
|
|
260
|
+
const mapped = mapToPixelCoords(items, containerSize, {
|
|
261
|
+
spacing: 10,
|
|
262
|
+
reverse: true,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
expect(mapped[0]).toEqual({ location: 100, size: 0 });
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe("Utility fuctions", () => {
|
|
270
|
+
test("getMinimumSize", () => {
|
|
271
|
+
const items = [{ px: 100 }, { grow: 1 }, { grow: 9 }, { px: 200 }];
|
|
272
|
+
|
|
273
|
+
expect(getMinimumSize(items)).toEqual(300);
|
|
274
|
+
|
|
275
|
+
expect(getMinimumSize(items, { spacing: 10 })).toEqual(330);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("getMinimumSize, items include zeroes", () => {
|
|
279
|
+
const items = [
|
|
280
|
+
{ px: 100 },
|
|
281
|
+
{ px: 0, grow: 0 },
|
|
282
|
+
{ grow: 1 },
|
|
283
|
+
{ grow: 9 },
|
|
284
|
+
{ px: 200 },
|
|
285
|
+
];
|
|
286
|
+
|
|
287
|
+
expect(getMinimumSize(items)).toEqual(300);
|
|
288
|
+
|
|
289
|
+
expect(getMinimumSize(items, { spacing: 10 })).toEqual(330);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("isStretching", () => {
|
|
293
|
+
expect(isStretching([{ grow: 1 }])).toBeTruthy();
|
|
294
|
+
expect(isStretching([{ px: 1 }])).toBeFalsy();
|
|
295
|
+
});
|
|
296
|
+
});
|