@genome-spy/core 0.14.1 → 0.17.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 +5030 -0
- package/dist/index.js +42 -42
- package/dist/style.css +1 -1
- package/package.json +5 -4
- 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 +2 -2
- 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 +9 -35
- 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/root.d.ts +1 -8
- 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 -5
- package/src/options.d.ts +0 -9
- 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/testUtils.js
CHANGED
|
@@ -15,15 +15,15 @@ import { ViewFactory } from "./viewFactory";
|
|
|
15
15
|
/** @type {<V extends View>(spec: ViewSpec, viewClass: { new(...args: any[]): V }, context?: ViewContext) => V} */
|
|
16
16
|
export function create(spec, viewClass, context = undefined) {
|
|
17
17
|
const viewTypeRegistry = new ViewFactory();
|
|
18
|
-
|
|
19
|
-
const c = {
|
|
18
|
+
|
|
19
|
+
const c = /** @type {ViewContext} */ ({
|
|
20
20
|
...(context || {}),
|
|
21
21
|
accessorFactory: new AccessorFactory(),
|
|
22
22
|
|
|
23
23
|
createView: function (spec, parent, defaultName) {
|
|
24
24
|
return viewTypeRegistry.createView(spec, c, parent, defaultName);
|
|
25
25
|
},
|
|
26
|
-
};
|
|
26
|
+
});
|
|
27
27
|
|
|
28
28
|
const view = c.createView(spec, null, "root");
|
|
29
29
|
|
|
@@ -40,7 +40,10 @@ export async function createAndInitialize(
|
|
|
40
40
|
viewClass,
|
|
41
41
|
context = undefined
|
|
42
42
|
) {
|
|
43
|
-
context =
|
|
43
|
+
context = /** @type {ViewContext} */ ({
|
|
44
|
+
...(context || {}),
|
|
45
|
+
dataFlow: new DataFlow(),
|
|
46
|
+
});
|
|
44
47
|
const view = create(spec, viewClass, context);
|
|
45
48
|
resolveScalesAndAxes(view);
|
|
46
49
|
await initializeData(view, context.dataFlow);
|
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
|
@@ -35,10 +35,9 @@ import { rollup } from "d3-array";
|
|
|
35
35
|
* @typedef {import("../spec/view").ImportSpec} ImportSpec
|
|
36
36
|
* @typedef {import("../spec/view").ImportConfig} ImportConfig
|
|
37
37
|
* @typedef {import("../spec/root").RootSpec} RootSpec
|
|
38
|
-
* @typedef {import("../spec/root").RootConfig} RootConfig
|
|
39
38
|
*
|
|
40
|
-
* @typedef {import("../spec/channel").FacetFieldDef} FacetFieldDef
|
|
41
39
|
* @typedef {import("../spec/view").FacetMapping} FacetMapping
|
|
40
|
+
* @typedef {import("../spec/channel").FacetFieldDef} FacetFieldDef
|
|
42
41
|
*/
|
|
43
42
|
|
|
44
43
|
/**
|
|
@@ -253,14 +252,14 @@ export async function initializeData(root, existingFlow) {
|
|
|
253
252
|
* @param {View} view
|
|
254
253
|
*/
|
|
255
254
|
export function findEncodedFields(view) {
|
|
256
|
-
/** @type {{view: UnitView, channel:
|
|
255
|
+
/** @type {{view: UnitView, channel: import("../spec/channel").Channel, field: import("../spec/channel").Field, type: import("../spec/channel").Type}[]} */
|
|
257
256
|
const fieldInfos = [];
|
|
258
257
|
|
|
259
258
|
view.visit((view) => {
|
|
260
259
|
if (view instanceof UnitView) {
|
|
261
260
|
const encoding = view.getEncoding();
|
|
262
261
|
for (const [channel, def] of Object.entries(encoding)) {
|
|
263
|
-
if (isFieldDef(def)) {
|
|
262
|
+
if (isFieldDef(def) && "type" in def) {
|
|
264
263
|
fieldInfos.push({
|
|
265
264
|
view,
|
|
266
265
|
channel,
|
|
@@ -292,7 +291,7 @@ async function loadExternalViewSpec(spec, baseUrl, viewContext) {
|
|
|
292
291
|
const url = spec.import.url;
|
|
293
292
|
|
|
294
293
|
const importedSpec = JSON.parse(
|
|
295
|
-
await loader.load(url).catch((e) => {
|
|
294
|
+
await loader.load(url).catch((/** @type {Error} */ e) => {
|
|
296
295
|
throw new Error(
|
|
297
296
|
`Could not load imported view spec: ${url} \nReason: ${e.message}`
|
|
298
297
|
);
|
package/src/options.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { TooltipHandler } from "./tooltip/tooltipHandler";
|
|
2
|
-
|
|
3
|
-
export interface EmbedOptions {
|
|
4
|
-
/** If true, don't display the toolbar. */
|
|
5
|
-
bare?: boolean;
|
|
6
|
-
|
|
7
|
-
/** Custom tooltip handlers. Use "default" to override the default handler */
|
|
8
|
-
tooltipHandlers?: Record<string, TooltipHandler>;
|
|
9
|
-
}
|
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
|
-
});
|