@genome-spy/core 0.15.0 → 0.16.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/LICENSE +7 -7
- package/README.md +16 -0
- package/dist/genome-spy-schema.json +816 -624
- package/dist/index.js +42 -42
- package/dist/style.css +1 -1
- package/package.json +3 -2
- package/src/data/sources/dataUtils.js +50 -3
- package/src/data/sources/dynamicCallbackSource.js +2 -1
- package/src/data/sources/dynamicSource.js +2 -1
- package/src/data/sources/inlineSource.js +3 -5
- package/src/data/sources/namedSource.js +3 -7
- package/src/data/sources/urlSource.js +1 -1
- package/src/data/transforms/aggregate.js +1 -0
- package/src/data/transforms/flattenDelimited.js +6 -0
- package/src/data/transforms/regexFold.js +1 -1
- package/src/data/transforms/stack.js +3 -3
- package/src/embedApi.d.ts +59 -0
- package/src/encoder/accessor.js +6 -6
- package/src/encoder/encoder.js +47 -22
- package/src/genome/scaleIndex.d.ts +38 -0
- package/src/genome/scaleIndex.js +18 -52
- package/src/genome/scaleLocus.d.ts +11 -0
- package/src/genome/scaleLocus.js +12 -16
- package/src/genomeSpy.js +1 -1
- package/src/gl/dataToVertices.js +14 -6
- package/src/gl/includes/fp64-utils.js +10 -0
- package/src/gl/includes/scales.glsl +2 -0
- package/src/gl/webGLHelper.js +3 -1
- package/src/index.js +6 -28
- package/src/marks/link.js +1 -12
- package/src/marks/mark.js +27 -5
- package/src/marks/markUtils.js +41 -25
- package/src/marks/pointMark.js +5 -2
- package/src/marks/rule.js +11 -2
- package/src/scale/glslScaleGenerator.js +16 -29
- package/src/scale/scale.js +10 -0
- package/src/scale/ticks.js +11 -6
- package/src/spec/channel.d.ts +343 -43
- package/src/spec/data.d.ts +14 -3
- package/src/spec/scale.d.ts +18 -1
- package/src/spec/view.d.ts +12 -6
- package/src/tooltip/refseqGeneTooltipHandler.js +1 -0
- package/src/types/filetypes.d.ts +10 -0
- package/src/types/internmap.d.ts +22 -0
- package/src/types/vega-loader.d.ts +1 -0
- package/src/utils/arrayUtils.js +12 -6
- package/src/utils/cloner.js +5 -3
- package/src/utils/concatIterables.js +2 -2
- package/src/utils/domainArray.js +0 -8
- package/src/utils/propertyCoalescer.js +9 -4
- package/src/view/axisResolution.js +11 -6
- package/src/view/axisView.js +8 -5
- package/src/view/decoratorView.js +6 -3
- package/src/view/facetView.js +3 -0
- package/src/view/flowBuilder.js +2 -1
- package/src/view/renderingContext/svgViewRenderingContext.js +7 -3
- package/src/view/scaleResolution.js +52 -32
- package/src/view/testUtils.js +7 -4
- package/src/view/unitView.js +15 -9
- package/src/view/view.js +10 -8
- package/src/view/viewFactory.js +2 -0
- package/src/view/viewUtils.js +4 -4
- package/src/options.d.ts +0 -15
- package/src/utils/fisheye.js +0 -60
- package/src/utils/html.js +0 -23
- package/src/utils/html.test.js +0 -13
- package/src/view/channel.js +0 -5
package/src/view/unitView.js
CHANGED
|
@@ -170,7 +170,18 @@ export default class UnitView extends ContainerView {
|
|
|
170
170
|
: new AxisResolution(targetChannel);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
// Looks silly, but keeps type checking happy
|
|
174
|
+
if (isPositionalChannel(channel)) {
|
|
175
|
+
view.resolutions[type][targetChannel].pushUnitView(
|
|
176
|
+
this,
|
|
177
|
+
channel
|
|
178
|
+
);
|
|
179
|
+
} else if (type == "scale") {
|
|
180
|
+
view.resolutions[type][targetChannel].pushUnitView(
|
|
181
|
+
this,
|
|
182
|
+
channel
|
|
183
|
+
);
|
|
184
|
+
}
|
|
174
185
|
}
|
|
175
186
|
}
|
|
176
187
|
|
|
@@ -223,16 +234,11 @@ export default class UnitView extends ContainerView {
|
|
|
223
234
|
}
|
|
224
235
|
|
|
225
236
|
const channelDef = this.mark.encoding[channel];
|
|
237
|
+
// TODO: Broken. Fix.
|
|
226
238
|
if (!isChannelDefWithScale(channelDef)) {
|
|
227
239
|
throw new Error("The channel has no scale, cannot get domain!");
|
|
228
240
|
}
|
|
229
241
|
|
|
230
|
-
const type = channelDef.type;
|
|
231
|
-
if (!type) {
|
|
232
|
-
throw new Error(`No data type for channel "${channel}"!`);
|
|
233
|
-
// TODO: Support defaults
|
|
234
|
-
}
|
|
235
|
-
|
|
236
242
|
return channelDef;
|
|
237
243
|
}
|
|
238
244
|
|
|
@@ -252,7 +258,7 @@ export default class UnitView extends ContainerView {
|
|
|
252
258
|
channelDef.resolutionChannel ?? channel
|
|
253
259
|
);
|
|
254
260
|
return createDomain(
|
|
255
|
-
channelDef.type,
|
|
261
|
+
channelDef.type ?? "nominal",
|
|
256
262
|
// Chrom/pos must be linearized first
|
|
257
263
|
scaleResolution.fromComplexInterval(specDomain)
|
|
258
264
|
);
|
|
@@ -274,7 +280,7 @@ export default class UnitView extends ContainerView {
|
|
|
274
280
|
*/
|
|
275
281
|
extractDataDomain(channel) {
|
|
276
282
|
const channelDef = this._validateDomainQuery(channel);
|
|
277
|
-
const type = channelDef.type;
|
|
283
|
+
const type = channelDef.type ?? "nominal"; // TODO: Should check that this is a channel without scale
|
|
278
284
|
|
|
279
285
|
/** @param {Channel} channel */
|
|
280
286
|
const extract = (channel) => {
|
package/src/view/view.js
CHANGED
|
@@ -142,13 +142,10 @@ export default class View {
|
|
|
142
142
|
*/
|
|
143
143
|
getSizeFromSpec() {
|
|
144
144
|
/**
|
|
145
|
-
*
|
|
146
145
|
* @param {"width" | "height"} dimension
|
|
147
146
|
* @return {SizeDef}
|
|
148
147
|
*/
|
|
149
148
|
const handleSize = (dimension) => {
|
|
150
|
-
/** @type {SizeDef} */
|
|
151
|
-
let sizeDef;
|
|
152
149
|
let value = this.spec[dimension];
|
|
153
150
|
|
|
154
151
|
if (isStepSize(value)) {
|
|
@@ -172,22 +169,27 @@ export default class View {
|
|
|
172
169
|
);
|
|
173
170
|
}
|
|
174
171
|
|
|
172
|
+
// TODO: Type guards maybe?
|
|
173
|
+
const _scale =
|
|
174
|
+
/** @type {import("d3-scale").ScaleBand<any> | import("../genome/scaleLocus").ScaleLocus | import("../genome/scaleIndex").ScaleIndex} */ (
|
|
175
|
+
scale
|
|
176
|
+
);
|
|
177
|
+
|
|
175
178
|
steps = bandSpace(
|
|
176
179
|
steps,
|
|
177
|
-
|
|
178
|
-
|
|
180
|
+
_scale.paddingInner(),
|
|
181
|
+
_scale.paddingOuter()
|
|
179
182
|
);
|
|
180
183
|
|
|
181
|
-
|
|
184
|
+
return { px: steps * stepSize, grow: 0 };
|
|
182
185
|
} else {
|
|
183
186
|
throw new Error(
|
|
184
187
|
"Cannot use 'step' size with missing scale!"
|
|
185
188
|
);
|
|
186
189
|
}
|
|
187
190
|
} else {
|
|
188
|
-
|
|
191
|
+
return (value && parseSizeDef(value)) ?? { px: 0, grow: 1 };
|
|
189
192
|
}
|
|
190
|
-
return sizeDef;
|
|
191
193
|
};
|
|
192
194
|
|
|
193
195
|
return this._cache(
|
package/src/view/viewFactory.js
CHANGED
|
@@ -123,8 +123,10 @@ export function isLayerSpec(spec) {
|
|
|
123
123
|
export function isFacetSpec(spec) {
|
|
124
124
|
return (
|
|
125
125
|
"facet" in spec &&
|
|
126
|
+
// @ts-expect-error
|
|
126
127
|
isObject(spec.facet) &&
|
|
127
128
|
"spec" in spec &&
|
|
129
|
+
// @ts-expect-error
|
|
128
130
|
isObject(spec.spec)
|
|
129
131
|
);
|
|
130
132
|
}
|
package/src/view/viewUtils.js
CHANGED
|
@@ -37,8 +37,8 @@ import { rollup } from "d3-array";
|
|
|
37
37
|
* @typedef {import("../spec/root").RootSpec} RootSpec
|
|
38
38
|
* @typedef {import("../spec/root").RootConfig} RootConfig
|
|
39
39
|
*
|
|
40
|
-
* @typedef {import("../spec/channel").FacetFieldDef} FacetFieldDef
|
|
41
40
|
* @typedef {import("../spec/view").FacetMapping} FacetMapping
|
|
41
|
+
* @typedef {import("../spec/channel").FacetFieldDef} FacetFieldDef
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -253,14 +253,14 @@ export async function initializeData(root, existingFlow) {
|
|
|
253
253
|
* @param {View} view
|
|
254
254
|
*/
|
|
255
255
|
export function findEncodedFields(view) {
|
|
256
|
-
/** @type {{view: UnitView, channel:
|
|
256
|
+
/** @type {{view: UnitView, channel: import("../spec/channel").Channel, field: import("../spec/channel").Field, type: import("../spec/channel").Type}[]} */
|
|
257
257
|
const fieldInfos = [];
|
|
258
258
|
|
|
259
259
|
view.visit((view) => {
|
|
260
260
|
if (view instanceof UnitView) {
|
|
261
261
|
const encoding = view.getEncoding();
|
|
262
262
|
for (const [channel, def] of Object.entries(encoding)) {
|
|
263
|
-
if (isFieldDef(def)) {
|
|
263
|
+
if (isFieldDef(def) && "type" in def) {
|
|
264
264
|
fieldInfos.push({
|
|
265
265
|
view,
|
|
266
266
|
channel,
|
|
@@ -292,7 +292,7 @@ async function loadExternalViewSpec(spec, baseUrl, viewContext) {
|
|
|
292
292
|
const url = spec.import.url;
|
|
293
293
|
|
|
294
294
|
const importedSpec = JSON.parse(
|
|
295
|
-
await loader.load(url).catch((e) => {
|
|
295
|
+
await loader.load(url).catch((/** @type {Error} */ e) => {
|
|
296
296
|
throw new Error(
|
|
297
297
|
`Could not load imported view spec: ${url} \nReason: ${e.message}`
|
|
298
298
|
);
|
package/src/options.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { TooltipHandler } from "./tooltip/tooltipHandler";
|
|
2
|
-
|
|
3
|
-
export interface EmbedOptions {
|
|
4
|
-
/**
|
|
5
|
-
* A function that allows retrieval of named data sources.
|
|
6
|
-
*
|
|
7
|
-
* TODO: Support dynamic updates, i.e., pushing new data.
|
|
8
|
-
*/
|
|
9
|
-
namedDataProvider?: (name: string) => any[];
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Custom tooltip handlers. Use "default" to override the default handler
|
|
13
|
-
*/
|
|
14
|
-
tooltipHandlers?: Record<string, TooltipHandler>;
|
|
15
|
-
}
|
package/src/utils/fisheye.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Based on:
|
|
3
|
-
* fisheye d3-plugin (c) Mike Bostock
|
|
4
|
-
* https://github.com/d3/d3-plugins/blob/master/fisheye/
|
|
5
|
-
*/
|
|
6
|
-
export default function () {
|
|
7
|
-
let radius = 200;
|
|
8
|
-
let distortion = 2;
|
|
9
|
-
let k0 = 1,
|
|
10
|
-
k1 = 1;
|
|
11
|
-
let focus = 0;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
*
|
|
15
|
-
* @param {number} x
|
|
16
|
-
*/
|
|
17
|
-
function fisheye(x) {
|
|
18
|
-
const dx = x - focus;
|
|
19
|
-
const dd = Math.abs(dx);
|
|
20
|
-
if (!dd || dd >= radius) return x;
|
|
21
|
-
const k = ((k0 * (1 - Math.exp(-dd * k1))) / dd) * 0.75 + 0.25;
|
|
22
|
-
return focus + dx * k;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function rescale() {
|
|
26
|
-
k0 = Math.exp(distortion);
|
|
27
|
-
k0 = (k0 / (k0 - 1)) * radius;
|
|
28
|
-
k1 = distortion / radius;
|
|
29
|
-
return fisheye;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @param {number} _
|
|
34
|
-
*/
|
|
35
|
-
fisheye.radius = function (_) {
|
|
36
|
-
if (!arguments.length) return radius;
|
|
37
|
-
radius = +_;
|
|
38
|
-
return rescale();
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @param {number} _
|
|
43
|
-
*/
|
|
44
|
-
fisheye.distortion = function (_) {
|
|
45
|
-
if (!arguments.length) return distortion;
|
|
46
|
-
distortion = +_;
|
|
47
|
-
return rescale();
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* @param {number} _
|
|
52
|
-
*/
|
|
53
|
-
fisheye.focus = function (_) {
|
|
54
|
-
if (!arguments.length) return focus;
|
|
55
|
-
focus = _;
|
|
56
|
-
return fisheye;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
return rescale();
|
|
60
|
-
}
|
package/src/utils/html.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/* https://stackoverflow.com/a/41699140/1547896 */
|
|
2
|
-
|
|
3
|
-
export function escapeHtml(str) {
|
|
4
|
-
var map = {
|
|
5
|
-
"&": "&",
|
|
6
|
-
"<": "<",
|
|
7
|
-
">": ">",
|
|
8
|
-
'"': """,
|
|
9
|
-
"'": "'",
|
|
10
|
-
};
|
|
11
|
-
return str.replace(/[&<>"']/g, (m) => map[m]);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function decodeHtml(str) {
|
|
15
|
-
var map = {
|
|
16
|
-
"&": "&",
|
|
17
|
-
"<": "<",
|
|
18
|
-
">": ">",
|
|
19
|
-
""": '"',
|
|
20
|
-
"'": "'",
|
|
21
|
-
};
|
|
22
|
-
return str.replace(/&|<|>|"|'/g, (m) => map[m]);
|
|
23
|
-
}
|
package/src/utils/html.test.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import * as html from "./html";
|
|
2
|
-
|
|
3
|
-
test("Escape HTML", () => {
|
|
4
|
-
expect(html.escapeHtml('< "x" & "y" >')).toEqual(
|
|
5
|
-
"< "x" & "y" >"
|
|
6
|
-
);
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
test("Decode HTML", () => {
|
|
10
|
-
expect(
|
|
11
|
-
html.decodeHtml("< "x" & "y" >")
|
|
12
|
-
).toEqual('< "x" & "y" >');
|
|
13
|
-
});
|