higlass 1.12.4 → 1.13.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.
Files changed (87) hide show
  1. package/README.md +3 -3
  2. package/app/globals.d.ts +11 -0
  3. package/app/missing-types.d.ts +21 -0
  4. package/app/scripts/CenterTrack.jsx +0 -1
  5. package/app/scripts/DraggableDiv.jsx +0 -1
  6. package/app/scripts/GenomePositionSearchBox.jsx +0 -1
  7. package/app/scripts/TrackRenderer.jsx +405 -89
  8. package/app/scripts/VerticalTiledPlot.jsx +0 -1
  9. package/app/scripts/configs/default-tracks-for-datatype.js +3 -2
  10. package/app/scripts/configs/primitives.js +3 -2
  11. package/app/scripts/configs/tracks-info-by-type.js +4 -1
  12. package/app/scripts/configs/tracks-info.js +25 -1
  13. package/app/scripts/types.ts +79 -0
  14. package/app/scripts/utils/abs-to-chr.js +16 -1
  15. package/app/scripts/utils/accessor-transposition.js +5 -4
  16. package/app/scripts/utils/add-arrays.js +9 -7
  17. package/app/scripts/utils/add-class.js +4 -2
  18. package/app/scripts/utils/add-event-listener-once.js +9 -3
  19. package/app/scripts/utils/background-task-scheduler.js +58 -2
  20. package/app/scripts/utils/base64-to-canvas.js +12 -5
  21. package/app/scripts/utils/chr-to-abs.js +10 -0
  22. package/app/scripts/utils/chrom-info-bisector.js +4 -1
  23. package/app/scripts/utils/clone-event.js +11 -4
  24. package/app/scripts/utils/color-to-hex.js +8 -0
  25. package/app/scripts/utils/color-to-rgba.js +8 -0
  26. package/app/scripts/utils/data-to-genomic-loci.js +13 -1
  27. package/app/scripts/utils/debounce.js +16 -11
  28. package/app/scripts/utils/dec-to-hex-str.js +7 -0
  29. package/app/scripts/utils/dict-from-tuples.js +11 -3
  30. package/app/scripts/utils/dict-items.js +15 -0
  31. package/app/scripts/utils/dict-keys.js +11 -1
  32. package/app/scripts/utils/dict-values.js +7 -0
  33. package/app/scripts/utils/download.js +14 -11
  34. package/app/scripts/utils/flatten.js +5 -2
  35. package/app/scripts/utils/for-each.js +7 -5
  36. package/app/scripts/utils/forward-event.js +3 -2
  37. package/app/scripts/utils/genome-loci-to-pixels.js +8 -0
  38. package/app/scripts/utils/genomic-range-to-chromosome-chunks.js +13 -6
  39. package/app/scripts/utils/get-aggregation-function.js +10 -2
  40. package/app/scripts/utils/get-element-dim.js +6 -0
  41. package/app/scripts/utils/gradient.js +14 -0
  42. package/app/scripts/utils/has-class.js +6 -4
  43. package/app/scripts/utils/hex-string-to-int.js +11 -4
  44. package/app/scripts/utils/index.js +1 -0
  45. package/app/scripts/utils/into-the-void.js +2 -1
  46. package/app/scripts/utils/is-track-or-child-track.js +6 -0
  47. package/app/scripts/utils/is-track-range-selectable.js +10 -1
  48. package/app/scripts/utils/is-within.js +9 -7
  49. package/app/scripts/utils/lat-to-y.js +6 -3
  50. package/app/scripts/utils/lng-to-x.js +4 -3
  51. package/app/scripts/utils/map.js +5 -2
  52. package/app/scripts/utils/max-non-zero.js +6 -0
  53. package/app/scripts/utils/max.js +4 -3
  54. package/app/scripts/utils/min-non-zero.js +6 -0
  55. package/app/scripts/utils/min.js +4 -3
  56. package/app/scripts/utils/mod.js +4 -3
  57. package/app/scripts/utils/numericify-version.js +5 -0
  58. package/app/scripts/utils/obj-vals.js +3 -2
  59. package/app/scripts/utils/or.js +4 -3
  60. package/app/scripts/utils/parse-chromsizes-rows.js +26 -5
  61. package/app/scripts/utils/q.js +3 -2
  62. package/app/scripts/utils/rad-to-deg.js +3 -2
  63. package/app/scripts/utils/reduce.js +2 -2
  64. package/app/scripts/utils/rel-to-abs-chrom-pos.js +10 -0
  65. package/app/scripts/utils/remove-class.js +3 -2
  66. package/app/scripts/utils/reset-d3-brush-style.js +7 -2
  67. package/app/scripts/utils/rgb-to-hex.js +9 -0
  68. package/app/scripts/utils/scales-center-and-k.js +5 -3
  69. package/app/scripts/utils/scales-to-genome-loci.js +10 -0
  70. package/app/scripts/utils/selected-items-to-cum-weights.js +12 -4
  71. package/app/scripts/utils/selected-items-to-size.js +5 -2
  72. package/app/scripts/utils/show-mouse-position.js +62 -19
  73. package/app/scripts/utils/some.js +6 -4
  74. package/app/scripts/utils/sum.js +4 -3
  75. package/app/scripts/utils/throttle-and-debounce.js +15 -6
  76. package/app/scripts/utils/tile-to-canvas.js +8 -4
  77. package/app/scripts/utils/timeout.js +2 -0
  78. package/app/scripts/utils/to-void.js +2 -0
  79. package/app/scripts/utils/total-track-pixel-height.js +11 -12
  80. package/app/scripts/utils/trim-trailing-slash.js +3 -2
  81. package/app/scripts/utils/type-guards.js +17 -0
  82. package/app/scripts/utils/value-to-color.js +7 -6
  83. package/app/scripts/utils/visit-positioned-tracks.js +16 -11
  84. package/app/scripts/utils/visit-tracks.js +12 -13
  85. package/dist/hglib.js +204 -131
  86. package/dist/hglib.min.js +70 -70
  87. package/package.json +16 -5
@@ -1,4 +1,3 @@
1
- // @ts-check
2
1
  import PropTypes from 'prop-types';
3
2
  import React from 'react';
4
3
 
@@ -1,4 +1,5 @@
1
- export const DEFAULT_TRACKS_FOR_DATATYPE = {
1
+ // @ts-check
2
+ export const DEFAULT_TRACKS_FOR_DATATYPE = /** @type {const} */ ({
2
3
  chromsizes: {
3
4
  center: '2d-chromosome-grid',
4
5
  top: 'chromosome-labels',
@@ -34,6 +35,6 @@ export const DEFAULT_TRACKS_FOR_DATATYPE = {
34
35
  left: 'vertical-bedlike',
35
36
  right: 'vertical-bedlike',
36
37
  },
37
- };
38
+ });
38
39
 
39
40
  export default DEFAULT_TRACKS_FOR_DATATYPE;
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  export const DEFAULT_VIEW_MARGIN = 0;
2
3
 
3
4
  export const DEFAULT_VIEW_PADDING = 5;
@@ -32,7 +33,7 @@ export const VIEW_HEADER_MED_WIDTH_SEARCH_BAR = 400;
32
33
 
33
34
  export const VIEW_HEADER_MIN_WIDTH_SEARCH_BAR = 300;
34
35
 
35
- export const TRACK_LOCATIONS = [
36
+ export const TRACK_LOCATIONS = /** @type {const} */ ([
36
37
  'top',
37
38
  'left',
38
39
  'right',
@@ -40,7 +41,7 @@ export const TRACK_LOCATIONS = [
40
41
  'center',
41
42
  'whole',
42
43
  'gallery',
43
- ];
44
+ ]);
44
45
 
45
46
  export const MIN_HORIZONTAL_HEIGHT = 20;
46
47
 
@@ -1,5 +1,8 @@
1
+ // @ts-check
1
2
  import { TRACKS_INFO } from './tracks-info';
2
3
 
4
+ /** @typedef {Record<string, import('./tracks-info').TrackInfo>} TracksInfoByType */
5
+
3
6
  export const TRACKS_INFO_BY_TYPE = TRACKS_INFO.reduce((tracksByType, track) => {
4
7
  tracksByType[track.type] = track;
5
8
  if (track.aliases) {
@@ -8,6 +11,6 @@ export const TRACKS_INFO_BY_TYPE = TRACKS_INFO.reduce((tracksByType, track) => {
8
11
  }
9
12
  }
10
13
  return tracksByType;
11
- }, {});
14
+ }, /** @type {TracksInfoByType} */ ({}));
12
15
 
13
16
  export default TRACKS_INFO_BY_TYPE;
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  import { THEME_DARK } from './themes';
2
3
 
3
4
  import {
@@ -14,6 +15,28 @@ import {
14
15
  svgGeoMapIcon,
15
16
  } from '../icons';
16
17
 
18
+ /**
19
+ * @typedef TrackInfo
20
+ * @property {string} type
21
+ * @property {string | string[]} datatype
22
+ * @property {string} orientation
23
+ * @property {string[]=} aliases
24
+ * @property {boolean=} local
25
+ * @property {boolean=} rotatable
26
+ * @property {boolean=} hidden
27
+ * @property {string=} name
28
+ * @property {HTMLElement | null | string=} thumbnail
29
+ * @property {string[]=} availableOptions
30
+ * @property {Record<string, unknown>=} defaultOptions
31
+ * @property {boolean=} exportable
32
+ * @property {Record<string, Record<string, unknown>>=} defaultOptionsByTheme
33
+ * @property {number=} minHeight
34
+ * @property {number=} defaultHeight
35
+ * @property {number=} defaultWidth
36
+ * @property {string=} chromInfoPath
37
+ * @property {boolean=} projection
38
+ */
39
+
17
40
  const osm = {
18
41
  type: 'osm-tiles',
19
42
  datatype: ['map-tiles'],
@@ -72,6 +95,7 @@ const mapbox = {
72
95
  },
73
96
  };
74
97
 
98
+ /** @type {TrackInfo[]} */
75
99
  export const TRACKS_INFO = [
76
100
  osm,
77
101
  { ...osm, type: 'osm' },
@@ -682,7 +706,7 @@ export const TRACKS_INFO = [
682
706
  aliases: ['horizontal-1d-value-interval', 'vertical-1d-value-interval'],
683
707
  datatype: ['bed-value'],
684
708
  local: false,
685
- orientation: ['1d-horizontal'],
709
+ orientation: '1d-horizontal',
686
710
  rotatable: true,
687
711
  name: '1D Rectangles',
688
712
  availableOptions: [
@@ -0,0 +1,79 @@
1
+ /* eslint-disable */
2
+
3
+ export type Scale = import("d3-scale").ScaleContinuousNumeric<number, number>;
4
+
5
+ export type TrackPosition = typeof import('./configs/primitives').TRACK_LOCATIONS[number];
6
+
7
+ export type ChromInfo<Name extends string = string> = {
8
+ cumPositions: { id?: number; pos: number; chr: Name }[];
9
+ chrPositions: Record<Name, { pos: number }>;
10
+ chromLengths: Record<Name, number>;
11
+ totalLength: number;
12
+ };
13
+
14
+ export type UnknownTrackConfig = {
15
+ uid: string;
16
+ options: Record<string, unknown>;
17
+ type: string;
18
+ position: TrackPosition;
19
+ data?: Record<string, unknown>;
20
+ server: string;
21
+ tilesetUid: string;
22
+ coordSystem?: unknown;
23
+ x?: number;
24
+ y?: number;
25
+ chromInfoPath: string;
26
+ projectionXDomain?: [number, number];
27
+ projectionYDomain?: [number, number];
28
+ registerViewportChanged?: unknown;
29
+ removeViewportChanged?: unknown;
30
+ setDomainsCallback?: unknown;
31
+ }
32
+
33
+ export type CombinedTrackConfig = {
34
+ uid: string;
35
+ options: Record<string, unknown>;
36
+ contents: TrackConfig[];
37
+ type: 'combined';
38
+ position: TrackPosition;
39
+ data?: Record<string, unknown>;
40
+ server: string;
41
+ tilesetUid: string;
42
+ coordSystem?: unknown;
43
+ x?: number;
44
+ y?: number;
45
+ chromInfoPath: string;
46
+ projectionXDomain?: [number, number];
47
+ projectionYDomain?: [number, number];
48
+ registerViewportChanged?: unknown;
49
+ removeViewportChanged?: unknown;
50
+ setDomainsCallback?: unknown;
51
+ }
52
+
53
+ export type TrackConfig = UnknownTrackConfig | CombinedTrackConfig;
54
+
55
+ export type TrackVisitor = {
56
+ (track: TrackConfig, position: null | TrackPosition): void;
57
+ }
58
+
59
+ type ZoomedFunction = {
60
+ (xScale: Scale, yScale: Scale, k?: number, x?: number, y?: number, xPosition?: number, yPosition?: number): void;
61
+ }
62
+
63
+ export interface TrackObject {
64
+ draw(): void;
65
+ rerender(options: unknown): void;
66
+ delayDrawing: boolean;
67
+ childTracks?: TrackObject[];
68
+ createdTracks: Record<string, TrackObject>;
69
+ refScalesChanged(x: Scale, y: Scale): void;
70
+ position: [number, number];
71
+ dimensions: [number, number];
72
+ updateContents(contents: TrackConfig[], x: unknown): TrackObject;
73
+ zoomed: ZoomedFunction;
74
+ setPosition(position: [number, number]): void;
75
+ setDimensions(dimensions: [number, number]): void;
76
+ remove(): void;
77
+ movedY(extent: number): void;
78
+ zoomedY(yPosition: number, wheelDelta: number): void;
79
+ }
@@ -1,5 +1,18 @@
1
+ // @ts-check
1
2
  import chromInfoBisector from './chrom-info-bisector';
2
3
 
4
+ /**
5
+ * @template {string} Name
6
+ * @typedef {[name: Name, pos: number, offset: number, insertPoint: number ]} ChromosomePosition
7
+ */
8
+
9
+ /**
10
+ * Convert an absolute genome position to a chromosome position.
11
+ * @template {string} Name
12
+ * @param {number} absPosition - Absolute genome position.
13
+ * @param {import('../types').ChromInfo<Name>} chromInfo - Chromosome info object.
14
+ * @return {ChromosomePosition<Name> | null} The chromosome position.
15
+ */
3
16
  const absToChr = (absPosition, chromInfo) => {
4
17
  if (!chromInfo || !chromInfo.cumPositions || !chromInfo.cumPositions.length) {
5
18
  return null;
@@ -9,7 +22,9 @@ const absToChr = (absPosition, chromInfo) => {
9
22
  const lastChr = chromInfo.cumPositions[chromInfo.cumPositions.length - 1].chr;
10
23
  const lastLength = chromInfo.chromLengths[lastChr];
11
24
 
12
- insertPoint -= insertPoint > 0 && 1;
25
+ if (insertPoint > 0) {
26
+ insertPoint -= 1;
27
+ }
13
28
 
14
29
  let chrPosition = Math.floor(
15
30
  absPosition - chromInfo.cumPositions[insertPoint].pos,
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  /**
2
4
  * Factory function for a transposition accessor for a 2D matrix in form of a 1D
3
5
  * array.
@@ -6,10 +8,9 @@
6
8
  * i^T = column * i + row
7
9
  * where column: `Math.floor(i / x)` and row: `Math.floor(i / x)`
8
10
  *
9
- * @param {Integer} x X dimension of the 2D matrix
10
- * @param {Integer} y Y dimension of the 2D matrix
11
- * @return {Function} Acessor function converting the orignal 1D index into
12
- * the transposed index
11
+ * @param {number} x - X dimension of the 2D matrix
12
+ * @param {number} y - Y dimension of the 2D matrix
13
+ * @return {(index: number) => number} Accessor function converting the orignal 1D index into the transposed index
13
14
  */
14
15
  const accessorTransposition = (x, y) => (i) => (i % x) * y + Math.floor(i / x);
15
16
 
@@ -1,4 +1,7 @@
1
- const STD_ACC = (i) => i;
1
+ // @ts-check
2
+
3
+ /** @type {<T>(index: T) => T} */
4
+ const identity = (i) => i;
2
5
 
3
6
  /**
4
7
  * Add two 1D arrays
@@ -11,13 +14,12 @@ const STD_ACC = (i) => i;
11
14
  * addArrays([1,2,3], [2,3,4,5]) === [3,5,7]
12
15
  * ```
13
16
  *
14
- * @param {Array} a1 First array of numbers
15
- * @param {Array} a2 Second array of numbers
16
- * @param {Function} accessor Allows to use custom accessors. This can be
17
- * useful when a2 represents a transformed (e.g., transposed matrix).
18
- * @return {Array} Combination of both arrays.
17
+ * @param {Array<number>} a1 - First array of numbers
18
+ * @param {Array<number>} a2 - Second array of numbers
19
+ * @param {(index: number) => number} accessor - Allows to use custom accessors. This can be useful when a2 represents a transformed (e.g., transposed matrix).
20
+ * @return {Array<number>} - Combination of both arrays.
19
21
  */
20
- const addArrays = (a1, a2, accessor = STD_ACC) =>
22
+ const addArrays = (a1, a2, accessor = identity) =>
21
23
  a1.map((val, i) => val + a2[accessor(i)]);
22
24
 
23
25
  export default addArrays;
@@ -1,11 +1,13 @@
1
+ // @ts-check
1
2
  import hasClass from './has-class';
2
3
 
3
4
  const XMLNS = 'http://www.w3.org/2000/svg';
4
5
 
5
6
  /**
6
7
  * Method to add a class name to an HTML or SVG element.
7
- * @param {object} el HTML or SVG element to add a class to.
8
- * @param {string} className The class name to be added.
8
+ * @param {Element} el - HTML or SVG element to add a class to.
9
+ * @param {string} className - The class name to be added.
10
+ * @returns {void}
9
11
  */
10
12
  const addClass = (el, className) => {
11
13
  if (el.namespaceURI === XMLNS) {
@@ -1,10 +1,16 @@
1
+ // @ts-check
2
+
1
3
  /**
2
4
  * Add an event listener that fires only once and auto-removes itself
3
- * @param {object} element DOM element to assign listener to.
4
- * @param {string} eventName Event name to listen to.
5
- * @param {function} f Event callback function.
5
+ *
6
+ * @template {keyof HTMLElementEventMap} K
7
+ *
8
+ * @param {HTMLElement} element - DOM element to assign listener to.
9
+ * @param {K} eventName - Event name to listen to.
10
+ * @param {(event: HTMLElementEventMap[K]) => void} f - Event callback function.
6
11
  */
7
12
  const addEventListenerOnce = (element, eventName, f) => {
13
+ /** @type {(event: HTMLElementEventMap[K]) => void} */
8
14
  const callback = (event) => {
9
15
  f(event);
10
16
  element.removeEventListener(eventName, callback);
@@ -1,10 +1,63 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @template T
5
+ * @typedef DataTask
6
+ * @property {T} data
7
+ * @property {(data: T) => void} handler
8
+ * @property {string=} trackId
9
+ */
10
+
11
+ /**
12
+ * @typedef NullDataTask
13
+ * @property {null} data
14
+ * @property {() => void} handler
15
+ * @property {string=} trackId
16
+ */
17
+
18
+ /** @typedef {DataTask<any> | NullDataTask} Task */
19
+
20
+ /**
21
+ * @param {Task} task
22
+ * @return {task is NullDataTask}
23
+ */
24
+ function isNullDataTask(task) {
25
+ return task.data === null;
26
+ }
27
+
28
+ /**
29
+ */
1
30
  class BackgroundTaskScheduler {
2
31
  constructor() {
32
+ /** @type {Task[]} */
3
33
  this.taskList = [];
4
34
  this.taskHandle = null;
5
35
  this.requestIdleCallbackTimeout = 300;
6
36
  }
7
37
 
38
+ /**
39
+ * @template T
40
+ * @overload
41
+ * @param {(data: T) => void} taskHandler
42
+ * @param {T} taskData
43
+ * @param {string | null=} trackId
44
+ * @return {void}
45
+ */
46
+ /**
47
+ * If `taskData` is `null` the `taskHandler` will eventaully be called without any arguments.
48
+ *
49
+ * @overload
50
+ * @param {() => void} taskHandler
51
+ * @param {null} taskData
52
+ * @param {string | null=} trackId
53
+ * @return {void}
54
+ */
55
+ /**
56
+ * @param {(data: any) => void} taskHandler
57
+ * @param {any} taskData
58
+ * @param {string | null} trackId
59
+ * @return {void}
60
+ */
8
61
  enqueueTask(taskHandler, taskData, trackId = null) {
9
62
  if (trackId === null) {
10
63
  this.taskList.push({
@@ -30,6 +83,9 @@ class BackgroundTaskScheduler {
30
83
  }
31
84
  }
32
85
 
86
+ /**
87
+ * @param {{ timeRemaining(): number, didTimeout: boolean }} deadline
88
+ */
33
89
  runTaskQueue(deadline) {
34
90
  while (
35
91
  (deadline.timeRemaining() > 0 || deadline.didTimeout) &&
@@ -37,9 +93,9 @@ class BackgroundTaskScheduler {
37
93
  ) {
38
94
  const task = this.taskList.shift();
39
95
 
40
- if (task.data === null) {
96
+ if (task && isNullDataTask(task)) {
41
97
  task.handler();
42
- } else {
98
+ } else if (task) {
43
99
  task.handler(task.data);
44
100
  }
45
101
  }
@@ -1,9 +1,11 @@
1
+ // @ts-check
2
+
1
3
  /**
2
4
  * Convert a base64 encoded image into a canvas object.
3
- * @param {string} base64 Base64 string encoding the image.
4
- * @param {number} width Custom width for the canvas object.
5
- * @param {number} height Custom height for the canvas object.
6
- * @return {object} The converted canvas object
5
+ * @param {string} base64 - Base64 string encoding the image.
6
+ * @param {number} width - Custom width for the canvas object.
7
+ * @param {number} height - Custom height for the canvas object.
8
+ * @return {Promise<HTMLCanvasElement>} The converted canvas object
7
9
  */
8
10
  const base64ToCanvas = (base64, width, height) => {
9
11
  const canvas = document.createElement('canvas');
@@ -13,7 +15,12 @@ const base64ToCanvas = (base64, width, height) => {
13
15
  img.onload = () => {
14
16
  canvas.width = width || img.width;
15
17
  canvas.height = height || img.height;
16
- canvas.getContext('2d').drawImage(img, 0, 0);
18
+ const context = canvas.getContext('2d');
19
+ if (!context) {
20
+ reject(new Error('Could not get canvas context'));
21
+ return;
22
+ }
23
+ context.drawImage(img, 0, 0);
17
24
  resolve(canvas);
18
25
  };
19
26
 
@@ -1,3 +1,13 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * Convert a chromosome position to an absolute genome position.
5
+ *
6
+ * @template {string} Name
7
+ * @param {Name} chrom - Chromosome name
8
+ * @param {number} chromPos - Chromosome position
9
+ * @param {import('../types').ChromInfo<Name>} chromInfo - Chromosome info object
10
+ */
1
11
  const chrToAbs = (chrom, chromPos, chromInfo) =>
2
12
  chromInfo.chrPositions[chrom].pos + chromPos;
3
13
 
@@ -1,5 +1,8 @@
1
+ // @ts-check
1
2
  import { bisector } from 'd3-array';
2
3
 
3
- const chromInfoBisector = bisector((d) => d.pos).left;
4
+ const chromInfoBisector = bisector(
5
+ (/** @type {{ pos: number }} */ d) => d.pos,
6
+ ).left;
4
7
 
5
8
  export default chromInfoBisector;
@@ -1,10 +1,17 @@
1
+ // @ts-check
2
+
1
3
  /**
2
- * Clone an event by invoking the source event's constructor and passing in
3
- * the source event.
4
- * @param {object} event Source event to be cloned.
5
- * @return {object} Cloned event
4
+ * Clone an event by invoking the source event's constructor and passing in the source event.
5
+ *
6
+ * @template {Event & { sourceUid?: string, forwarded?: boolean }} TEvent
7
+ * @param {TEvent} event - Source event to be cloned.
8
+ * @return {TEvent} Cloned event
6
9
  */
7
10
  const cloneEvent = (event) => {
11
+ if (!(event instanceof Event)) {
12
+ throw new Error('Event must be an instance of Event');
13
+ }
14
+ // @ts-expect-error - TS doesn't know about the constructor property.
8
15
  const newEvent = new event.constructor(event.type, event);
9
16
  newEvent.sourceUid = event.sourceUid;
10
17
  newEvent.forwarded = event.forwarded;
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  import { color } from 'd3-color';
2
3
 
3
4
  // Configs
@@ -6,8 +7,15 @@ import { GLOBALS } from '../configs';
6
7
  /**
7
8
  * Convert a regular color value (e.g. 'red', '#FF0000', 'rgb(255,0,0)') to a
8
9
  * hex value which is legible by PIXI
10
+ *
11
+ * @param {string} colorValue - Color value to convert
12
+ * @return {number} Hex value
9
13
  */
10
14
  const colorToHex = (colorValue) => {
15
+ /** @type {import('d3-color').RGBColor} */
16
+ // @ts-expect-error - FIXME: `color` can return many different types
17
+ // depending on the string input. We should probably use a different
18
+ // the more strict `rgb` function instead?
11
19
  const c = color(colorValue);
12
20
  const hex = GLOBALS.PIXI.utils.rgb2hex([
13
21
  c.r / 255.0,
@@ -1,13 +1,21 @@
1
+ // @ts-check
1
2
  import { color } from 'd3-color';
2
3
 
3
4
  /**
4
5
  * Convert a regular color value (e.g. 'red', '#FF0000', 'rgb(255,0,0)') to a
5
6
  * RGBA array, with support for the value "transparent".
7
+ *
8
+ * @param {string} colorValue - An RGB(A) color value to convert.
9
+ * @return {[r: number, g: number, b: number, a: number]} An RGBA array.
6
10
  */
7
11
  const colorToRgba = (colorValue) => {
8
12
  if (colorValue === 'transparent') {
9
13
  return [255, 255, 255, 0];
10
14
  }
15
+ /** @type {import('d3-color').RGBColor} */
16
+ // @ts-expect-error - FIXME: `color` can return many different types
17
+ // depending on the string input. We should probably use a different
18
+ // the more strict `rgb` function instead?
11
19
  const c = color(colorValue);
12
20
  return [c.r, c.g, c.b, 255];
13
21
  };
@@ -1,9 +1,21 @@
1
+ // @ts-check
1
2
  import absToChr from './abs-to-chr';
2
3
 
4
+ /** @typedef {[startChromName: string, startChromPos: number, endChromName: string, endChromPos: number]} GenomicLoci */
5
+
6
+ /**
7
+ * Convert a pair of data coordinates to genomic coordinates.
8
+ * @param {number} x0 - The first data coordinate.
9
+ * @param {number} x1 - The second data coordinate.
10
+ * @param {import('../types').ChromInfo} chromInfo - The chromosome info object.
11
+ * @returns {GenomicLoci} The genomic coordinates.
12
+ */
3
13
  const dataToGenomicLoci = (x0, x1, chromInfo) => {
4
14
  const gX0 = absToChr(x0, chromInfo);
5
15
  const gX1 = absToChr(x1, chromInfo);
6
-
16
+ if (!gX0 || !gX1) {
17
+ throw new Error("Couldn't convert data to genomic coordinates");
18
+ }
7
19
  return [gX0[0], Math.round(gX0[1]), gX1[0], Math.round(gX1[1])];
8
20
  };
9
21
 
@@ -1,3 +1,5 @@
1
+ // @ts-check
2
+
1
3
  /**
2
4
  * Debounce a function call
3
5
  *
@@ -9,21 +11,24 @@
9
11
  * Once webpack 2 with tree-shaking is supported I'd advocate to use lodash's
10
12
  * debounce method.
11
13
  *
12
- * @method debounce
13
- * @author Fritz Lekschas
14
- * @date 2017-01-14
15
- * @param {Function} func Function to be debounced
16
- * @param {Number} wait Number of milliseconds to debounce the
17
- * function call.
18
- * @param {Boolean} immediate If `true` function is not debounced.
19
- * @return {Functiomn} Debounced function.
14
+ * @method debounce
15
+ * @author Fritz Lekschas
16
+ * @date 2017-01-14
17
+ *
18
+ * @template {any[]} Args
19
+ * @param {(...args: Args) => void} func - Function to be debounced
20
+ * @param {number} wait - Number of milliseconds to debounce the function call.
21
+ * @param {boolean} immediate - If `true` function is not debounced.
22
+ * @return {{ (...args: Args): void, cancel(): void }} Debounced function.
20
23
  */
21
24
  export const debounce = (func, wait, immediate) => {
25
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
22
26
  let timeout;
23
27
 
28
+ /** @param {Args} args */
24
29
  const debounced = (...args) => {
25
30
  const later = () => {
26
- timeout = null;
31
+ timeout = undefined;
27
32
  if (!immediate) {
28
33
  func(...args);
29
34
  }
@@ -38,9 +43,9 @@ export const debounce = (func, wait, immediate) => {
38
43
  }
39
44
  };
40
45
 
41
- debounce.cancel = () => {
46
+ debounced.cancel = () => {
42
47
  clearTimeout(timeout);
43
- timeout = null;
48
+ timeout = undefined;
44
49
  };
45
50
 
46
51
  return debounced;
@@ -1,3 +1,10 @@
1
+ // @ts-check
2
+ /**
3
+ * Converts a decimal number to a hex string.
4
+ *
5
+ * @param {number} dec - Decimal number
6
+ * @return {string} Hex string
7
+ */
1
8
  const decToHexStr = (dec) => (dec + 16 ** 6).toString(16).substr(-6);
2
9
 
3
10
  export default decToHexStr;
@@ -1,15 +1,23 @@
1
+ // @ts-check
2
+
1
3
  /**
2
- * Create a dictionary from a list of [key,value] pairs.
3
- * @param tuples: A list of [key,value] pairs
4
- * @return: A dictionary
4
+ * Create a dictionary from a list of [key, value] pairs.
5
+ *
6
+ * TODO(Trevor): Replace with Object.fromEntries?
7
+ *
8
+ * @template {ReadonlyArray<readonly [PropertyKey, unknown]>} T
9
+ * @param {T} tuples: A list of [key, value] pairs
10
+ * @return {{ [Item in T[number] as Item[0]]: Item[1] }} A dictionary
5
11
  */
6
12
  const dictFromTuples = (tuples) => {
13
+ /** @type {Record<PropertyKey, unknown>} */
7
14
  const dict = {};
8
15
 
9
16
  tuples.forEach((x) => {
10
17
  dict[x[0]] = x[1];
11
18
  });
12
19
 
20
+ // @ts-expect-error - TS inference not good enough to infer the correct type
13
21
  return dict;
14
22
  };
15
23
 
@@ -1,8 +1,23 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @template T
5
+ * @typedef {Array<{ [Key in keyof T]: [Key, T[Key]] }[keyof T]>} DictItems
6
+ */
7
+
1
8
  /**
2
9
  * Return an array of (key,value) pairs that are present in this
3
10
  * dictionary
11
+ *
12
+ * TODO(Trevor): Replace with `Object.entries`?
13
+ *
14
+ * @template {object} T
15
+ * @param {T} dictionary
16
+ *
17
+ * @returns {DictItems<T>}
4
18
  */
5
19
  const dictItems = (dictionary) => {
20
+ /** @type {DictItems<T>} */
6
21
  const keyValues = [];
7
22
 
8
23
  for (const key in dictionary) {