jbrowse-plugin-mafviewer 1.0.2 → 1.0.4

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.
@@ -1,10 +1,10 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@jbrowse/core/Plugin'), require('@jbrowse/core/pluggableElementTypes'), require('@jbrowse/core/configuration'), require('@jbrowse/core/data_adapters/BaseAdapter'), require('mobx-state-tree'), require('@jbrowse/core/util'), require('@jbrowse/core/util/rxjs'), require('react'), require('mobx-react'), require('@jbrowse/core/ui'), require('@mui/material'), require('tss-react/mui')) :
3
- typeof define === 'function' && define.amd ? define(['exports', '@jbrowse/core/Plugin', '@jbrowse/core/pluggableElementTypes', '@jbrowse/core/configuration', '@jbrowse/core/data_adapters/BaseAdapter', 'mobx-state-tree', '@jbrowse/core/util', '@jbrowse/core/util/rxjs', 'react', 'mobx-react', '@jbrowse/core/ui', '@mui/material', 'tss-react/mui'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.JBrowsePluginMafViewer = {}, global.JBrowseExports["@jbrowse/core/Plugin"], global.JBrowseExports["@jbrowse/core/pluggableElementTypes"], global.JBrowseExports["@jbrowse/core/configuration"], global.JBrowseExports["@jbrowse/core/data_adapters/BaseAdapter"], global.JBrowseExports["mobx-state-tree"], global.JBrowseExports["@jbrowse/core/util"], global.JBrowseExports["@jbrowse/core/util/rxjs"], global.JBrowseExports.react, global.JBrowseExports["mobx-react"], global.JBrowseExports["@jbrowse/core/ui"], global.JBrowseExports["@mui/material"], global.JBrowseExports["tss-react/mui"]));
5
- })(this, (function (exports, Plugin, pluggableElementTypes, configuration, BaseAdapter, mobxStateTree, util, rxjs, React, mobxReact, ui, material, mui) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@jbrowse/core/Plugin'), require('@jbrowse/core/pluggableElementTypes'), require('@jbrowse/core/configuration'), require('@jbrowse/core/data_adapters/BaseAdapter'), require('mobx-state-tree'), require('@jbrowse/core/util'), require('@jbrowse/core/util/rxjs'), require('react'), require('mobx-react'), require('@mui/material'), require('@jbrowse/core/ui'), require('tss-react/mui')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', '@jbrowse/core/Plugin', '@jbrowse/core/pluggableElementTypes', '@jbrowse/core/configuration', '@jbrowse/core/data_adapters/BaseAdapter', 'mobx-state-tree', '@jbrowse/core/util', '@jbrowse/core/util/rxjs', 'react', 'mobx-react', '@mui/material', '@jbrowse/core/ui', 'tss-react/mui'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.JBrowsePluginMafViewer = {}, global.JBrowseExports["@jbrowse/core/Plugin"], global.JBrowseExports["@jbrowse/core/pluggableElementTypes"], global.JBrowseExports["@jbrowse/core/configuration"], global.JBrowseExports["@jbrowse/core/data_adapters/BaseAdapter"], global.JBrowseExports["mobx-state-tree"], global.JBrowseExports["@jbrowse/core/util"], global.JBrowseExports["@jbrowse/core/util/rxjs"], global.JBrowseExports.react, global.JBrowseExports["mobx-react"], global.JBrowseExports["@mui/material"], global.JBrowseExports["@jbrowse/core/ui"], global.JBrowseExports["tss-react/mui"]));
5
+ })(this, (function (exports, Plugin, pluggableElementTypes, configuration, BaseAdapter, mobxStateTree, util, rxjs, React, mobxReact, material, ui, mui) { 'use strict';
6
6
 
7
- var version = "1.0.2";
7
+ var version = "1.0.4";
8
8
 
9
9
  const configSchema$2 = configuration.ConfigurationSchema('BigMafAdapter', {
10
10
  /**
@@ -649,28 +649,19 @@
649
649
  const alignments = {};
650
650
  const blocks2 = [];
651
651
  for (const block of blocks) {
652
- if (block[0] === 's') {
653
- if (!aln) {
654
- aln = block.split(/ +/)[6];
655
- alns.push(aln);
652
+ if (block.startsWith('s')) {
653
+ if (aln) {
654
+ alns.push(block.split(/ +/)[6]);
656
655
  blocks2.push(block);
657
656
  }
658
657
  else {
659
- alns.push(block.split(/ +/)[6]);
658
+ aln = block.split(/ +/)[6];
659
+ alns.push(aln);
660
660
  blocks2.push(block);
661
661
  }
662
662
  }
663
663
  }
664
- const alns2 = alns.map(() => '');
665
- if (aln) {
666
- for (let i = 0; i < aln?.length; i++) {
667
- if (aln[i] !== '-') {
668
- for (let j = 0; j < alns.length; j++) {
669
- alns2[j] += alns[j][i];
670
- }
671
- }
672
- }
673
- }
664
+ // eslint-disable-next-line unicorn/no-for-loop
674
665
  for (let i = 0; i < blocks2.length; i++) {
675
666
  const elt = blocks2[i];
676
667
  const ad = elt.split(/ +/);
@@ -683,7 +674,7 @@
683
674
  srcSize: +ad[2],
684
675
  strand: ad[3] === '+' ? 1 : -1,
685
676
  unknown: +ad[4],
686
- data: alns2[i],
677
+ data: alns[i],
687
678
  };
688
679
  }
689
680
  observer.next(new util.SimpleFeature({
@@ -692,7 +683,7 @@
692
683
  start: feature.get('start'),
693
684
  end: feature.get('end'),
694
685
  refName: feature.get('refName'),
695
- seq: alns2[0],
686
+ seq: alns[0],
696
687
  alignments: alignments,
697
688
  },
698
689
  }));
@@ -738,19 +729,42 @@
738
729
 
739
730
  function configSchemaF(pluginManager) {
740
731
  const LinearGenomePlugin = pluginManager.getPlugin('LinearGenomeViewPlugin');
741
- // @ts-expect-error
742
- const { linearBasicDisplayConfigSchemaFactory } = LinearGenomePlugin.exports;
732
+ const { baseLinearDisplayConfigSchema } = LinearGenomePlugin.exports;
743
733
  return configuration.ConfigurationSchema('LinearMafDisplay', {
744
734
  /**
745
735
  * #slot
746
736
  */
747
737
  renderer: pluginManager.pluggableConfigSchemaType('renderer'),
748
738
  }, {
749
- baseConfiguration: linearBasicDisplayConfigSchemaFactory(pluginManager),
739
+ baseConfiguration: baseLinearDisplayConfigSchema,
750
740
  explicitlyTyped: true,
751
741
  });
752
742
  }
753
743
 
744
+ const useStyles$1 = mui.makeStyles()({
745
+ root: {
746
+ width: 500,
747
+ },
748
+ });
749
+ const SetRowHeightDialog = mobxReact.observer(function (props) {
750
+ const { model, handleClose } = props;
751
+ const { classes } = useStyles$1();
752
+ const [rowHeight, setRowHeight] = React.useState(`${model.rowHeight}`);
753
+ const [rowProportion, setRowProportion] = React.useState(`${model.rowProportion}`);
754
+ return (React.createElement(ui.Dialog, { open: true, onClose: handleClose, title: "Filter options" },
755
+ React.createElement(material.DialogContent, { className: classes.root },
756
+ React.createElement(material.Typography, null, "Set row height and the proportion of the row height to use for drawing each row"),
757
+ React.createElement(material.TextField, { value: rowHeight, onChange: event => setRowHeight(event.target.value), placeholder: "Enter row height" }),
758
+ React.createElement(material.TextField, { value: rowProportion, onChange: event => setRowProportion(event.target.value), placeholder: "Enter row proportion" }),
759
+ React.createElement(material.DialogActions, null,
760
+ React.createElement(material.Button, { variant: "contained", color: "primary", type: "submit", autoFocus: true, onClick: () => {
761
+ model.setRowProportion(+rowProportion);
762
+ model.setRowHeight(+rowHeight);
763
+ handleClose();
764
+ } }, "Submit"),
765
+ React.createElement(material.Button, { variant: "contained", color: "secondary", onClick: () => handleClose() }, "Cancel")))));
766
+ });
767
+
754
768
  function isStrs(array) {
755
769
  return typeof array[0] === 'string';
756
770
  }
@@ -760,10 +774,9 @@
760
774
  */
761
775
  function stateModelFactory(configSchema, pluginManager) {
762
776
  const LinearGenomePlugin = pluginManager.getPlugin('LinearGenomeViewPlugin');
763
- // @ts-expect-error
764
- const { linearBasicDisplayModelFactory } = LinearGenomePlugin.exports;
777
+ const { BaseLinearDisplay } = LinearGenomePlugin.exports;
765
778
  return mobxStateTree.types
766
- .compose('LinearMafDisplay', linearBasicDisplayModelFactory(configSchema), mobxStateTree.types.model({
779
+ .compose('LinearMafDisplay', BaseLinearDisplay, mobxStateTree.types.model({
767
780
  /**
768
781
  * #property
769
782
  */
@@ -772,28 +785,51 @@
772
785
  * #property
773
786
  */
774
787
  configuration: configuration.ConfigurationReference(configSchema),
788
+ /**
789
+ * #property
790
+ */
791
+ rowHeight: 15,
792
+ /**
793
+ * #property
794
+ */
795
+ rowProportion: 0.8,
796
+ /**
797
+ * #property
798
+ */
799
+ showAllLetters: false,
775
800
  }))
776
801
  .volatile(() => ({
777
802
  prefersOffset: true,
778
803
  }))
779
- .views(self => ({
804
+ .actions(self => ({
780
805
  /**
781
- * #getter
806
+ * #action
782
807
  */
783
- get samples() {
784
- const r = self.adapterConfig.samples;
785
- if (isStrs(r)) {
786
- return r.map(elt => ({ id: elt, label: elt, color: undefined }));
787
- }
788
- else {
789
- return r;
790
- }
808
+ setRowHeight(n) {
809
+ self.rowHeight = n;
810
+ },
811
+ /**
812
+ * #action
813
+ */
814
+ setRowProportion(n) {
815
+ self.rowProportion = n;
816
+ },
817
+ /**
818
+ * #action
819
+ */
820
+ setShowAllLetters(f) {
821
+ self.showAllLetters = f;
791
822
  },
823
+ }))
824
+ .views(self => ({
792
825
  /**
793
826
  * #getter
794
827
  */
795
- get rowHeight() {
796
- return 20;
828
+ get samples() {
829
+ const r = self.adapterConfig.samples;
830
+ return isStrs(r)
831
+ ? r.map(elt => ({ id: elt, label: elt, color: undefined }))
832
+ : r;
797
833
  },
798
834
  /**
799
835
  * #getter
@@ -814,21 +850,51 @@
814
850
  },
815
851
  }))
816
852
  .views(self => {
817
- const { renderProps: superRenderProps } = self;
853
+ const { trackMenuItems: superTrackMenuItems, renderProps: superRenderProps, } = self;
818
854
  return {
819
855
  /**
820
856
  * #method
821
857
  */
822
858
  renderProps() {
859
+ const { showAllLetters, rendererConfig, samples, rowHeight, rowProportion, } = self;
823
860
  return {
824
861
  ...superRenderProps(),
825
- samples: self.samples,
826
- rowHeight: self.rowHeight,
862
+ config: rendererConfig,
863
+ samples,
864
+ rowHeight,
865
+ rowProportion,
866
+ showAllLetters,
827
867
  };
828
868
  },
869
+ /**
870
+ * #method
871
+ */
872
+ trackMenuItems() {
873
+ return [
874
+ ...superTrackMenuItems(),
875
+ {
876
+ label: 'Set row height',
877
+ onClick: () => {
878
+ util.getSession(self).queueDialog(handleClose => [
879
+ SetRowHeightDialog,
880
+ { model: self, handleClose },
881
+ ]);
882
+ },
883
+ },
884
+ {
885
+ label: 'Show all letters',
886
+ type: 'checkbox',
887
+ checked: self.showAllLetters,
888
+ onClick: () => {
889
+ self.setShowAllLetters(!self.showAllLetters);
890
+ },
891
+ },
892
+ ];
893
+ },
829
894
  };
830
895
  })
831
896
  .actions(self => {
897
+ // eslint-disable-next-line @typescript-eslint/unbound-method
832
898
  const { renderSvg: superRenderSvg } = self;
833
899
  return {
834
900
  /**
@@ -836,7 +902,6 @@
836
902
  */
837
903
  async renderSvg(opts) {
838
904
  const { renderSvg } = await Promise.resolve().then(function () { return renderSvg$1; });
839
- // @ts-expect-error
840
905
  return renderSvg(self, opts, superRenderSvg);
841
906
  },
842
907
  };
@@ -848,19 +913,15 @@
848
913
  return React.createElement("rect", { ...props, fill: color });
849
914
  };
850
915
 
851
- const ColorLegend = mobxReact.observer(function ({ model, labelWidth, }) {
916
+ const ColorLegend = mobxReact.observer(function ({ model, labelWidth, svgFontSize, }) {
852
917
  const { samples, rowHeight } = model;
853
- const svgFontSize = Math.min(rowHeight, 12);
854
- const canDisplayLabel = rowHeight > 11;
855
- const colorBoxWidth = 0;
856
- const legendWidth = labelWidth + colorBoxWidth + 5;
857
- const extraOffset = 0;
858
- return samples ? (React.createElement(React.Fragment, null, samples.map((sample, idx) => {
859
- const boxHeight = Math.min(20, rowHeight);
860
- return (React.createElement(React.Fragment, { key: `${sample.id}-${idx}` },
861
- React.createElement(RectBg, { y: idx * rowHeight + 1, x: extraOffset, width: legendWidth, height: boxHeight, color: sample.color }),
862
- canDisplayLabel ? (React.createElement("text", { y: idx * rowHeight + 14, x: extraOffset + colorBoxWidth + 2, fontSize: svgFontSize }, sample.label)) : null));
863
- }))) : null;
918
+ const canDisplayLabel = rowHeight >= 10;
919
+ const boxHeight = Math.min(20, rowHeight);
920
+ return samples ? (React.createElement(React.Fragment, null,
921
+ samples.map((sample, idx) => (React.createElement(RectBg, { key: `${sample.id}-${idx}`, y: idx * rowHeight, x: 0, width: labelWidth + 5, height: boxHeight, color: sample.color }))),
922
+ canDisplayLabel
923
+ ? samples.map((sample, idx) => (React.createElement("text", { key: `${sample.id}-${idx}`, y: idx * rowHeight + rowHeight / 2, dominantBaseline: "middle", x: 2, fontSize: svgFontSize }, sample.label)))
924
+ : null)) : null;
864
925
  });
865
926
 
866
927
  const Wrapper = mobxReact.observer(function ({ children, model, exportSVG, }) {
@@ -882,14 +943,14 @@
882
943
  const YScaleBars = mobxReact.observer(function (props) {
883
944
  const { model } = props;
884
945
  const { rowHeight, samples } = model;
885
- const svgFontSize = Math.min(rowHeight, 12);
886
- const canDisplayLabel = rowHeight > 11;
946
+ const svgFontSize = Math.min(Math.max(rowHeight, 10), 14);
947
+ const canDisplayLabel = rowHeight >= 10;
887
948
  const minWidth = 20;
888
949
  const labelWidth = Math.max(...(samples
889
950
  .map(s => util.measureText(s.label, svgFontSize))
890
951
  .map(width => (canDisplayLabel ? width : minWidth)) || [0]));
891
952
  return (React.createElement(Wrapper, { ...props },
892
- React.createElement(ColorLegend, { model: model, labelWidth: labelWidth })));
953
+ React.createElement(ColorLegend, { model: model, labelWidth: labelWidth, svgFontSize: svgFontSize })));
893
954
  });
894
955
 
895
956
  const LinearMafDisplay = mobxReact.observer(function (props) {
@@ -925,20 +986,6 @@
925
986
  explicitlyTyped: true,
926
987
  });
927
988
 
928
- function getCorrectionFactor(scale) {
929
- if (scale >= 1) {
930
- return 0.6;
931
- }
932
- else if (scale >= 0.2) {
933
- return 0.05;
934
- }
935
- else if (scale >= 0.02) {
936
- return 0.03;
937
- }
938
- else {
939
- return 0.02;
940
- }
941
- }
942
989
  function getContrastBaseMap(theme) {
943
990
  return Object.fromEntries(Object.entries(getColorBaseMap(theme)).map(([key, value]) => [
944
991
  key,
@@ -955,16 +1002,19 @@
955
1002
  };
956
1003
  }
957
1004
  function makeImageData({ ctx, renderArgs, }) {
958
- const { regions, bpPerPx, rowHeight, theme: configTheme, samples, } = renderArgs;
1005
+ const { regions, bpPerPx, rowHeight, showAllLetters, theme: configTheme, samples, rowProportion, } = renderArgs;
959
1006
  const [region] = regions;
960
1007
  const features = renderArgs.features;
961
- const h = rowHeight;
1008
+ const h = rowHeight * rowProportion;
962
1009
  const theme = ui.createJBrowseTheme(configTheme);
963
1010
  const colorForBase = getColorBaseMap(theme);
964
1011
  const contrastForBase = getContrastBaseMap(theme);
965
1012
  const sampleToRowMap = new Map(samples.map((s, i) => [s.id, i]));
966
1013
  const scale = 1 / bpPerPx;
967
- const correctionFactor = getCorrectionFactor(bpPerPx);
1014
+ const f = 0.4;
1015
+ const h2 = rowHeight / 2;
1016
+ const hp2 = h / 2;
1017
+ const offset = (rowHeight - h) / 2;
968
1018
  // sample as alignments
969
1019
  ctx.font = 'bold 10px Courier New,monospace';
970
1020
  for (const feature of features.values()) {
@@ -974,67 +1024,123 @@
974
1024
  for (const [sample, val] of Object.entries(vals)) {
975
1025
  const origAlignment = val.data;
976
1026
  const alignment = origAlignment.toLowerCase();
977
- // gaps
978
- ctx.beginPath();
979
- ctx.fillStyle = 'black';
980
- const offset0 = (5 / 12) * h;
981
- const h6 = h / 6;
982
1027
  const row = sampleToRowMap.get(sample);
983
1028
  if (row === undefined) {
984
1029
  throw new Error(`unknown sample encountered: ${sample}`);
985
1030
  }
986
- const t = h * row;
987
- for (let i = 0; i < alignment.length; i++) {
988
- const l = leftPx + scale * i;
989
- if (alignment[i] === '-') {
990
- ctx.rect(l, offset0 + t, scale + correctionFactor, h6);
1031
+ const t = rowHeight * row;
1032
+ // gaps
1033
+ ctx.beginPath();
1034
+ ctx.fillStyle = 'black';
1035
+ for (let i = 0, o = 0; i < alignment.length; i++) {
1036
+ if (seq[i] !== '-') {
1037
+ if (alignment[i] === '-') {
1038
+ const l = leftPx + scale * o;
1039
+ ctx.moveTo(l, t + h2);
1040
+ ctx.lineTo(l + scale + f, t + h2);
1041
+ }
1042
+ o++;
991
1043
  }
992
1044
  }
993
- ctx.fill();
994
- const offset = (1 / 4) * h;
995
- const h2 = h / 2;
996
- // matches
997
- ctx.beginPath();
998
- ctx.fillStyle = 'lightgrey';
999
- for (let i = 0; i < alignment.length; i++) {
1000
- const c = alignment[i];
1001
- const l = leftPx + scale * i;
1002
- if (seq[i] === c && c !== '-') {
1003
- ctx.rect(l, offset + t, scale + correctionFactor, h2);
1045
+ ctx.stroke();
1046
+ if (!showAllLetters) {
1047
+ // matches
1048
+ ctx.beginPath();
1049
+ ctx.fillStyle = 'lightgrey';
1050
+ for (let i = 0, o = 0; i < alignment.length; i++) {
1051
+ if (seq[i] !== '-') {
1052
+ const c = alignment[i];
1053
+ const l = leftPx + scale * o;
1054
+ if (seq[i] === c && c !== '-') {
1055
+ ctx.rect(l, offset + t, scale + f, h);
1056
+ }
1057
+ o++;
1058
+ }
1004
1059
  }
1060
+ ctx.fill();
1005
1061
  }
1006
- ctx.fill();
1007
1062
  // mismatches
1008
- for (let i = 0; i < alignment.length; i++) {
1063
+ for (let i = 0, o = 0; i < alignment.length; i++) {
1009
1064
  const c = alignment[i];
1010
- if (seq[i] !== c && c !== '-') {
1011
- const l = leftPx + scale * i;
1012
- ctx.fillStyle =
1013
- colorForBase[c] ?? 'purple';
1014
- ctx.fillRect(l, offset + t, scale + correctionFactor, h2);
1065
+ if (seq[i] !== '-') {
1066
+ if ((showAllLetters || seq[i] !== c) && c !== '-') {
1067
+ const l = leftPx + scale * o;
1068
+ ctx.fillStyle =
1069
+ colorForBase[c] ?? 'black';
1070
+ ctx.fillRect(l, offset + t, scale + f, h);
1071
+ }
1072
+ o++;
1015
1073
  }
1016
1074
  }
1017
1075
  // font
1018
1076
  const charSize = { w: 10 };
1019
1077
  if (scale >= charSize.w) {
1020
- for (let i = 0; i < alignment.length; i++) {
1021
- const l = leftPx + scale * i;
1022
- const offset = (scale - charSize.w) / 2 + 1;
1023
- const c = alignment[i];
1024
- if (seq[i] !== c && c !== '-') {
1025
- ctx.fillStyle = contrastForBase[c] ?? 'black';
1026
- ctx.fillText(origAlignment[i], l + offset, h2 + t + 3);
1078
+ for (let i = 0, o = 0; i < alignment.length; i++) {
1079
+ if (seq[i] !== '-') {
1080
+ const l = leftPx + scale * o;
1081
+ const offset = (scale - charSize.w) / 2 + 1;
1082
+ const c = alignment[i];
1083
+ if ((showAllLetters || seq[i] !== c) && c !== '-') {
1084
+ ctx.fillStyle = contrastForBase[c] ?? 'white';
1085
+ ctx.fillText(origAlignment[i], l + offset, hp2 + t + 3);
1086
+ }
1087
+ o++;
1027
1088
  }
1028
1089
  }
1029
1090
  }
1030
1091
  }
1031
1092
  }
1093
+ // second pass for insertions, has slightly improved look since the
1094
+ // insertions are always 'on top' of the other features
1095
+ for (const feature of features.values()) {
1096
+ const [leftPx] = util.featureSpanPx(feature, region, bpPerPx);
1097
+ const vals = feature.get('alignments');
1098
+ const seq = feature.get('seq').toLowerCase();
1099
+ for (const [sample, val] of Object.entries(vals)) {
1100
+ const origAlignment = val.data;
1101
+ const alignment = origAlignment.toLowerCase();
1102
+ const row = sampleToRowMap.get(sample);
1103
+ if (row === undefined) {
1104
+ throw new Error(`unknown sample encountered: ${sample}`);
1105
+ }
1106
+ const t = rowHeight * row;
1107
+ ctx.beginPath();
1108
+ ctx.fillStyle = 'purple';
1109
+ for (let i = 0, o = 0; i < alignment.length; i++) {
1110
+ let ins = '';
1111
+ while (seq[i] === '-') {
1112
+ if (alignment[i] !== '-') {
1113
+ ins += alignment[i];
1114
+ }
1115
+ i++;
1116
+ }
1117
+ if (ins.length > 0) {
1118
+ const l = leftPx + scale * o - 1;
1119
+ ctx.rect(l, offset + t + 1, 1, h - 1);
1120
+ ctx.rect(l - 2, offset + t, 5, 1);
1121
+ ctx.rect(l - 2, offset + t + h - 1, 5, 1);
1122
+ }
1123
+ o++;
1124
+ }
1125
+ ctx.fill();
1126
+ }
1127
+ }
1032
1128
  }
1033
1129
  class LinearMafRenderer extends pluggableElementTypes.FeatureRendererType {
1130
+ getExpandedRegion(region) {
1131
+ const { start, end } = region;
1132
+ const bpExpansion = 1;
1133
+ return {
1134
+ // xref https://github.com/mobxjs/mobx-state-tree/issues/1524 for Omit
1135
+ ...region,
1136
+ start: Math.floor(Math.max(start - bpExpansion, 0)),
1137
+ end: Math.ceil(end + bpExpansion),
1138
+ };
1139
+ }
1034
1140
  async render(renderProps) {
1035
1141
  const { regions, bpPerPx, samples, rowHeight } = renderProps;
1036
1142
  const [region] = regions;
1037
- const height = samples.length * rowHeight;
1143
+ const height = samples.length * rowHeight + 100;
1038
1144
  const width = (region.end - region.start) / bpPerPx;
1039
1145
  const features = await this.getFeatures(renderProps);
1040
1146
  const res = await util.renderToAbstractCanvas(width, height, renderProps, ctx => makeImageData({
@@ -1133,6 +1239,10 @@
1133
1239
  const { adapter } = await this.setup();
1134
1240
  return adapter.getRefNames();
1135
1241
  }
1242
+ async getHeader() {
1243
+ const { adapter } = await this.setup();
1244
+ return adapter.getHeader();
1245
+ }
1136
1246
  getFeatures(query) {
1137
1247
  return rxjs.ObservableCreate(async (observer) => {
1138
1248
  const { adapter } = await this.setup();
@@ -1140,31 +1250,17 @@
1140
1250
  for (const feature of features) {
1141
1251
  const data = feature.get('field5').split(',');
1142
1252
  const alignments = {};
1143
- const main = data[0];
1144
- const aln = main.split(':')[5];
1145
1253
  const alns = data.map(elt => elt.split(':')[5]);
1146
- const alns2 = data.map(() => '');
1147
- // remove extraneous data in other alignments
1148
- // reason being: cannot represent missing data in main species that are in others)
1149
- for (let i = 0, o = 0; i < aln.length; i++, o++) {
1150
- if (aln[i] !== '-') {
1151
- for (let j = 0; j < data.length; j++) {
1152
- alns2[j] += alns[j][i];
1153
- }
1154
- }
1155
- }
1156
- for (let j = 0; j < data.length; j++) {
1157
- const elt = data[j];
1254
+ for (const [j, elt] of data.entries()) {
1158
1255
  const ad = elt.split(':');
1159
- const org = ad[0].split('.')[0];
1160
- const chr = ad[0].split('.')[1];
1256
+ const [org, chr] = ad[0].split('.');
1161
1257
  alignments[org] = {
1162
- chr: chr,
1258
+ chr,
1163
1259
  start: +ad[1],
1164
1260
  srcSize: +ad[2],
1165
1261
  strand: ad[3] === '-' ? -1 : 1,
1166
1262
  unknown: +ad[4],
1167
- data: alns2[j],
1263
+ data: alns[j],
1168
1264
  };
1169
1265
  }
1170
1266
  observer.next(new util.SimpleFeature({
@@ -1175,8 +1271,8 @@
1175
1271
  refName: feature.get('refName'),
1176
1272
  name: feature.get('name'),
1177
1273
  score: feature.get('score'),
1178
- alignments: alignments,
1179
- seq: alns2[0],
1274
+ alignments,
1275
+ seq: alns[0],
1180
1276
  },
1181
1277
  }));
1182
1278
  }
@@ -1216,6 +1312,7 @@
1216
1312
  const [error, setError] = React.useState();
1217
1313
  const [trackName, setTrackName] = React.useState('MAF track');
1218
1314
  const [choice, setChoice] = React.useState('BigMafAdapter');
1315
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1219
1316
  const rootModel = mobxStateTree.getRoot(model);
1220
1317
  return (React.createElement(material.Paper, { className: classes.paper },
1221
1318
  React.createElement(material.Paper, null,
@@ -1299,11 +1396,17 @@
1299
1396
  }
1300
1397
 
1301
1398
  async function renderSvg(self, opts, superRenderSvg) {
1302
- const { offsetPx } = util.getContainingView(self);
1399
+ const { height, id } = self;
1400
+ const { offsetPx, width } = util.getContainingView(self);
1401
+ const clipid = `mafclip-${id}`;
1303
1402
  return (React.createElement(React.Fragment, null,
1304
- React.createElement("g", { id: "snpcov" }, await superRenderSvg(opts)),
1305
- React.createElement("g", { transform: `translate(${Math.max(-offsetPx, 0)})` },
1306
- React.createElement(YScaleBars, { model: self, orientation: "left", exportSVG: true }))));
1403
+ React.createElement("defs", null,
1404
+ React.createElement("clipPath", { id: clipid },
1405
+ React.createElement("rect", { x: 0, y: 0, width: width, height: height }))),
1406
+ React.createElement("g", { clipPath: `url(#${clipid})` },
1407
+ React.createElement("g", { id: "snpcov" }, await superRenderSvg(opts)),
1408
+ React.createElement("g", { transform: `translate(${Math.max(-offsetPx, 0)})` },
1409
+ React.createElement(YScaleBars, { model: self, orientation: "left", exportSVG: true })))));
1307
1410
  }
1308
1411
 
1309
1412
  var renderSvg$1 = {