@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
@@ -3,12 +3,13 @@ import { fromNewick } from '@gmod/hclust';
3
3
  import { ConfigurationReference, getConf } from '@jbrowse/core/configuration';
4
4
  import SerializableFilterChain from '@jbrowse/core/pluggableElementTypes/renderers/util/serializableFilterChain';
5
5
  import { set1 } from '@jbrowse/core/ui/colors';
6
- import { getSession } from '@jbrowse/core/util';
6
+ import { SimpleFeature, getContainingTrack, getSession, } from '@jbrowse/core/util';
7
7
  import { stopStopToken } from '@jbrowse/core/util/stopToken';
8
+ import { getRpcSessionId } from '@jbrowse/core/util/tracks';
8
9
  import { cast, isAlive, types } from '@jbrowse/mobx-state-tree';
9
10
  import { linearBareDisplayStateModelFactory } from '@jbrowse/plugin-linear-genome-view';
10
11
  import CategoryIcon from '@mui/icons-material/Category';
11
- import FilterListIcon from '@mui/icons-material/FilterList';
12
+ import ClearAllIcon from '@mui/icons-material/ClearAll';
12
13
  import HeightIcon from '@mui/icons-material/Height';
13
14
  import SplitscreenIcon from '@mui/icons-material/Splitscreen';
14
15
  import VisibilityIcon from '@mui/icons-material/Visibility';
@@ -16,10 +17,10 @@ import { ascending } from '@mui/x-charts-vendor/d3-array';
16
17
  import deepEqual from 'fast-deep-equal';
17
18
  import { NO_CALL_COLOR, OTHER_ALT_COLOR, REFERENCE_COLOR, UNPHASED_COLOR, getAltColorForDosage, } from "./constants.js";
18
19
  import { getSources } from "./getSources.js";
20
+ import { createMAFFilterMenuItem } from "./mafFilterUtils.js";
19
21
  import { cluster, hierarchy } from "../d3-hierarchy2/index.js";
20
- const SetColorDialog = lazy(() => import("./components/SetColorDialog.js"));
21
- const MAFFilterDialog = lazy(() => import("./components/MAFFilterDialog.js"));
22
22
  const AddFiltersDialog = lazy(() => import("./components/AddFiltersDialog.js"));
23
+ const SetColorDialog = lazy(() => import("./components/SetColorDialog.js"));
23
24
  const ClusterDialog = lazy(() => import("./components/MultiVariantClusterDialog/ClusterDialog.js"));
24
25
  const SetRowHeightDialog = lazy(() => import("./components/SetRowHeightDialog.js"));
25
26
  export default function MultiVariantBaseModelF(configSchema) {
@@ -28,14 +29,14 @@ export default function MultiVariantBaseModelF(configSchema) {
28
29
  type: types.literal('LinearVariantMatrixDisplay'),
29
30
  layout: types.optional(types.frozen(), []),
30
31
  configuration: ConfigurationReference(configSchema),
31
- minorAlleleFrequencyFilter: types.optional(types.number, 0),
32
- showSidebarLabelsSetting: types.optional(types.boolean, true),
33
- showTree: types.optional(types.boolean, true),
34
- renderingMode: types.optional(types.string, 'alleleCount'),
32
+ minorAlleleFrequencyFilterSetting: types.maybe(types.number),
33
+ showSidebarLabelsSetting: types.maybe(types.boolean),
34
+ showTreeSetting: types.maybe(types.boolean),
35
+ renderingModeSetting: types.maybe(types.string),
35
36
  rowHeightMode: types.optional(types.union(types.literal('auto'), types.number), 'auto'),
36
37
  lengthCutoffFilter: types.optional(types.number, Number.MAX_SAFE_INTEGER),
37
38
  jexlFilters: types.maybe(types.array(types.string)),
38
- referenceDrawingMode: types.optional(types.string, 'skip'),
39
+ referenceDrawingModeSetting: types.maybe(types.string),
39
40
  clusterTree: types.maybe(types.string),
40
41
  treeAreaWidth: types.optional(types.number, 80),
41
42
  lineZoneHeight: types.optional(types.number, 0),
@@ -45,6 +46,7 @@ export default function MultiVariantBaseModelF(configSchema) {
45
46
  simplifiedFeaturesStopToken: undefined,
46
47
  featureUnderMouseVolatile: undefined,
47
48
  sourcesVolatile: undefined,
49
+ colorByApplied: false,
48
50
  featuresVolatile: undefined,
49
51
  hasPhased: false,
50
52
  sampleInfo: undefined,
@@ -52,6 +54,11 @@ export default function MultiVariantBaseModelF(configSchema) {
52
54
  hoveredTreeNode: undefined,
53
55
  treeCanvas: undefined,
54
56
  mouseoverCanvas: undefined,
57
+ }))
58
+ .views(self => ({
59
+ get renderingMode() {
60
+ return (self.renderingModeSetting ?? getConf(self, 'renderingMode'));
61
+ },
55
62
  }))
56
63
  .actions(self => ({
57
64
  setJexlFilters(f) {
@@ -78,6 +85,9 @@ export default function MultiVariantBaseModelF(configSchema) {
78
85
  setFeatures(f) {
79
86
  self.featuresVolatile = f;
80
87
  },
88
+ setColorByApplied(value) {
89
+ self.colorByApplied = value;
90
+ },
81
91
  setLayout(layout, clearTree = true) {
82
92
  const orderChanged = clearTree &&
83
93
  self.clusterTree &&
@@ -113,16 +123,21 @@ export default function MultiVariantBaseModelF(configSchema) {
113
123
  }
114
124
  },
115
125
  setMafFilter(arg) {
116
- self.minorAlleleFrequencyFilter = arg;
126
+ self.minorAlleleFrequencyFilterSetting = arg;
117
127
  },
118
128
  setShowSidebarLabels(arg) {
119
129
  self.showSidebarLabelsSetting = arg;
120
130
  },
121
131
  setShowTree(arg) {
122
- self.showTree = arg;
132
+ self.showTreeSetting = arg;
123
133
  },
124
134
  setPhasedMode(arg) {
125
- self.renderingMode = arg;
135
+ const currentMode = self.renderingModeSetting ?? getConf(self, 'renderingMode');
136
+ if (currentMode !== arg) {
137
+ self.layout = [];
138
+ self.clusterTree = undefined;
139
+ }
140
+ self.renderingModeSetting = arg;
126
141
  },
127
142
  setAutoHeight(auto) {
128
143
  self.rowHeightMode = auto ? 'auto' : 10;
@@ -136,14 +151,31 @@ export default function MultiVariantBaseModelF(configSchema) {
136
151
  }
137
152
  },
138
153
  setReferenceDrawingMode(arg) {
139
- self.referenceDrawingMode = arg;
154
+ self.referenceDrawingModeSetting = arg;
140
155
  },
141
156
  }))
142
157
  .views(self => ({
143
158
  get autoHeight() {
144
159
  return self.rowHeightMode === 'auto';
145
160
  },
146
- get activeFilters() {
161
+ get minorAlleleFrequencyFilter() {
162
+ return (self.minorAlleleFrequencyFilterSetting ??
163
+ getConf(self, 'minorAlleleFrequencyFilter'));
164
+ },
165
+ get showSidebarLabels() {
166
+ return (self.showSidebarLabelsSetting ?? getConf(self, 'showSidebarLabels'));
167
+ },
168
+ get showTree() {
169
+ return self.showTreeSetting ?? getConf(self, 'showTree');
170
+ },
171
+ get referenceDrawingMode() {
172
+ if (self.referenceDrawingModeSetting !== undefined) {
173
+ return self.referenceDrawingModeSetting;
174
+ }
175
+ const showRef = getConf(self, 'showReferenceAlleles');
176
+ return showRef ? 'draw' : 'skip';
177
+ },
178
+ activeFilters() {
147
179
  return (self.jexlFilters ??
148
180
  getConf(self, 'jexlFilters').map((r) => `jexl:${r}`));
149
181
  },
@@ -157,12 +189,10 @@ export default function MultiVariantBaseModelF(configSchema) {
157
189
  : undefined;
158
190
  },
159
191
  get sources() {
160
- const sourcesWithLayout = self.layout.length
161
- ? self.layout
162
- : self.sourcesVolatile;
163
- return sourcesWithLayout
192
+ return self.sourcesVolatile
164
193
  ? getSources({
165
- sources: sourcesWithLayout,
194
+ sources: self.sourcesVolatile,
195
+ layout: self.layout.length ? self.layout : undefined,
166
196
  renderingMode: self.renderingMode,
167
197
  sampleInfo: self.sampleInfo,
168
198
  })
@@ -184,7 +214,7 @@ export default function MultiVariantBaseModelF(configSchema) {
184
214
  return {
185
215
  get sourceMap() {
186
216
  return self.sources
187
- ? Object.fromEntries(self.sources.map(source => [source.name, source]))
217
+ ? Object.fromEntries(self.sources.map((source) => [source.name, source]))
188
218
  : undefined;
189
219
  },
190
220
  get availableHeight() {
@@ -231,13 +261,13 @@ export default function MultiVariantBaseModelF(configSchema) {
231
261
  {
232
262
  label: 'Show sidebar labels',
233
263
  type: 'checkbox',
234
- checked: self.showSidebarLabelsSetting,
264
+ checked: self.showSidebarLabels,
235
265
  onClick: () => {
236
- self.setShowSidebarLabels(!self.showSidebarLabelsSetting);
266
+ self.setShowSidebarLabels(!self.showSidebarLabels);
237
267
  },
238
268
  },
239
269
  {
240
- label: 'Show tree',
270
+ label: `Show tree${!self.clusterTree ? ' (run clustering first)' : ''}`,
241
271
  type: 'checkbox',
242
272
  checked: self.showTree,
243
273
  disabled: !self.clusterTree,
@@ -305,9 +335,11 @@ export default function MultiVariantBaseModelF(configSchema) {
305
335
  },
306
336
  },
307
337
  {
308
- label: `Phased${!self.hasPhased
309
- ? ' (disabled, no phased variants found)'
310
- : ''}`,
338
+ label: `Phased${self.hasPhased
339
+ ? ''
340
+ : !self.featuresVolatile
341
+ ? ' (checking for phased variants...)'
342
+ : ' (disabled, no phased variants found)'}`,
311
343
  helpText: 'Phased mode splits each sample into multiple rows representing each haplotype, and the phasing of the variants is used to color the variant in the individual haplotype rows. For example, a diploid sample SAMPLE1 will generate two rows SAMPLE1-HP0 and SAMPLE1 HP1 and a variant 1|0 will draw a box in the top row but not the bottom row',
312
344
  disabled: !self.hasPhased,
313
345
  checked: self.renderingMode === 'phased',
@@ -320,20 +352,9 @@ export default function MultiVariantBaseModelF(configSchema) {
320
352
  },
321
353
  {
322
354
  label: 'Filter by',
323
- icon: FilterListIcon,
355
+ icon: ClearAllIcon,
324
356
  subMenu: [
325
- {
326
- label: 'Minor allele frequency',
327
- onClick: () => {
328
- getSession(self).queueDialog(handleClose => [
329
- MAFFilterDialog,
330
- {
331
- model: self,
332
- handleClose,
333
- },
334
- ]);
335
- },
336
- },
357
+ createMAFFilterMenuItem(self),
337
358
  {
338
359
  label: 'Edit filters',
339
360
  onClick: () => {
@@ -379,7 +400,7 @@ export default function MultiVariantBaseModelF(configSchema) {
379
400
  })
380
401
  .views(self => ({
381
402
  get canDisplayLabels() {
382
- return self.rowHeight >= 6 && self.showSidebarLabelsSetting;
403
+ return self.rowHeight >= 6 && self.showSidebarLabels;
383
404
  },
384
405
  get totalHeight() {
385
406
  return self.rowHeight * (self.sources?.length || 1);
@@ -419,10 +440,36 @@ export default function MultiVariantBaseModelF(configSchema) {
419
440
  scrollTop: self.scrollTop,
420
441
  referenceDrawingMode: self.referenceDrawingMode,
421
442
  filters: new SerializableFilterChain({
422
- filters: self.activeFilters,
443
+ filters: self.activeFilters(),
423
444
  }),
424
445
  };
425
446
  },
447
+ renderingProps() {
448
+ return {
449
+ displayModel: self,
450
+ async onFeatureClick(_, featureId) {
451
+ const session = getSession(self);
452
+ const { rpcManager } = session;
453
+ try {
454
+ const sessionId = getRpcSessionId(self);
455
+ const track = getContainingTrack(self);
456
+ const { feature } = (await rpcManager.call(sessionId, 'MultiVariantGetFeatureDetails', {
457
+ featureId,
458
+ sessionId,
459
+ trackInstanceId: track.id,
460
+ rendererType: self.rendererTypeName,
461
+ }));
462
+ if (isAlive(self) && feature) {
463
+ self.selectFeature(new SimpleFeature(feature));
464
+ }
465
+ }
466
+ catch (e) {
467
+ console.error(e);
468
+ session.notifyError(`${e}`, e);
469
+ }
470
+ },
471
+ };
472
+ },
426
473
  legendItems() {
427
474
  if (self.renderingMode === 'phased') {
428
475
  let maxAltAlleles = 1;
@@ -478,20 +525,26 @@ export default function MultiVariantBaseModelF(configSchema) {
478
525
  if (!snap) {
479
526
  return snap;
480
527
  }
481
- const { layout, minorAlleleFrequencyFilter, showSidebarLabelsSetting, showTree, renderingMode, rowHeightMode, lengthCutoffFilter, jexlFilters, referenceDrawingMode, clusterTree, treeAreaWidth, lineZoneHeight, ...rest } = snap;
528
+ const { layout, minorAlleleFrequencyFilterSetting, showSidebarLabelsSetting, showTreeSetting, renderingModeSetting, rowHeightMode, lengthCutoffFilter, jexlFilters, referenceDrawingModeSetting, clusterTree, treeAreaWidth, lineZoneHeight, ...rest } = snap;
482
529
  return {
483
530
  ...rest,
484
531
  ...(layout.length ? { layout } : {}),
485
- ...(minorAlleleFrequencyFilter ? { minorAlleleFrequencyFilter } : {}),
486
- ...(!showSidebarLabelsSetting ? { showSidebarLabelsSetting } : {}),
487
- ...(!showTree ? { showTree } : {}),
488
- ...(renderingMode !== 'alleleCount' ? { renderingMode } : {}),
532
+ ...(minorAlleleFrequencyFilterSetting !== undefined
533
+ ? { minorAlleleFrequencyFilterSetting }
534
+ : {}),
535
+ ...(showSidebarLabelsSetting !== undefined
536
+ ? { showSidebarLabelsSetting }
537
+ : {}),
538
+ ...(showTreeSetting !== undefined ? { showTreeSetting } : {}),
539
+ ...(renderingModeSetting !== undefined ? { renderingModeSetting } : {}),
489
540
  ...(rowHeightMode !== 'auto' ? { rowHeightMode } : {}),
490
541
  ...(lengthCutoffFilter !== Number.MAX_SAFE_INTEGER
491
542
  ? { lengthCutoffFilter }
492
543
  : {}),
493
544
  ...(jexlFilters?.length ? { jexlFilters } : {}),
494
- ...(referenceDrawingMode !== 'skip' ? { referenceDrawingMode } : {}),
545
+ ...(referenceDrawingModeSetting !== undefined
546
+ ? { referenceDrawingModeSetting }
547
+ : {}),
495
548
  ...(clusterTree !== undefined ? { clusterTree } : {}),
496
549
  ...(treeAreaWidth !== 80 ? { treeAreaWidth } : {}),
497
550
  ...(lineZoneHeight ? { lineZoneHeight } : {}),
@@ -1,4 +1,30 @@
1
- export default function sharedVariantConfigFactory(): import("node_modules/@jbrowse/core/src/configuration/configurationSchema").ConfigurationSchemaType<{}, import("node_modules/@jbrowse/core/src/configuration/configurationSchema").ConfigurationSchemaOptions<import("node_modules/@jbrowse/core/src/configuration/configurationSchema").ConfigurationSchemaType<{
1
+ export default function sharedVariantConfigFactory(): import("node_modules/@jbrowse/core/src/configuration/configurationSchema").ConfigurationSchemaType<{
2
+ showReferenceAlleles: {
3
+ type: string;
4
+ defaultValue: boolean;
5
+ };
6
+ showSidebarLabels: {
7
+ type: string;
8
+ defaultValue: boolean;
9
+ };
10
+ showTree: {
11
+ type: string;
12
+ defaultValue: boolean;
13
+ };
14
+ renderingMode: {
15
+ type: string;
16
+ model: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
17
+ defaultValue: string;
18
+ };
19
+ minorAlleleFrequencyFilter: {
20
+ type: string;
21
+ defaultValue: number;
22
+ };
23
+ colorBy: {
24
+ type: string;
25
+ defaultValue: string;
26
+ };
27
+ }, import("node_modules/@jbrowse/core/src/configuration/configurationSchema").ConfigurationSchemaOptions<import("node_modules/@jbrowse/core/src/configuration/configurationSchema").ConfigurationSchemaType<{
2
28
  maxFeatureScreenDensity: {
3
29
  type: string;
4
30
  description: string;
@@ -1,7 +1,34 @@
1
1
  import { ConfigurationSchema } from '@jbrowse/core/configuration';
2
+ import { types } from '@jbrowse/mobx-state-tree';
2
3
  import { baseLinearDisplayConfigSchema } from '@jbrowse/plugin-linear-genome-view';
3
4
  export default function sharedVariantConfigFactory() {
4
- return ConfigurationSchema('SharedVariantDisplay', {}, {
5
+ return ConfigurationSchema('SharedVariantDisplay', {
6
+ showReferenceAlleles: {
7
+ type: 'boolean',
8
+ defaultValue: false,
9
+ },
10
+ showSidebarLabels: {
11
+ type: 'boolean',
12
+ defaultValue: true,
13
+ },
14
+ showTree: {
15
+ type: 'boolean',
16
+ defaultValue: true,
17
+ },
18
+ renderingMode: {
19
+ type: 'stringEnum',
20
+ model: types.enumeration('RenderingMode', ['alleleCount', 'phased']),
21
+ defaultValue: 'alleleCount',
22
+ },
23
+ minorAlleleFrequencyFilter: {
24
+ type: 'number',
25
+ defaultValue: 0,
26
+ },
27
+ colorBy: {
28
+ type: 'string',
29
+ defaultValue: '',
30
+ },
31
+ }, {
5
32
  baseConfiguration: baseLinearDisplayConfigSchema,
6
33
  explicitlyTyped: true,
7
34
  });
@@ -0,0 +1,27 @@
1
+ import type { Feature } from '@jbrowse/core/util';
2
+ export declare class VariantFeatureSession {
3
+ private features;
4
+ addFeatures(refName: string, features: Map<string, Feature>): void;
5
+ getDataByID(id: string): Feature | undefined;
6
+ discardRange(refName: string, start: number, end: number): void;
7
+ }
8
+ export declare class VariantFeatureCacheManager {
9
+ private sessions;
10
+ getSession(args: {
11
+ sessionId: string;
12
+ trackInstanceId: string;
13
+ }): VariantFeatureSession;
14
+ freeResources(args: {
15
+ sessionId: string;
16
+ trackInstanceId: string;
17
+ regions: {
18
+ refName: string;
19
+ start: number;
20
+ end: number;
21
+ }[];
22
+ }): void;
23
+ deleteSession(args: {
24
+ sessionId: string;
25
+ trackInstanceId: string;
26
+ }): void;
27
+ }
@@ -0,0 +1,48 @@
1
+ export class VariantFeatureSession {
2
+ features = new Map();
3
+ addFeatures(refName, features) {
4
+ for (const [id, feature] of features) {
5
+ this.features.set(id, feature);
6
+ }
7
+ }
8
+ getDataByID(id) {
9
+ return this.features.get(id);
10
+ }
11
+ discardRange(refName, start, end) {
12
+ for (const [id, feature] of this.features) {
13
+ const fRefName = feature.get('refName');
14
+ const fStart = feature.get('start');
15
+ const fEnd = feature.get('end');
16
+ if (fRefName === refName && fStart < end && fEnd > start) {
17
+ this.features.delete(id);
18
+ }
19
+ }
20
+ }
21
+ }
22
+ function getLayoutId(args) {
23
+ return `${args.sessionId}-${args.trackInstanceId}`;
24
+ }
25
+ export class VariantFeatureCacheManager {
26
+ sessions = {};
27
+ getSession(args) {
28
+ const key = getLayoutId(args);
29
+ if (!this.sessions[key]) {
30
+ this.sessions[key] = new VariantFeatureSession();
31
+ }
32
+ return this.sessions[key];
33
+ }
34
+ freeResources(args) {
35
+ const key = getLayoutId(args);
36
+ const session = this.sessions[key];
37
+ if (session) {
38
+ const region = args.regions[0];
39
+ if (region) {
40
+ session.discardRange(region.refName, region.start, region.end);
41
+ }
42
+ }
43
+ }
44
+ deleteSession(args) {
45
+ const key = getLayoutId(args);
46
+ delete this.sessions[key];
47
+ }
48
+ }
@@ -0,0 +1,23 @@
1
+ import FeatureRendererType from '@jbrowse/core/pluggableElementTypes/renderers/FeatureRendererType';
2
+ import { VariantFeatureCacheManager } from './VariantFeatureCache.ts';
3
+ import type { Feature } from '@jbrowse/core/util';
4
+ export default class VariantRendererType extends FeatureRendererType {
5
+ featureCacheManager: VariantFeatureCacheManager;
6
+ getFeatureById(featureId: string, args: {
7
+ sessionId: string;
8
+ trackInstanceId: string;
9
+ }): Feature | undefined;
10
+ freeResources(args: {
11
+ sessionId: string;
12
+ trackInstanceId: string;
13
+ regions: {
14
+ refName: string;
15
+ start: number;
16
+ end: number;
17
+ }[];
18
+ }): void;
19
+ protected cacheFeatures(args: {
20
+ sessionId: string;
21
+ trackInstanceId: string;
22
+ }, refName: string, features: Map<string, Feature>): void;
23
+ }
@@ -0,0 +1,15 @@
1
+ import FeatureRendererType from '@jbrowse/core/pluggableElementTypes/renderers/FeatureRendererType';
2
+ import { VariantFeatureCacheManager } from "./VariantFeatureCache.js";
3
+ export default class VariantRendererType extends FeatureRendererType {
4
+ featureCacheManager = new VariantFeatureCacheManager();
5
+ getFeatureById(featureId, args) {
6
+ return this.featureCacheManager.getSession(args).getDataByID(featureId);
7
+ }
8
+ freeResources(args) {
9
+ this.featureCacheManager.freeResources(args);
10
+ }
11
+ cacheFeatures(args, refName, features) {
12
+ const session = this.featureCacheManager.getSession(args);
13
+ session.addFeatures(refName, features);
14
+ }
15
+ }
@@ -0,0 +1,9 @@
1
+ import type { Source } from './types.ts';
2
+ export declare function applyColorPalette(sources: Source[], attribute: string): Source[] | {
3
+ color: string | undefined;
4
+ baseUri?: string;
5
+ name: string;
6
+ baseName?: string;
7
+ group?: string;
8
+ HP?: number;
9
+ }[];
@@ -0,0 +1,23 @@
1
+ import { set1 } from '@jbrowse/core/ui/colors';
2
+ import { randomColor } from '@jbrowse/core/util/color';
3
+ export function applyColorPalette(sources, attribute) {
4
+ if (!attribute || sources.length === 0) {
5
+ return sources;
6
+ }
7
+ const hasAttribute = sources.some(source => attribute in source);
8
+ if (!hasAttribute) {
9
+ return sources;
10
+ }
11
+ const counts = new Map();
12
+ for (const source of sources) {
13
+ const key = String(source[attribute] ?? '');
14
+ counts.set(key, (counts.get(key) || 0) + 1);
15
+ }
16
+ const colorMap = Object.fromEntries([...counts.entries()]
17
+ .sort((a, b) => a[1] - b[1])
18
+ .map(([key], idx) => [key, set1[idx] || randomColor(key)]));
19
+ return sources.map(source => ({
20
+ ...source,
21
+ color: colorMap[String(source[attribute] ?? '')],
22
+ }));
23
+ }
@@ -0,0 +1,10 @@
1
+ import type { Source } from './types.ts';
2
+ import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
3
+ export declare function setupColorByAutorun(self: {
4
+ configuration: AnyConfigurationModel;
5
+ sourcesVolatile: Source[] | undefined;
6
+ layout: Source[];
7
+ colorByApplied: boolean;
8
+ setLayout: (layout: Source[], clearTree?: boolean) => void;
9
+ setColorByApplied: (value: boolean) => void;
10
+ }): void;
@@ -0,0 +1,39 @@
1
+ import { getConf } from '@jbrowse/core/configuration';
2
+ import { addDisposer, isAlive } from '@jbrowse/mobx-state-tree';
3
+ import { autorun } from 'mobx';
4
+ import { applyColorPalette } from "./applyColorPalette.js";
5
+ export function setupColorByAutorun(self) {
6
+ addDisposer(self, autorun(() => {
7
+ if (!isAlive(self)) {
8
+ return;
9
+ }
10
+ if (self.colorByApplied) {
11
+ return;
12
+ }
13
+ const sources = self.sourcesVolatile;
14
+ if (!sources || sources.length === 0) {
15
+ return;
16
+ }
17
+ if (self.layout.length > 0) {
18
+ self.setColorByApplied(true);
19
+ return;
20
+ }
21
+ const colorBy = getConf(self, 'colorBy');
22
+ if (!colorBy) {
23
+ self.setColorByApplied(true);
24
+ return;
25
+ }
26
+ const hasAttribute = sources.some(source => colorBy in source);
27
+ if (!hasAttribute) {
28
+ console.warn(`colorBy attribute "${colorBy}" not found in sample metadata. ` +
29
+ `Available attributes: ${Object.keys(sources[0] || {}).join(', ')}`);
30
+ self.setColorByApplied(true);
31
+ return;
32
+ }
33
+ const coloredSources = applyColorPalette(sources, colorBy);
34
+ self.setLayout(coloredSources, false);
35
+ self.setColorByApplied(true);
36
+ }, {
37
+ name: 'ColorByAutorun',
38
+ }));
39
+ }
@@ -1,8 +1,8 @@
1
- export default function AddFiltersDialog({ model, handleClose, }: {
1
+ declare const AddFiltersDialog: ({ model, handleClose, }: {
2
2
  model: {
3
3
  jexlFilters?: string[];
4
- activeFilters: string[];
5
4
  setJexlFilters: (arg?: string[]) => void;
6
5
  };
7
6
  handleClose: () => void;
8
- }): import("react/jsx-runtime").JSX.Element;
7
+ }) => import("react/jsx-runtime").JSX.Element;
8
+ export default AddFiltersDialog;
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo, useState } from 'react';
2
+ import { useEffect, useState } from 'react';
3
3
  import { Dialog } from '@jbrowse/core/ui';
4
4
  import { stringToJexlExpression } from '@jbrowse/core/util/jexlStrings';
5
5
  import { makeStyles } from '@jbrowse/core/util/tss-react';
6
6
  import { Button, DialogActions, DialogContent, TextField } from '@mui/material';
7
+ import { observer } from 'mobx-react';
7
8
  const useStyles = makeStyles()({
8
9
  dialogContent: {
9
10
  width: '80em',
@@ -16,27 +17,30 @@ const useStyles = makeStyles()({
16
17
  fontSize: '0.8em',
17
18
  },
18
19
  });
19
- function validateJexl(data) {
20
- try {
21
- for (const line of data
22
- .split('\n')
23
- .map(l => l.trim())
24
- .filter(Boolean)) {
25
- stringToJexlExpression(line);
26
- }
27
- return undefined;
28
- }
29
- catch (e) {
30
- console.error(e);
31
- return e;
32
- }
20
+ function checkJexl(code) {
21
+ stringToJexlExpression(code);
33
22
  }
34
- export default function AddFiltersDialog({ model, handleClose, }) {
23
+ const AddFiltersDialog = observer(function AddFiltersDialog({ model, handleClose, }) {
35
24
  const { classes } = useStyles();
36
- const { activeFilters } = model;
37
- const [data, setData] = useState(activeFilters.join('\n'));
38
- const error = useMemo(() => validateJexl(data), [data]);
39
- return (_jsxs(Dialog, { maxWidth: "xl", open: true, onClose: handleClose, title: "Add track filters", children: [_jsxs(DialogContent, { children: [_jsxs("div", { children: ["Add filters, in jexl format, one per line, starting with the string jexl:. Examples:", ' ', _jsxs("ul", { children: [_jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'name')=='BRCA1'" }), " - show only feature where the name attribute is BRCA1"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'type')=='gene'" }), " - show only gene type features in a GFF that has many other feature types"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'score') > 400" }), " - show only features that have a score greater than 400"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'end') - get(feature,'start') < 1000000" }), ' ', "- show only features with length less than 1Mbp"] })] })] }), error ? _jsx("p", { className: classes.error, children: `${error}` }) : null, _jsx(TextField, { variant: "outlined", multiline: true, minRows: 5, maxRows: 10, className: classes.dialogContent, fullWidth: true, value: data, onChange: event => {
25
+ const { jexlFilters } = model;
26
+ const [data, setData] = useState((jexlFilters ?? []).join('\n'));
27
+ const [error, setError] = useState();
28
+ useEffect(() => {
29
+ try {
30
+ for (const line of data
31
+ .split('\n')
32
+ .map(line => line.trim())
33
+ .filter(line => !!line)) {
34
+ checkJexl(line);
35
+ }
36
+ setError(undefined);
37
+ }
38
+ catch (e) {
39
+ console.error(e);
40
+ setError(e);
41
+ }
42
+ }, [data]);
43
+ return (_jsxs(Dialog, { maxWidth: "xl", open: true, onClose: handleClose, title: "Add track filters", children: [_jsxs(DialogContent, { children: [_jsxs("div", { children: ["Add filters, in jexl format, one per line, starting with the string jexl:. Examples:", ' ', _jsxs("ul", { children: [_jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'name')=='BRCA1'" }), " - show only feature where the name attribute is BRCA1"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:startsWith(get(feature,'name'),'PREFIX')" }), " - show only feature where the string 'PREFIX' is the prefix of feature name. endsWith also works"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:includes(get(feature,'name'),'PREFIX')" }), " - show only feature where the string 'PREFIX' is the prefix of feature name"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'type')=='gene'" }), " - show only gene type features in a GFF that has many other feature types"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'score') > 400" }), " - show only features that have a score greater than 400"] }), _jsxs("li", { children: [_jsx("code", { children: "jexl:get(feature,'end') - get(feature,'start') < 1000000" }), ' ', "- show only features with length less than 1Mbp"] })] }), _jsxs("p", { children: ["Please see", ' ', _jsx("a", { href: "https://jbrowse.org/jb2/docs/config_guides/jexl/", children: "Jexl" }), ' ', "documentation for more information"] })] }), error ? _jsx("p", { className: classes.error, children: `${error}` }) : null, _jsx(TextField, { variant: "outlined", multiline: true, minRows: 5, maxRows: 10, className: classes.dialogContent, fullWidth: true, value: data, onChange: event => {
40
44
  setData(event.target.value);
41
45
  }, slotProps: {
42
46
  input: {
@@ -47,5 +51,8 @@ export default function AddFiltersDialog({ model, handleClose, }) {
47
51
  } })] }), _jsxs(DialogActions, { children: [_jsx(Button, { variant: "contained", color: "primary", type: "submit", autoFocus: true, disabled: !!error, onClick: () => {
48
52
  model.setJexlFilters(data.split('\n'));
49
53
  handleClose();
50
- }, children: "Submit" }), _jsx(Button, { variant: "contained", color: "secondary", onClick: handleClose, children: "Cancel" })] })] }));
51
- }
54
+ }, children: "Submit" }), _jsx(Button, { variant: "contained", color: "secondary", onClick: () => {
55
+ handleClose();
56
+ }, children: "Cancel" })] })] }));
57
+ });
58
+ export default AddFiltersDialog;