@jbrowse/plugin-variants 4.0.3 → 4.1.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 (144) hide show
  1. package/esm/ChordVariantDisplay/models/stateModelFactory.d.ts +9 -0
  2. package/esm/LDDisplay/SharedLDConfigSchema.d.ts +102 -0
  3. package/esm/LDDisplay/SharedLDConfigSchema.js +83 -0
  4. package/esm/LDDisplay/afterAttach.d.ts +2 -0
  5. package/esm/LDDisplay/afterAttach.js +123 -0
  6. package/esm/LDDisplay/components/BaseDisplayComponent.d.ts +15 -0
  7. package/esm/LDDisplay/components/BaseDisplayComponent.js +39 -0
  8. package/esm/LDDisplay/components/LDColorLegend.d.ts +15 -0
  9. package/esm/LDDisplay/components/LDColorLegend.js +75 -0
  10. package/esm/LDDisplay/components/LDDisplayComponent.d.ts +5 -0
  11. package/esm/LDDisplay/components/LDDisplayComponent.js +203 -0
  12. package/esm/LDDisplay/components/LinesConnectingMatrixToGenomicPosition.d.ts +16 -0
  13. package/esm/LDDisplay/components/LinesConnectingMatrixToGenomicPosition.js +109 -0
  14. package/esm/LDDisplay/configSchema1.d.ts +115 -0
  15. package/esm/LDDisplay/configSchema1.js +16 -0
  16. package/esm/LDDisplay/configSchema2.d.ts +115 -0
  17. package/esm/LDDisplay/configSchema2.js +16 -0
  18. package/esm/LDDisplay/index.d.ts +2 -0
  19. package/esm/LDDisplay/index.js +35 -0
  20. package/esm/LDDisplay/renderSvg.d.ts +3 -0
  21. package/esm/LDDisplay/renderSvg.js +36 -0
  22. package/esm/LDDisplay/shared.d.ts +367 -0
  23. package/esm/LDDisplay/shared.js +467 -0
  24. package/esm/LDDisplay/stateModel1.d.ts +365 -0
  25. package/esm/LDDisplay/stateModel1.js +10 -0
  26. package/esm/LDDisplay/stateModel2.d.ts +365 -0
  27. package/esm/LDDisplay/stateModel2.js +10 -0
  28. package/esm/LDRenderer/LDRenderer.d.ts +30 -0
  29. package/esm/LDRenderer/LDRenderer.js +109 -0
  30. package/esm/LDRenderer/components/LDRendering.d.ts +2 -0
  31. package/esm/LDRenderer/components/LDRendering.js +4 -0
  32. package/esm/LDRenderer/configSchema.d.ts +8 -0
  33. package/esm/LDRenderer/configSchema.js +10 -0
  34. package/esm/LDRenderer/index.d.ts +2 -0
  35. package/esm/LDRenderer/index.js +11 -0
  36. package/esm/LDRenderer/makeImageData.d.ts +20 -0
  37. package/esm/LDRenderer/makeImageData.js +158 -0
  38. package/esm/LDRenderer/types.d.ts +8 -0
  39. package/esm/LDRenderer/types.js +1 -0
  40. package/esm/LDTrack/configSchema.d.ts +85 -0
  41. package/esm/LDTrack/configSchema.js +7 -0
  42. package/esm/LDTrack/index.d.ts +2 -0
  43. package/esm/LDTrack/index.js +14 -0
  44. package/esm/LinearVariantDisplay/model.d.ts +139 -42
  45. package/esm/LinearVariantDisplay/model.js +46 -8
  46. package/esm/MultiLinearVariantDisplay/configSchema.d.ts +27 -1
  47. package/esm/MultiLinearVariantDisplay/model.d.ts +2647 -58
  48. package/esm/MultiLinearVariantDisplay/model.js +6 -0
  49. package/esm/MultiLinearVariantDisplay/renderSvg.d.ts +10 -2
  50. package/esm/MultiLinearVariantMatrixDisplay/configSchema.d.ts +25 -0
  51. package/esm/MultiLinearVariantMatrixDisplay/configSchema.js +26 -0
  52. package/esm/MultiLinearVariantMatrixDisplay/model.d.ts +2648 -59
  53. package/esm/MultiLinearVariantMatrixDisplay/model.js +6 -0
  54. package/esm/MultiLinearVariantMatrixRenderer/MultiLinearVariantMatrixRenderer.d.ts +2 -2
  55. package/esm/MultiLinearVariantMatrixRenderer/MultiLinearVariantMatrixRenderer.js +11 -9
  56. package/esm/MultiLinearVariantMatrixRenderer/components/MultiLinearVariantMatrixRendering.d.ts +8 -0
  57. package/esm/MultiLinearVariantMatrixRenderer/components/MultiLinearVariantMatrixRendering.js +14 -2
  58. package/esm/MultiLinearVariantMatrixRenderer/makeImageData.js +14 -8
  59. package/esm/MultiLinearVariantRenderer/MultiVariantRenderer.d.ts +2 -2
  60. package/esm/MultiLinearVariantRenderer/MultiVariantRenderer.js +4 -3
  61. package/esm/MultiLinearVariantRenderer/components/MultiLinearVariantRendering.d.ts +4 -0
  62. package/esm/MultiLinearVariantRenderer/components/MultiLinearVariantRendering.js +23 -2
  63. package/esm/MultiLinearVariantRenderer/makeImageData.js +8 -3
  64. package/esm/PlinkLDAdapter/PlinkLDAdapter.d.ts +25 -0
  65. package/esm/PlinkLDAdapter/PlinkLDAdapter.js +147 -0
  66. package/esm/PlinkLDAdapter/PlinkLDTabixAdapter.d.ts +24 -0
  67. package/esm/PlinkLDAdapter/PlinkLDTabixAdapter.js +156 -0
  68. package/esm/PlinkLDAdapter/configSchema.d.ts +10 -0
  69. package/esm/PlinkLDAdapter/configSchema.js +25 -0
  70. package/esm/PlinkLDAdapter/configSchemaTabix.d.ts +24 -0
  71. package/esm/PlinkLDAdapter/configSchemaTabix.js +46 -0
  72. package/esm/PlinkLDAdapter/index.d.ts +2 -0
  73. package/esm/PlinkLDAdapter/index.js +25 -0
  74. package/esm/PlinkLDAdapter/types.d.ts +29 -0
  75. package/esm/PlinkLDAdapter/types.js +1 -0
  76. package/esm/VariantFeatureWidget/VariantSampleGrid/VariantSampleGrid.js +1 -1
  77. package/esm/VariantRPC/MultiVariantGetFeatureDetails.d.ts +14 -0
  78. package/esm/VariantRPC/MultiVariantGetFeatureDetails.js +15 -0
  79. package/esm/VariantRPC/executeClusterGenotypeMatrix.d.ts +3 -1
  80. package/esm/VariantRPC/executeClusterGenotypeMatrix.js +8 -4
  81. package/esm/VariantRPC/getLDMatrix.d.ts +48 -0
  82. package/esm/VariantRPC/getLDMatrix.js +388 -0
  83. package/esm/VariantRPC/getLDMatrixFromPlink.d.ts +16 -0
  84. package/esm/VariantRPC/getLDMatrixFromPlink.js +105 -0
  85. package/esm/VariantRPC/getPhasedGenotypeMatrix.d.ts +20 -0
  86. package/esm/VariantRPC/getPhasedGenotypeMatrix.js +50 -0
  87. package/esm/VariantRPC/types.d.ts +3 -0
  88. package/esm/VcfAdapter/VcfAdapter.d.ts +1 -1
  89. package/esm/VcfAdapter/VcfAdapter.js +1 -2
  90. package/esm/VcfExtensionPoints/index.js +29 -3
  91. package/esm/VcfFeature/index.d.ts +2 -1
  92. package/esm/VcfFeature/index.js +4 -2
  93. package/esm/index.d.ts +1 -0
  94. package/esm/index.js +23 -0
  95. package/esm/shared/MultiVariantBaseModel.d.ts +2638 -53
  96. package/esm/shared/MultiVariantBaseModel.js +100 -47
  97. package/esm/shared/SharedVariantConfigSchema.d.ts +27 -1
  98. package/esm/shared/SharedVariantConfigSchema.js +28 -1
  99. package/esm/shared/VariantFeatureCache.d.ts +27 -0
  100. package/esm/shared/VariantFeatureCache.js +48 -0
  101. package/esm/shared/VariantRendererType.d.ts +23 -0
  102. package/esm/shared/VariantRendererType.js +15 -0
  103. package/esm/shared/applyColorPalette.d.ts +9 -0
  104. package/esm/shared/applyColorPalette.js +23 -0
  105. package/esm/shared/colorByAutorun.d.ts +10 -0
  106. package/esm/shared/colorByAutorun.js +39 -0
  107. package/esm/shared/components/AddFiltersDialog.d.ts +3 -3
  108. package/esm/shared/components/AddFiltersDialog.js +29 -22
  109. package/esm/shared/components/LDFilterDialog.d.ts +13 -0
  110. package/esm/shared/components/LDFilterDialog.js +102 -0
  111. package/esm/shared/components/MAFFilterDialog.js +23 -16
  112. package/esm/shared/components/MultiVariantClusterDialog/ClusterDialogAuto.js +19 -6
  113. package/esm/shared/components/MultiVariantClusterDialog/types.d.ts +4 -1
  114. package/esm/shared/components/MultiVariantColorLegend.js +4 -4
  115. package/esm/shared/components/MultiVariantLegendBar.js +1 -1
  116. package/esm/shared/components/MultiVariantTooltip.d.ts +1 -0
  117. package/esm/shared/components/MultiVariantTooltip.js +2 -2
  118. package/esm/shared/components/RecombinationTrack.d.ts +21 -0
  119. package/esm/shared/components/RecombinationTrack.js +54 -0
  120. package/esm/shared/components/RecombinationYScaleBar.d.ts +7 -0
  121. package/esm/shared/components/RecombinationYScaleBar.js +34 -0
  122. package/esm/shared/components/RectBg.js +1 -1
  123. package/esm/shared/components/SetColorDialogRowPalettizer.d.ts +3 -8
  124. package/esm/shared/components/SetColorDialogRowPalettizer.js +2 -14
  125. package/esm/shared/components/SourcesDataGrid.js +4 -4
  126. package/esm/shared/components/TreeSidebar.js +11 -1
  127. package/esm/shared/drawAlleleCount.js +9 -0
  128. package/esm/shared/drawPhased.d.ts +1 -1
  129. package/esm/shared/drawPhased.js +31 -2
  130. package/esm/shared/getMultiVariantFeaturesAutorun.d.ts +1 -0
  131. package/esm/shared/getMultiVariantFeaturesAutorun.js +3 -0
  132. package/esm/shared/getMultiVariantSourcesAutorun.d.ts +1 -0
  133. package/esm/shared/getMultiVariantSourcesAutorun.js +3 -0
  134. package/esm/shared/getSources.d.ts +5 -9
  135. package/esm/shared/getSources.js +30 -25
  136. package/esm/shared/mafFilterUtils.d.ts +5 -0
  137. package/esm/shared/mafFilterUtils.js +17 -0
  138. package/esm/shared/minorAlleleFrequencyUtils.d.ts +2 -0
  139. package/esm/shared/minorAlleleFrequencyUtils.js +259 -17
  140. package/esm/shared/setupMultiVariantAutoruns.js +2 -0
  141. package/esm/shared/treeDrawingAutorun.d.ts +1 -0
  142. package/esm/shared/treeDrawingAutorun.js +7 -1
  143. package/esm/shared/types.d.ts +1 -2
  144. package/package.json +12 -11
@@ -14,6 +14,12 @@ export default function stateModelFactory(configSchema) {
14
14
  get prefersOffset() {
15
15
  return true;
16
16
  },
17
+ get featureWidgetType() {
18
+ return {
19
+ type: 'VariantFeatureWidget',
20
+ id: 'variantFeature',
21
+ };
22
+ },
17
23
  }))
18
24
  .views(self => ({
19
25
  renderProps() {
@@ -1,6 +1,6 @@
1
- import FeatureRendererType from '@jbrowse/core/pluggableElementTypes/renderers/FeatureRendererType';
1
+ import VariantRendererType from '../shared/VariantRendererType.ts';
2
2
  import type { RenderArgsDeserialized } from './types.ts';
3
- export default class LinearVariantMatrixRenderer extends FeatureRendererType {
3
+ export default class LinearVariantMatrixRenderer extends VariantRendererType {
4
4
  supportsSVG: boolean;
5
5
  render(renderProps: RenderArgsDeserialized): Promise<import("librpc-web-mod").RpcResult>;
6
6
  }
@@ -1,14 +1,15 @@
1
- import FeatureRendererType from '@jbrowse/core/pluggableElementTypes/renderers/FeatureRendererType';
2
1
  import { renderToAbstractCanvas } from '@jbrowse/core/util';
3
2
  import { rpcResult } from '@jbrowse/core/util/librpc';
4
3
  import { collectTransferables } from '@jbrowse/core/util/offscreenCanvasPonyfill';
5
- export default class LinearVariantMatrixRenderer extends FeatureRendererType {
4
+ import VariantRendererType from "../shared/VariantRendererType.js";
5
+ export default class LinearVariantMatrixRenderer extends VariantRendererType {
6
6
  supportsSVG = true;
7
7
  async render(renderProps) {
8
8
  const features = await this.getFeatures(renderProps);
9
- const { height, regions, bpPerPx, scrollTop, rowHeight } = renderProps;
9
+ const { height, regions, bpPerPx, scrollTop, rowHeight, sessionId, trackInstanceId, } = renderProps;
10
10
  const region = regions[0];
11
11
  const width = (region.end - region.start) / bpPerPx;
12
+ this.cacheFeatures({ sessionId, trackInstanceId }, region.refName, features);
12
13
  const { makeImageData } = await import("./makeImageData.js");
13
14
  const { mafs, ...rest } = await renderToAbstractCanvas(width, height, renderProps, ctx => makeImageData({
14
15
  ctx,
@@ -16,14 +17,15 @@ export default class LinearVariantMatrixRenderer extends FeatureRendererType {
16
17
  canvasHeight: height,
17
18
  renderArgs: { ...renderProps, features },
18
19
  }));
20
+ const simplifiedFeatures = mafs.map(({ feature }) => ({
21
+ uniqueId: feature.id(),
22
+ start: feature.get('start'),
23
+ end: feature.get('end'),
24
+ refName: feature.get('refName'),
25
+ }));
19
26
  const serialized = {
20
27
  ...rest,
21
- simplifiedFeatures: mafs.map(({ feature }) => ({
22
- uniqueId: feature.id(),
23
- start: feature.get('start'),
24
- end: feature.get('end'),
25
- refName: feature.get('refName'),
26
- })),
28
+ simplifiedFeatures,
27
29
  height,
28
30
  width,
29
31
  origScrollTop: scrollTop,
@@ -6,6 +6,12 @@ interface FeatureData {
6
6
  description: string;
7
7
  length: number;
8
8
  }
9
+ interface SimplifiedFeature {
10
+ uniqueId: string;
11
+ }
12
+ interface RenderingProps {
13
+ onFeatureClick?: (event: React.MouseEvent, featureId: string) => void;
14
+ }
9
15
  declare const MultiLinearVariantMatrixRendering: (props: {
10
16
  width: number;
11
17
  height: number;
@@ -15,5 +21,7 @@ declare const MultiLinearVariantMatrixRendering: (props: {
15
21
  rowHeight: number;
16
22
  origScrollTop: number;
17
23
  totalHeight: number;
24
+ simplifiedFeatures?: SimplifiedFeature[];
25
+ renderingProps?: RenderingProps;
18
26
  }) => import("react/jsx-runtime").JSX.Element;
19
27
  export default MultiLinearVariantMatrixRendering;
@@ -5,7 +5,7 @@ import { getBpDisplayStr } from '@jbrowse/core/util';
5
5
  import { observer } from 'mobx-react';
6
6
  import { makeSimpleAltString } from "../../VcfFeature/util.js";
7
7
  const MultiLinearVariantMatrixRendering = observer(function MultiLinearVariantMatrixRendering(props) {
8
- const { arr, width, displayModel, featureData, totalHeight, origScrollTop, rowHeight, } = props;
8
+ const { arr, width, displayModel, featureData, totalHeight, origScrollTop, rowHeight, simplifiedFeatures, renderingProps, } = props;
9
9
  const ref = useRef(null);
10
10
  const lastHoveredRef = useRef(undefined);
11
11
  const getFeatureUnderMouse = useCallback((eventClientX, eventClientY) => {
@@ -52,7 +52,19 @@ const MultiLinearVariantMatrixRendering = observer(function MultiLinearVariantMa
52
52
  displayModel.setHoveredGenotype(undefined);
53
53
  }
54
54
  }, [displayModel]);
55
- return (_jsx("div", { ref: ref, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, onMouseOut: handleMouseLeave, style: {
55
+ const handleClick = useCallback((e) => {
56
+ if (!ref.current || !simplifiedFeatures) {
57
+ return;
58
+ }
59
+ const r = ref.current.getBoundingClientRect();
60
+ const offsetX = e.clientX - r.left;
61
+ const featureIdx = Math.floor((offsetX / width) * arr.length);
62
+ const feature = simplifiedFeatures[featureIdx];
63
+ if (feature) {
64
+ renderingProps?.onFeatureClick?.(e, feature.uniqueId);
65
+ }
66
+ }, [arr.length, width, simplifiedFeatures, renderingProps]);
67
+ return (_jsx("div", { ref: ref, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, onMouseOut: handleMouseLeave, onClick: handleClick, style: {
56
68
  overflow: 'visible',
57
69
  position: 'relative',
58
70
  height: totalHeight,
@@ -17,8 +17,10 @@ function drawPhasedMode(drawCtx, mafs) {
17
17
  const samp = feature.get('samples');
18
18
  for (let j = startRow; j < endRow; j++) {
19
19
  const y = j * h - scrollTop;
20
- const { name, HP } = sources[j];
21
- const s = samp[name];
20
+ const source = sources[j];
21
+ const { name, HP, baseName } = source;
22
+ const sampleName = baseName ?? name;
23
+ const s = samp[sampleName];
22
24
  if (s) {
23
25
  const genotype = s.GT?.[0];
24
26
  if (genotype) {
@@ -47,8 +49,10 @@ function drawPhasedMode(drawCtx, mafs) {
47
49
  }
48
50
  for (let j = startRow; j < endRow; j++) {
49
51
  const y = j * h - scrollTop;
50
- const { name, HP } = sources[j];
51
- const genotype = samp[name];
52
+ const source = sources[j];
53
+ const { name, HP, baseName } = source;
54
+ const sampleName = baseName ?? name;
55
+ const genotype = samp[sampleName];
52
56
  if (genotype) {
53
57
  arr2.push(genotype);
54
58
  const isPhased = genotype.includes('|');
@@ -82,8 +86,9 @@ function drawAlleleCountMode(drawCtx, mafs) {
82
86
  const samp = feature.get('samples');
83
87
  for (let j = startRow; j < endRow; j++) {
84
88
  const y = j * h - scrollTop;
85
- const { name } = sources[j];
86
- const s = samp[name];
89
+ const source = sources[j];
90
+ const sampleName = source.baseName ?? source.name;
91
+ const s = samp[sampleName];
87
92
  if (s) {
88
93
  const genotype = s.GT?.[0];
89
94
  if (genotype) {
@@ -105,8 +110,9 @@ function drawAlleleCountMode(drawCtx, mafs) {
105
110
  }
106
111
  for (let j = startRow; j < endRow; j++) {
107
112
  const y = j * h - scrollTop;
108
- const { name } = sources[j];
109
- const genotype = samp[name];
113
+ const source = sources[j];
114
+ const sampleName = source.baseName ?? source.name;
115
+ const genotype = samp[sampleName];
110
116
  if (genotype) {
111
117
  arr2.push(genotype);
112
118
  const c = getAlleleColor(genotype, mostFrequentAlt, colorCache, splitCache, true);
@@ -1,6 +1,6 @@
1
- import FeatureRendererType from '@jbrowse/core/pluggableElementTypes/renderers/FeatureRendererType';
1
+ import VariantRendererType from '../shared/VariantRendererType.ts';
2
2
  import type { MultiRenderArgsDeserialized } from './types.ts';
3
- export default class MultiVariantRenderer extends FeatureRendererType {
3
+ export default class MultiVariantRenderer extends VariantRendererType {
4
4
  supportsSVG: boolean;
5
5
  render(renderProps: MultiRenderArgsDeserialized): Promise<import("librpc-web-mod").RpcResult>;
6
6
  }
@@ -1,14 +1,15 @@
1
- import FeatureRendererType from '@jbrowse/core/pluggableElementTypes/renderers/FeatureRendererType';
2
1
  import { renderToAbstractCanvas } from '@jbrowse/core/util';
3
2
  import { rpcResult } from '@jbrowse/core/util/librpc';
4
3
  import { collectTransferables } from '@jbrowse/core/util/offscreenCanvasPonyfill';
5
- export default class MultiVariantRenderer extends FeatureRendererType {
4
+ import VariantRendererType from "../shared/VariantRendererType.js";
5
+ export default class MultiVariantRenderer extends VariantRendererType {
6
6
  supportsSVG = true;
7
7
  async render(renderProps) {
8
8
  const features = await this.getFeatures(renderProps);
9
- const { height, referenceDrawingMode, regions, bpPerPx, scrollTop } = renderProps;
9
+ const { height, referenceDrawingMode, regions, bpPerPx, scrollTop, sessionId, trackInstanceId, } = renderProps;
10
10
  const region = regions[0];
11
11
  const width = (region.end - region.start) / bpPerPx;
12
+ this.cacheFeatures({ sessionId, trackInstanceId }, region.refName, features);
12
13
  const { makeImageData } = await import("./makeImageData.js");
13
14
  const ret = await renderToAbstractCanvas(width, height, renderProps, ctx => {
14
15
  if (referenceDrawingMode === 'skip') {
@@ -17,6 +17,9 @@ interface MinimizedVariantRecord {
17
17
  interface MultiVariantDisplayModel {
18
18
  setHoveredGenotype?: (genotype?: Record<string, unknown>) => void;
19
19
  }
20
+ interface RenderingProps {
21
+ onFeatureClick?: (event: React.MouseEvent, featureId: string) => void;
22
+ }
20
23
  declare const MultiVariantRendering: (props: {
21
24
  regions: Region[];
22
25
  features: Map<string, Feature>;
@@ -30,5 +33,6 @@ declare const MultiVariantRendering: (props: {
30
33
  flatbush: any;
31
34
  items: Item[];
32
35
  displayModel: MultiVariantDisplayModel;
36
+ renderingProps?: RenderingProps;
33
37
  }) => import("react/jsx-runtime").JSX.Element;
34
38
  export default MultiVariantRendering;
@@ -7,7 +7,7 @@ import { observer } from 'mobx-react';
7
7
  import { minElt } from "./util.js";
8
8
  import { makeSimpleAltString } from "../../VcfFeature/util.js";
9
9
  const MultiVariantRendering = observer(function MultiVariantRendering(props) {
10
- const { flatbush, items, displayModel, featureGenotypeMap, totalHeight, origScrollTop, } = props;
10
+ const { flatbush, items, displayModel, featureGenotypeMap, totalHeight, origScrollTop, renderingProps, } = props;
11
11
  const ref = useRef(null);
12
12
  const lastHoveredRef = useRef(undefined);
13
13
  const flatbush2 = useMemo(() => Flatbush.from(flatbush), [flatbush]);
@@ -53,7 +53,28 @@ const MultiVariantRendering = observer(function MultiVariantRendering(props) {
53
53
  displayModel.setHoveredGenotype?.(undefined);
54
54
  }
55
55
  }, [displayModel]);
56
- return (_jsx("div", { ref: ref, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, onMouseOut: handleMouseLeave, style: {
56
+ const getFeatureIdUnderMouse = useCallback((eventClientX, eventClientY) => {
57
+ if (!ref.current) {
58
+ return undefined;
59
+ }
60
+ const rect = ref.current.getBoundingClientRect();
61
+ const offsetX = eventClientX - rect.left;
62
+ const offsetY = eventClientY - rect.top;
63
+ const canvasOffsetY = offsetY - origScrollTop;
64
+ const x = flatbush2.search(offsetX, canvasOffsetY, offsetX + 1, canvasOffsetY + 1);
65
+ if (x.length) {
66
+ const res = minElt(x, idx => items[idx]?.bpLen ?? 0);
67
+ return items[res]?.featureId;
68
+ }
69
+ return undefined;
70
+ }, [flatbush2, items, origScrollTop]);
71
+ const handleClick = useCallback((e) => {
72
+ const featureId = getFeatureIdUnderMouse(e.clientX, e.clientY);
73
+ if (featureId) {
74
+ renderingProps?.onFeatureClick?.(e, featureId);
75
+ }
76
+ }, [getFeatureIdUnderMouse, renderingProps]);
77
+ return (_jsx("div", { ref: ref, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, onMouseOut: handleMouseLeave, onClick: handleClick, style: {
57
78
  overflow: 'visible',
58
79
  position: 'relative',
59
80
  height: totalHeight,
@@ -20,15 +20,20 @@ function drawPhasedMode(drawCtx, itemData, mafs) {
20
20
  samp = feature.get('genotypes');
21
21
  genotypesCache.set(featureId, samp);
22
22
  }
23
+ const featureType = feature.get('type');
24
+ const featureStrand = feature.get('strand');
25
+ const alpha = bpLen > 5 ? 0.75 : 1;
23
26
  for (let j = startRow; j < endRow; j++) {
24
27
  const y = j * h - scrollTop;
25
- const { name, HP } = sources[j];
26
- const genotype = samp[name];
28
+ const source = sources[j];
29
+ const { name, HP, baseName } = source;
30
+ const sampleName = baseName ?? name;
31
+ const genotype = samp[sampleName];
27
32
  if (genotype) {
28
33
  const isPhased = genotype.includes('|');
29
34
  if (isPhased) {
30
35
  const alleles = splitCache[genotype] ?? (splitCache[genotype] = genotype.split('|'));
31
- if (drawPhased(alleles, ctx, x, y, w, drawH, HP, undefined, drawRef)) {
36
+ if (drawPhased(alleles, ctx, x, y, w, drawH, HP, undefined, drawRef, alpha, featureType, featureStrand)) {
32
37
  items.push({ name, genotype, featureId, bpLen });
33
38
  coords.push(x, y, x + w, y + drawH);
34
39
  }
@@ -0,0 +1,25 @@
1
+ import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
+ import type { PlinkLDHeader, PlinkLDRecord } from './types.ts';
3
+ import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
4
+ import type { NoAssemblyRegion } from '@jbrowse/core/util/types';
5
+ export default class PlinkLDAdapter extends BaseAdapter {
6
+ private configured?;
7
+ private configurePre;
8
+ private parseHeader;
9
+ protected configurePre2(opts?: BaseOptions): Promise<{
10
+ records: PlinkLDRecord[];
11
+ header: PlinkLDHeader;
12
+ refNames: string[];
13
+ }>;
14
+ configure(opts?: BaseOptions): Promise<{
15
+ records: PlinkLDRecord[];
16
+ header: PlinkLDHeader;
17
+ refNames: string[];
18
+ }>;
19
+ getRefNames(opts?: BaseOptions): Promise<string[]>;
20
+ getHeader(opts?: BaseOptions): Promise<PlinkLDHeader>;
21
+ getLDRecords(query: NoAssemblyRegion, opts?: BaseOptions): Promise<PlinkLDRecord[]>;
22
+ getLDRecordsInRegion(query: NoAssemblyRegion, opts?: BaseOptions): Promise<PlinkLDRecord[]>;
23
+ private parseLine;
24
+ freeResources(): void;
25
+ }
@@ -0,0 +1,147 @@
1
+ import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
2
+ import { fetchAndMaybeUnzipText, updateStatus } from '@jbrowse/core/util';
3
+ export default class PlinkLDAdapter extends BaseAdapter {
4
+ configured;
5
+ async configurePre(opts) {
6
+ const ldLocation = this.getConf('ldLocation');
7
+ const text = await fetchAndMaybeUnzipText(ldLocation, opts);
8
+ const lines = text.split('\n').filter(line => line.trim());
9
+ if (lines.length === 0) {
10
+ throw new Error('Empty LD file');
11
+ }
12
+ const header = this.parseHeader(lines[0]);
13
+ const records = [];
14
+ const refNamesSet = new Set();
15
+ for (let i = 1; i < lines.length; i++) {
16
+ const record = this.parseLine(lines[i], header);
17
+ if (record) {
18
+ records.push(record);
19
+ refNamesSet.add(record.chrA);
20
+ refNamesSet.add(record.chrB);
21
+ }
22
+ }
23
+ const refNames = [...refNamesSet].sort();
24
+ return { records, header, refNames };
25
+ }
26
+ parseHeader(headerLine) {
27
+ const columns = headerLine.trim().split(/\s+/);
28
+ const findIdx = (names) => {
29
+ for (const name of names) {
30
+ const idx = columns.indexOf(name);
31
+ if (idx !== -1) {
32
+ return idx;
33
+ }
34
+ }
35
+ return -1;
36
+ };
37
+ const chrAIdx = findIdx(['CHR_A', 'CHR1']);
38
+ const bpAIdx = findIdx(['BP_A', 'BP1', 'POS_A', 'POS1']);
39
+ const snpAIdx = findIdx(['SNP_A', 'SNP1', 'ID_A', 'ID1']);
40
+ const chrBIdx = findIdx(['CHR_B', 'CHR2']);
41
+ const bpBIdx = findIdx(['BP_B', 'BP2', 'POS_B', 'POS2']);
42
+ const snpBIdx = findIdx(['SNP_B', 'SNP2', 'ID_B', 'ID2']);
43
+ const r2Idx = findIdx(['R2', 'R^2', 'RSQ']);
44
+ const dprimeIdx = findIdx(['DP', 'DPRIME', "D'"]);
45
+ const mafAIdx = findIdx(['MAF_A', 'MAF1']);
46
+ const mafBIdx = findIdx(['MAF_B', 'MAF2']);
47
+ if (chrAIdx === -1 || bpAIdx === -1 || chrBIdx === -1 || bpBIdx === -1) {
48
+ throw new Error(`Invalid PLINK LD header. Expected columns CHR_A, BP_A, CHR_B, BP_B. Got: ${columns.join(', ')}`);
49
+ }
50
+ if (r2Idx === -1 && dprimeIdx === -1) {
51
+ throw new Error(`Invalid PLINK LD header. Expected at least R2 or DP column. Got: ${columns.join(', ')}`);
52
+ }
53
+ return {
54
+ columns,
55
+ hasR2: r2Idx !== -1,
56
+ hasDprime: dprimeIdx !== -1,
57
+ hasMafA: mafAIdx !== -1,
58
+ hasMafB: mafBIdx !== -1,
59
+ chrAIdx,
60
+ bpAIdx,
61
+ snpAIdx,
62
+ chrBIdx,
63
+ bpBIdx,
64
+ snpBIdx,
65
+ r2Idx,
66
+ dprimeIdx,
67
+ mafAIdx,
68
+ mafBIdx,
69
+ };
70
+ }
71
+ async configurePre2(opts) {
72
+ if (!this.configured) {
73
+ this.configured = this.configurePre(opts).catch((e) => {
74
+ this.configured = undefined;
75
+ throw e;
76
+ });
77
+ }
78
+ return this.configured;
79
+ }
80
+ async configure(opts) {
81
+ const { statusCallback = () => { } } = opts || {};
82
+ return updateStatus('Loading LD data', statusCallback, () => this.configurePre2(opts));
83
+ }
84
+ async getRefNames(opts = {}) {
85
+ const { refNames } = await this.configure(opts);
86
+ return refNames;
87
+ }
88
+ async getHeader(opts) {
89
+ const { header } = await this.configure(opts);
90
+ return header;
91
+ }
92
+ async getLDRecords(query, opts = {}) {
93
+ const { refName, start, end } = query;
94
+ const { records } = await this.configure(opts);
95
+ const filtered = records.filter(r => r.chrA === refName && r.bpA >= start && r.bpA <= end);
96
+ return filtered;
97
+ }
98
+ async getLDRecordsInRegion(query, opts = {}) {
99
+ const { refName, start, end } = query;
100
+ const records = await this.getLDRecords(query, opts);
101
+ const filtered = records.filter(r => r.chrB === refName &&
102
+ r.bpB >= start &&
103
+ r.bpB <= end &&
104
+ r.chrA === refName &&
105
+ r.bpA >= start &&
106
+ r.bpA <= end);
107
+ return filtered;
108
+ }
109
+ parseLine(line, header) {
110
+ const fields = line.trim().split(/\s+/);
111
+ const chrA = fields[header.chrAIdx];
112
+ const bpA = Number.parseInt(fields[header.bpAIdx] ?? '', 10);
113
+ const snpA = header.snpAIdx >= 0 ? fields[header.snpAIdx] : `${chrA}:${bpA}`;
114
+ const chrB = fields[header.chrBIdx];
115
+ const bpB = Number.parseInt(fields[header.bpBIdx] ?? '', 10);
116
+ const snpB = header.snpBIdx >= 0 ? fields[header.snpBIdx] : `${chrB}:${bpB}`;
117
+ if (!chrA || !chrB || Number.isNaN(bpA) || Number.isNaN(bpB)) {
118
+ return null;
119
+ }
120
+ const r2 = header.r2Idx >= 0
121
+ ? Number.parseFloat(fields[header.r2Idx] ?? '')
122
+ : undefined;
123
+ const dprime = header.dprimeIdx >= 0
124
+ ? Number.parseFloat(fields[header.dprimeIdx] ?? '')
125
+ : undefined;
126
+ const mafA = header.mafAIdx >= 0
127
+ ? Number.parseFloat(fields[header.mafAIdx] ?? '')
128
+ : undefined;
129
+ const mafB = header.mafBIdx >= 0
130
+ ? Number.parseFloat(fields[header.mafBIdx] ?? '')
131
+ : undefined;
132
+ return {
133
+ chrA,
134
+ bpA,
135
+ snpA: snpA ?? `${chrA}:${bpA}`,
136
+ chrB,
137
+ bpB,
138
+ snpB: snpB ?? `${chrB}:${bpB}`,
139
+ r2: r2 ?? 0,
140
+ dprime,
141
+ mafA,
142
+ mafB,
143
+ };
144
+ }
145
+ freeResources() {
146
+ }
147
+ }
@@ -0,0 +1,24 @@
1
+ import { TabixIndexedFile } from '@gmod/tabix';
2
+ import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
3
+ import type { PlinkLDHeader, PlinkLDRecord } from './types.ts';
4
+ import type { BaseOptions } from '@jbrowse/core/data_adapters/BaseAdapter';
5
+ import type { NoAssemblyRegion } from '@jbrowse/core/util/types';
6
+ export default class PlinkLDTabixAdapter extends BaseAdapter {
7
+ private configured?;
8
+ private configurePre;
9
+ private parseHeader;
10
+ protected configurePre2(): Promise<{
11
+ ld: TabixIndexedFile;
12
+ header: PlinkLDHeader;
13
+ }>;
14
+ configure(opts?: BaseOptions): Promise<{
15
+ ld: TabixIndexedFile;
16
+ header: PlinkLDHeader;
17
+ }>;
18
+ getRefNames(opts?: BaseOptions): Promise<string[]>;
19
+ getHeader(opts?: BaseOptions): Promise<PlinkLDHeader>;
20
+ getLDRecords(query: NoAssemblyRegion, opts?: BaseOptions): Promise<PlinkLDRecord[]>;
21
+ getLDRecordsInRegion(query: NoAssemblyRegion, opts?: BaseOptions): Promise<PlinkLDRecord[]>;
22
+ private parseLine;
23
+ freeResources(): void;
24
+ }
@@ -0,0 +1,156 @@
1
+ import { TabixIndexedFile } from '@gmod/tabix';
2
+ import { BaseAdapter } from '@jbrowse/core/data_adapters/BaseAdapter';
3
+ import { updateStatus } from '@jbrowse/core/util';
4
+ import { openLocation } from '@jbrowse/core/util/io';
5
+ export default class PlinkLDTabixAdapter extends BaseAdapter {
6
+ configured;
7
+ async configurePre(_opts) {
8
+ const ldLocation = this.getConf('ldLocation');
9
+ const location = this.getConf(['index', 'location']);
10
+ const indexType = this.getConf(['index', 'indexType']);
11
+ const filehandle = openLocation(ldLocation, this.pluginManager);
12
+ const isCSI = indexType === 'CSI';
13
+ const ld = new TabixIndexedFile({
14
+ filehandle,
15
+ csiFilehandle: isCSI
16
+ ? openLocation(location, this.pluginManager)
17
+ : undefined,
18
+ tbiFilehandle: !isCSI
19
+ ? openLocation(location, this.pluginManager)
20
+ : undefined,
21
+ chunkCacheSize: 50 * 2 ** 20,
22
+ });
23
+ const headerLine = await ld.getHeader();
24
+ const header = this.parseHeader(headerLine);
25
+ return { ld, header };
26
+ }
27
+ parseHeader(headerLine) {
28
+ const columns = headerLine.trim().split(/\s+/);
29
+ const findIdx = (names) => {
30
+ for (const name of names) {
31
+ const idx = columns.indexOf(name);
32
+ if (idx !== -1) {
33
+ return idx;
34
+ }
35
+ }
36
+ return -1;
37
+ };
38
+ const chrAIdx = findIdx(['CHR_A', 'CHR1']);
39
+ const bpAIdx = findIdx(['BP_A', 'BP1', 'POS_A', 'POS1']);
40
+ const snpAIdx = findIdx(['SNP_A', 'SNP1', 'ID_A', 'ID1']);
41
+ const chrBIdx = findIdx(['CHR_B', 'CHR2']);
42
+ const bpBIdx = findIdx(['BP_B', 'BP2', 'POS_B', 'POS2']);
43
+ const snpBIdx = findIdx(['SNP_B', 'SNP2', 'ID_B', 'ID2']);
44
+ const r2Idx = findIdx(['R2', 'R^2', 'RSQ']);
45
+ const dprimeIdx = findIdx(['DP', 'DPRIME', "D'"]);
46
+ const mafAIdx = findIdx(['MAF_A', 'MAF1']);
47
+ const mafBIdx = findIdx(['MAF_B', 'MAF2']);
48
+ if (chrAIdx === -1 || bpAIdx === -1 || chrBIdx === -1 || bpBIdx === -1) {
49
+ throw new Error(`Invalid PLINK LD header. Expected columns CHR_A, BP_A, CHR_B, BP_B. Got: ${columns.join(', ')}`);
50
+ }
51
+ if (r2Idx === -1 && dprimeIdx === -1) {
52
+ throw new Error(`Invalid PLINK LD header. Expected at least R2 or DP column. Got: ${columns.join(', ')}`);
53
+ }
54
+ return {
55
+ columns,
56
+ hasR2: r2Idx !== -1,
57
+ hasDprime: dprimeIdx !== -1,
58
+ hasMafA: mafAIdx !== -1,
59
+ hasMafB: mafBIdx !== -1,
60
+ chrAIdx,
61
+ bpAIdx,
62
+ snpAIdx,
63
+ chrBIdx,
64
+ bpBIdx,
65
+ snpBIdx,
66
+ r2Idx,
67
+ dprimeIdx,
68
+ mafAIdx,
69
+ mafBIdx,
70
+ };
71
+ }
72
+ async configurePre2() {
73
+ if (!this.configured) {
74
+ this.configured = this.configurePre().catch((e) => {
75
+ this.configured = undefined;
76
+ throw e;
77
+ });
78
+ }
79
+ return this.configured;
80
+ }
81
+ async configure(opts) {
82
+ const { statusCallback = () => { } } = opts || {};
83
+ return updateStatus('Downloading index', statusCallback, () => this.configurePre2());
84
+ }
85
+ async getRefNames(opts = {}) {
86
+ const { ld } = await this.configure(opts);
87
+ return ld.getReferenceSequenceNames(opts);
88
+ }
89
+ async getHeader(opts) {
90
+ const { header } = await this.configure(opts);
91
+ return header;
92
+ }
93
+ async getLDRecords(query, opts = {}) {
94
+ const { refName, start, end } = query;
95
+ const { ld, header } = await this.configure(opts);
96
+ const records = [];
97
+ await ld.getLines(refName, start, end, {
98
+ lineCallback: (line) => {
99
+ const record = this.parseLine(line, header);
100
+ if (record) {
101
+ records.push(record);
102
+ }
103
+ },
104
+ ...opts,
105
+ });
106
+ return records;
107
+ }
108
+ async getLDRecordsInRegion(query, opts = {}) {
109
+ const { refName, start, end } = query;
110
+ const records = await this.getLDRecords(query, opts);
111
+ return records.filter(r => r.chrB === refName &&
112
+ r.bpB >= start &&
113
+ r.bpB <= end &&
114
+ r.chrA === refName &&
115
+ r.bpA >= start &&
116
+ r.bpA <= end);
117
+ }
118
+ parseLine(line, header) {
119
+ const fields = line.trim().split(/\s+/);
120
+ const chrA = fields[header.chrAIdx];
121
+ const bpA = Number.parseInt(fields[header.bpAIdx] ?? '', 10);
122
+ const snpA = header.snpAIdx >= 0 ? fields[header.snpAIdx] : `${chrA}:${bpA}`;
123
+ const chrB = fields[header.chrBIdx];
124
+ const bpB = Number.parseInt(fields[header.bpBIdx] ?? '', 10);
125
+ const snpB = header.snpBIdx >= 0 ? fields[header.snpBIdx] : `${chrB}:${bpB}`;
126
+ if (!chrA || !chrB || Number.isNaN(bpA) || Number.isNaN(bpB)) {
127
+ return null;
128
+ }
129
+ const r2 = header.r2Idx >= 0
130
+ ? Number.parseFloat(fields[header.r2Idx] ?? '')
131
+ : undefined;
132
+ const dprime = header.dprimeIdx >= 0
133
+ ? Number.parseFloat(fields[header.dprimeIdx] ?? '')
134
+ : undefined;
135
+ const mafA = header.mafAIdx >= 0
136
+ ? Number.parseFloat(fields[header.mafAIdx] ?? '')
137
+ : undefined;
138
+ const mafB = header.mafBIdx >= 0
139
+ ? Number.parseFloat(fields[header.mafBIdx] ?? '')
140
+ : undefined;
141
+ return {
142
+ chrA,
143
+ bpA,
144
+ snpA: snpA ?? `${chrA}:${bpA}`,
145
+ chrB,
146
+ bpB,
147
+ snpB: snpB ?? `${chrB}:${bpB}`,
148
+ r2: r2 ?? 0,
149
+ dprime,
150
+ mafA,
151
+ mafB,
152
+ };
153
+ }
154
+ freeResources() {
155
+ }
156
+ }
@@ -0,0 +1,10 @@
1
+ declare const PlinkLDAdapter: import("node_modules/@jbrowse/core/src/configuration/configurationSchema").ConfigurationSchemaType<{
2
+ ldLocation: {
3
+ type: string;
4
+ defaultValue: {
5
+ uri: string;
6
+ locationType: string;
7
+ };
8
+ };
9
+ }, import("node_modules/@jbrowse/core/src/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
10
+ export default PlinkLDAdapter;