@genome-spy/core 0.15.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +7 -7
  2. package/README.md +16 -0
  3. package/dist/index.js +42 -42
  4. package/dist/{genome-spy-schema.json → schema.json} +1824 -652
  5. package/dist/style.css +1 -1
  6. package/package.json +9 -7
  7. package/src/data/sources/dataUtils.js +50 -3
  8. package/src/data/sources/dynamicCallbackSource.js +2 -1
  9. package/src/data/sources/dynamicSource.js +2 -1
  10. package/src/data/sources/inlineSource.js +3 -5
  11. package/src/data/sources/namedSource.js +3 -7
  12. package/src/data/sources/urlSource.js +1 -1
  13. package/src/data/transforms/aggregate.js +1 -0
  14. package/src/data/transforms/flattenDelimited.js +6 -0
  15. package/src/data/transforms/regexFold.js +1 -1
  16. package/src/data/transforms/stack.js +3 -3
  17. package/src/embedApi.d.ts +59 -0
  18. package/src/encoder/accessor.js +6 -6
  19. package/src/encoder/encoder.js +47 -22
  20. package/src/genome/scaleIndex.d.ts +38 -0
  21. package/src/genome/scaleIndex.js +18 -52
  22. package/src/genome/scaleLocus.d.ts +11 -0
  23. package/src/genome/scaleLocus.js +12 -16
  24. package/src/genomeSpy.js +1 -1
  25. package/src/gl/dataToVertices.js +14 -6
  26. package/src/gl/includes/fp64-utils.js +10 -0
  27. package/src/gl/includes/scales.glsl +2 -0
  28. package/src/gl/webGLHelper.js +3 -1
  29. package/src/index.js +6 -28
  30. package/src/marks/link.js +1 -12
  31. package/src/marks/mark.js +27 -5
  32. package/src/marks/markUtils.js +41 -25
  33. package/src/marks/pointMark.js +5 -2
  34. package/src/marks/rule.js +11 -2
  35. package/src/scale/glslScaleGenerator.js +16 -29
  36. package/src/scale/scale.js +10 -0
  37. package/src/scale/ticks.js +11 -6
  38. package/src/spec/channel.d.ts +343 -43
  39. package/src/spec/data.d.ts +14 -3
  40. package/src/spec/root.d.ts +3 -8
  41. package/src/spec/scale.d.ts +18 -1
  42. package/src/spec/view.d.ts +12 -6
  43. package/src/tooltip/refseqGeneTooltipHandler.js +1 -0
  44. package/src/types/filetypes.d.ts +10 -0
  45. package/src/types/internmap.d.ts +22 -0
  46. package/src/types/vega-loader.d.ts +1 -0
  47. package/src/utils/addBaseUrl.js +19 -0
  48. package/src/utils/addBaseUrl.test.js +21 -0
  49. package/src/utils/arrayUtils.js +12 -6
  50. package/src/utils/cloner.js +5 -3
  51. package/src/utils/concatIterables.js +2 -2
  52. package/src/utils/domainArray.js +0 -8
  53. package/src/utils/propertyCoalescer.js +9 -4
  54. package/src/view/axisResolution.js +11 -6
  55. package/src/view/axisView.js +8 -5
  56. package/src/view/decoratorView.js +6 -3
  57. package/src/view/facetView.js +3 -0
  58. package/src/view/flowBuilder.js +2 -1
  59. package/src/view/renderingContext/svgViewRenderingContext.js +7 -3
  60. package/src/view/scaleResolution.js +52 -32
  61. package/src/view/testUtils.js +7 -4
  62. package/src/view/unitView.js +15 -9
  63. package/src/view/view.js +10 -8
  64. package/src/view/viewFactory.js +2 -0
  65. package/src/view/viewUtils.js +4 -5
  66. package/src/options.d.ts +0 -15
  67. package/src/utils/fisheye.js +0 -60
  68. package/src/utils/html.js +0 -23
  69. package/src/utils/html.test.js +0 -13
  70. package/src/view/channel.js +0 -5
@@ -10,6 +10,23 @@
10
10
 
11
11
  import { ChromosomalLocus } from "./genome";
12
12
 
13
+ export type ScaleType =
14
+ | "null"
15
+ | "linear"
16
+ | "log"
17
+ | "pow"
18
+ | "sqrt"
19
+ | "symlog"
20
+ | "identity"
21
+ | "sequential"
22
+ | "quantize"
23
+ | "threshold"
24
+ | "ordinal"
25
+ | "point"
26
+ | "band"
27
+ | "index"
28
+ | "locus";
29
+
13
30
  export interface Scale {
14
31
  /**
15
32
  * The name of the scale. Names are optional but allow the scales to be referenced and found with the API.
@@ -27,7 +44,7 @@ export interface Scale {
27
44
  *
28
45
  * __Default value:__ please see the [scale type table](https://vega.github.io/vega-lite/docs/scale.html#type).
29
46
  */
30
- type?: string; // ScaleType;
47
+ type?: ScaleType;
31
48
 
32
49
  /**
33
50
  * Customized domain values.
@@ -1,7 +1,13 @@
1
1
  import { Data } from "./data";
2
2
  import { Scale } from "./scale";
3
3
  import { TransformParams } from "./transform";
4
- import { Channel, Encoding, FacetFieldDef, PositionalChannel } from "./channel";
4
+ import {
5
+ Channel,
6
+ Encoding,
7
+ FacetFieldDef,
8
+ PrimaryPositionalChannel,
9
+ Type,
10
+ } from "./channel";
5
11
  import {
6
12
  FillAndStrokeProps,
7
13
  MarkConfigAndType,
@@ -30,7 +36,7 @@ export interface FacetMapping {
30
36
  * The opacity is interpolated between the specified stops.
31
37
  */
32
38
  export interface DynamicOpacity {
33
- channel?: PositionalChannel;
39
+ channel?: PrimaryPositionalChannel;
34
40
  /** Stops expressed as units (base pairs, for example) per pixel. */
35
41
  unitsPerPixel: number[];
36
42
  /** Opacity values that match the given stops. */
@@ -124,14 +130,14 @@ export interface LayerSpec extends ViewSpecBase, AggregateSamplesSpec {
124
130
  }
125
131
 
126
132
  export interface FacetSpec extends ViewSpecBase {
127
- facet: FacetFieldDef | FacetMapping;
133
+ facet: any; //FacetMapping | FacetFieldDef
128
134
  spec: LayerSpec | UnitSpec;
129
135
  columns?: number;
130
136
  spacing?: number;
131
137
  }
132
138
 
133
139
  export interface SampleAttributeDef {
134
- type: string;
140
+ type: Type; // TODO: Omit index/locus
135
141
  /** Color scale (primary) */
136
142
  scale?: Scale;
137
143
  barScale?: Scale;
@@ -170,7 +176,7 @@ export interface ResolveSpec {
170
176
 
171
177
  export type ContainerSpec = (
172
178
  | LayerSpec
173
- | FacetSpec
179
+ // | FacetSpec
174
180
  | SampleSpec
175
181
  | VConcatSpec
176
182
  | HConcatSpec
@@ -182,7 +188,7 @@ export type ContainerSpec = (
182
188
  export type ViewSpec =
183
189
  | UnitSpec
184
190
  | LayerSpec
185
- | FacetSpec
191
+ // | FacetSpec
186
192
  | SampleSpec
187
193
  | VConcatSpec
188
194
  | HConcatSpec
@@ -43,6 +43,7 @@ async function fetchGeneSummary(symbol) {
43
43
 
44
44
  console.log("Searching: " + symbol);
45
45
 
46
+ /** @type {RequestInit} */
46
47
  const opts = { mode: "cors" };
47
48
 
48
49
  const searchResult = await fetch(
@@ -2,3 +2,13 @@ declare module "*.svg" {
2
2
  const content: string;
3
3
  export default content;
4
4
  }
5
+
6
+ declare module "*.txt" {
7
+ const content: string;
8
+ export default content;
9
+ }
10
+
11
+ declare module "*.png" {
12
+ const value: any;
13
+ export = value;
14
+ }
@@ -0,0 +1,22 @@
1
+ // Source: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/d3-array/index.d.ts
2
+
3
+ declare module "internmap" {
4
+ /**
5
+ * The InternMap class extends the native JavaScript Map class, allowing Dates and other non-primitive keys by bypassing the SameValueZero algorithm when determining key equality.
6
+ */
7
+ export class InternMap<K = any, V = any> extends Map<K, V> {
8
+ constructor(
9
+ entries?: readonly (readonly [K, V])[] | null,
10
+ keyFunction?: KeyFunction
11
+ );
12
+ }
13
+
14
+ /**
15
+ * The InternSet class extends the native JavaScript Set class, allowing Dates and other non-primitive keys by bypassing the SameValueZero algorithm when determining key equality.
16
+ */
17
+ export class InternSet<T = any> extends Set<T> {
18
+ constructor(values?: readonly T[] | null, keyFunction?: KeyFunction);
19
+ }
20
+ }
21
+
22
+ type KeyFunction = (key: any) => string | number | bigint | boolean | symbol;
@@ -0,0 +1 @@
1
+ declare module "vega-loader";
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @param {string} url
3
+ * @param {string} baseUrl
4
+ */
5
+ export default function addBaseUrl(url, baseUrl) {
6
+ // Regex copied from vega-loader
7
+ if (!baseUrl || /^(data:|([A-Za-z]+:)?\/\/)/.test(url)) {
8
+ return url;
9
+ }
10
+
11
+ if (!url.startsWith("/")) {
12
+ if (!baseUrl.endsWith("/")) {
13
+ baseUrl += "/";
14
+ }
15
+ return baseUrl + url;
16
+ }
17
+
18
+ return url;
19
+ }
@@ -0,0 +1,21 @@
1
+ import addBaseUrl from "./addBaseUrl";
2
+
3
+ test("addBaseUrl adds baseUrl when needed", () => {
4
+ expect(addBaseUrl("foo.html", "https://site.com/")).toEqual(
5
+ "https://site.com/foo.html"
6
+ );
7
+ expect(addBaseUrl("foo.html", "https://site.com")).toEqual(
8
+ "https://site.com/foo.html"
9
+ );
10
+ expect(addBaseUrl("bar/foo.html", "https://site.com/")).toEqual(
11
+ "https://site.com/bar/foo.html"
12
+ );
13
+ expect(addBaseUrl("../foo.html", "https://site.com/bar/")).toEqual(
14
+ "https://site.com/bar/../foo.html"
15
+ );
16
+ });
17
+
18
+ test("addBaseUrl doesn't add baseUrl when not needed", () => {
19
+ expect(addBaseUrl("/foo.html", "https://site.com/")).toEqual("/foo.html");
20
+ expect(addBaseUrl("foo.html", undefined)).toEqual("foo.html");
21
+ });
@@ -1,14 +1,20 @@
1
1
  /**
2
- *
2
+ * @param {T[]} a
3
+ * @param {T[]} b
4
+ * @template T
5
+ */
6
+ export function shallowArrayEquals(a, b) {
7
+ return a.length == b.length && a.every((s, i) => a[i] === b[i]);
8
+ }
9
+
10
+ /**
3
11
  * @param {A[]} a
4
12
  * @param {B[]} b
5
- * @param {function(A):T} [aAccessor]
6
- * @param {function(B):T} [bAccessor]
13
+ * @param {function(A):T} aAccessor
14
+ * @param {function(B):T} bAccessor
7
15
  * @template A, B, T
8
16
  */
9
- export function shallowArrayEquals(a, b, aAccessor, bAccessor) {
10
- aAccessor = aAccessor || ((x) => x);
11
- bAccessor = bAccessor || ((x) => x);
17
+ export function shallowArrayEqualsWithAccessors(a, b, aAccessor, bAccessor) {
12
18
  return (
13
19
  a.length == b.length &&
14
20
  a.every((s, i) => aAccessor(a[i]) === bAccessor(b[i]))
@@ -7,14 +7,16 @@
7
7
  * https://mrale.ph/blog/2014/07/30/constructor-vs-objectcreate.html
8
8
  *
9
9
  * @param {T} template The template object that
10
- * @returns {function(T):T}
10
+ * @returns {(function(T):T) & { properties: string[] }}
11
11
  * @template T
12
12
  */
13
13
  export default function createCloner(template) {
14
14
  // TODO: Check that only properties, not methods get cloned
15
- const properties = Object.keys(template);
15
+ const properties = /** @type {string[]} */ (
16
+ Object.keys(template).filter((k) => typeof k == "string")
17
+ );
16
18
 
17
- const cloner = /** @type {function(T):T} */ (
19
+ const cloner = /** @type {(function(T):T) & { properties: string[] }} */ (
18
20
  new Function(
19
21
  "source",
20
22
  "return { " +
@@ -1,6 +1,6 @@
1
1
  /**
2
- *
3
- * @param {...Iterable} iterables
2
+ * @param {...Iterable<T>} iterables
3
+ * @template T
4
4
  */
5
5
  export default function concatIterables(...iterables) {
6
6
  if (iterables.length <= 0) {
@@ -194,14 +194,6 @@ export default function createDomain(type, initialDomain) {
194
194
  throw new Error("Unknown type: " + type);
195
195
  }
196
196
 
197
- /**
198
- *
199
- * @param {array} array
200
- */
201
- export function isDomainArray(array) {
202
- return array instanceof DomainArray;
203
- }
204
-
205
197
  /**
206
198
  * For unit tests
207
199
  *
@@ -2,16 +2,19 @@
2
2
  * Coalesces properties. Allows for creating chains of defaults without
3
3
  * using object destructuring, which may generate piles of garbage for GC.
4
4
  *
5
- * Still WIP. TODO: Efficient computed defaults.
5
+ * Still WIP.
6
+ * TODO: Efficient computed defaults.
7
+ * TODO: Make sense of the types
6
8
  *
7
9
  * @param {...function():T} sources
8
10
  * @returns {T}
9
11
  *
10
- * @template T
12
+ * @template {Record<string | symbol, any>} [T=object]
11
13
  */
12
14
  export default function coalesceProperties(...sources) {
15
+ /** @type {ProxyHandler<T>} */
13
16
  const handler = {
14
- get: function (target, prop, receiver) {
17
+ get(_target, prop, _receiver) {
15
18
  for (const source of sources) {
16
19
  const props = source();
17
20
  const value = props[prop];
@@ -22,7 +25,8 @@ export default function coalesceProperties(...sources) {
22
25
  return undefined;
23
26
  },
24
27
 
25
- has: function (target, prop, receiver) {
28
+ // @ts-ignore
29
+ has(target, prop, _receiver) {
26
30
  for (const source of sources) {
27
31
  const props = source();
28
32
  if (prop in props) {
@@ -33,5 +37,6 @@ export default function coalesceProperties(...sources) {
33
37
  },
34
38
  };
35
39
 
40
+ // @ts-ignore
36
41
  return new Proxy({}, handler);
37
42
  }
@@ -5,6 +5,7 @@ import {
5
5
  isExprDef,
6
6
  isFieldDef,
7
7
  isSecondaryChannel,
8
+ isValueDef,
8
9
  } from "../encoder/encoder";
9
10
  import { peek } from "../utils/arrayUtils";
10
11
  import coalesce from "../utils/coalesce";
@@ -22,7 +23,7 @@ export default class AxisResolution {
22
23
  */
23
24
  constructor(channel) {
24
25
  this.channel = channel;
25
- /** @type {import("./scaleResolution").ResolutionMember[]} The involved views */
26
+ /** @type {import("./scaleResolution").ResolutionMember<import("../spec/channel").PositionalChannel>[]} The involved views */
26
27
  this.members = [];
27
28
  }
28
29
 
@@ -35,7 +36,7 @@ export default class AxisResolution {
35
36
  * scales have been resolved.
36
37
  *
37
38
  * @param {UnitView} view
38
- * @param {import("../spec/channel").Channel} channel TODO: Do something for this
39
+ * @param {import("../spec/channel").PositionalChannel} channel TODO: Do something for this
39
40
  */
40
41
  pushUnitView(view, channel) {
41
42
  const newScaleResolution = view.getScaleResolution(this.channel);
@@ -56,10 +57,10 @@ export default class AxisResolution {
56
57
 
57
58
  getAxisProps() {
58
59
  return getCachedOrCall(this, "axisProps", () => {
59
- const propArray = this.members.map(
60
- (member) =>
61
- getChannelDefWithScale(member.view, member.channel).axis
62
- );
60
+ const propArray = this.members.map((member) => {
61
+ const channelDef = member.view.mark.encoding[member.channel];
62
+ return "axis" in channelDef && channelDef.axis;
63
+ });
63
64
 
64
65
  if (
65
66
  propArray.length > 0 &&
@@ -87,6 +88,10 @@ export default class AxisResolution {
87
88
  member.channel
88
89
  );
89
90
 
91
+ if (isValueDef(channelDef)) {
92
+ return undefined;
93
+ }
94
+
90
95
  // Retain nulls as they indicate that no title should be shown
91
96
 
92
97
  return {
@@ -2,14 +2,17 @@ import { validTicks, tickValues, tickFormat, tickCount } from "../scale/ticks";
2
2
  import LayerView from "./layerView";
3
3
  import { isNumber } from "vega-util";
4
4
  import smoothstep from "../utils/smoothstep";
5
- import { shallowArrayEquals } from "../utils/arrayUtils";
5
+ import {
6
+ shallowArrayEquals,
7
+ shallowArrayEqualsWithAccessors,
8
+ } from "../utils/arrayUtils";
6
9
  import { FlexDimensions } from "../utils/layout/flexLayout";
7
10
  import DynamicCallbackSource from "../data/sources/dynamicCallbackSource";
8
11
 
9
12
  const CHROM_LAYER_NAME = "chromosome_ticks_and_labels";
10
13
 
11
14
  /**
12
- * @typedef {import("../spec/channel").PositionalChannel} PositionalChannel
15
+ * @typedef {import("../spec/channel").PrimaryPositionalChannel} PositionalChannel
13
16
  * @typedef {import("../spec/view").GeometricDimension} GeometricDimension
14
17
  */
15
18
 
@@ -265,7 +268,7 @@ function generateTicks(axisProps, scale, axisLength, oldTicks = []) {
265
268
  : tickValues(scale, count);
266
269
 
267
270
  if (
268
- shallowArrayEquals(
271
+ shallowArrayEqualsWithAccessors(
269
272
  values,
270
273
  oldTicks,
271
274
  (v) => v,
@@ -428,7 +431,7 @@ function createAxis(axisProps) {
428
431
  },
429
432
  encoding: {
430
433
  [main]: { field: "value", type: "quantitative" },
431
- text: { field: "label", type: "quantitative" },
434
+ text: { field: "label" },
432
435
  },
433
436
  });
434
437
 
@@ -649,7 +652,7 @@ export function createGenomeAxis(axisProps) {
649
652
  },
650
653
  encoding: {
651
654
  [main + "2"]: { field: "continuousEnd", type: "locus" },
652
- text: { field: "name", type: "ordinal" },
655
+ text: { field: "name" },
653
656
  },
654
657
  };
655
658
  return labels;
@@ -6,7 +6,7 @@ import UnitView from "./unitView";
6
6
  import { ZERO_FLEXDIMENSIONS } from "../utils/layout/flexLayout";
7
7
 
8
8
  /**
9
- * @typedef {import("../spec/channel").PositionalChannel} PositionalChannel
9
+ * @typedef {import("../spec/channel").PrimaryPositionalChannel} PositionalChannel
10
10
  * @typedef {import("../spec/view").GeometricDimension} GeometricDimension
11
11
  */
12
12
 
@@ -384,7 +384,10 @@ export default class DecoratorView extends ContainerView {
384
384
  zDelta: 0,
385
385
  });
386
386
  }
387
- } else if (event.type == "mousedown" && event.uiEvent.button === 0) {
387
+ } else if (
388
+ event.type == "mousedown" &&
389
+ /** @type {MouseEvent} */ (event.uiEvent).button === 0
390
+ ) {
388
391
  const mouseEvent = /** @type {MouseEvent} */ (event.uiEvent);
389
392
  mouseEvent.preventDefault();
390
393
 
@@ -424,7 +427,7 @@ export default class DecoratorView extends ContainerView {
424
427
 
425
428
  _getZoomableResolutions() {
426
429
  return this._cache("zoomableResolutions", () => {
427
- /** @type {Record<import("../spec/channel").PositionalChannel, Set<import("./scaleResolution").default>>} */
430
+ /** @type {Record<import("../spec/channel").PrimaryPositionalChannel, Set<import("./scaleResolution").default>>} */
428
431
  const resolutions = {
429
432
  x: new Set(),
430
433
  y: new Set(),
@@ -1,3 +1,6 @@
1
+ // @ts-nocheck
2
+ // TODO: Enable when this is taken to use again
3
+
1
4
  import { isFacetFieldDef, isFacetMapping } from "./viewUtils";
2
5
  import ContainerView from "./containerView";
3
6
  import UnitView from "./unitView";
@@ -286,6 +286,7 @@ export function linearizeLocusAccess(view) {
286
286
  ...rewrittenEncoding,
287
287
  };
288
288
  // This is so ugly...
289
+ // @ts-ignore
289
290
  invalidate(view.mark, "encoding");
290
291
  },
291
292
  }
@@ -294,7 +295,7 @@ export function linearizeLocusAccess(view) {
294
295
 
295
296
  /**
296
297
  * @param {View} view
297
- * @param {Record<string, import("../spec/channel").ChannelDef>} [encoding]
298
+ * @param {Encoding} [encoding]
298
299
  * @returns {import("../spec/transform").CompareParams}
299
300
  */
300
301
  function getCompareParamsForView(view, encoding) {
@@ -8,8 +8,12 @@ import ViewRenderingContext from "./viewRenderingContext";
8
8
  * @typedef {import("../view").default} View
9
9
  */
10
10
  export default class SvgViewRenderingContext extends ViewRenderingContext {
11
- constructor() {
12
- super();
11
+ /**
12
+ *
13
+ * @param {import("../rendering").GlobalRenderingOptions} globalOptions
14
+ */
15
+ constructor(globalOptions) {
16
+ super(globalOptions);
13
17
 
14
18
  /** @type {import("../../utils/layout/rectangle").default} */
15
19
  this.coords = undefined;
@@ -39,7 +43,7 @@ export default class SvgViewRenderingContext extends ViewRenderingContext {
39
43
  this.svg.setAttributeNS(
40
44
  null,
41
45
  "viewBox",
42
- ["x", "y", "width", "height"].map((a) => viewBox[a]).join(" ")
46
+ [viewBox.x, viewBox.y, viewBox.width, viewBox.height].join(" ")
43
47
  );
44
48
  }
45
49