react-msaview 3.1.0 → 3.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 (35) hide show
  1. package/bundle/index.js +23 -23
  2. package/dist/calculateBlocks.d.ts +12 -0
  3. package/dist/calculateBlocks.js +23 -0
  4. package/dist/calculateBlocks.js.map +1 -0
  5. package/dist/components/ExportSVGDialog.d.ts +6 -0
  6. package/dist/components/ExportSVGDialog.js +26 -0
  7. package/dist/components/ExportSVGDialog.js.map +1 -0
  8. package/dist/components/MSAPanel/MSABlock.js +2 -3
  9. package/dist/components/MSAPanel/MSABlock.js.map +1 -1
  10. package/dist/components/MSAPanel/renderMSABlock.d.ts +1 -1
  11. package/dist/components/MSAPanel/renderMSABlock.js +1 -1
  12. package/dist/components/MSAPanel/renderMSABlock.js.map +1 -1
  13. package/dist/components/MinimapSVG.d.ts +6 -0
  14. package/dist/components/MinimapSVG.js +25 -0
  15. package/dist/components/MinimapSVG.js.map +1 -0
  16. package/dist/components/ZoomControls.js +9 -7
  17. package/dist/components/ZoomControls.js.map +1 -1
  18. package/dist/model.d.ts +53 -42
  19. package/dist/model.js +71 -71
  20. package/dist/model.js.map +1 -1
  21. package/dist/renderToSvg.d.ts +1 -0
  22. package/dist/renderToSvg.js +25 -12
  23. package/dist/renderToSvg.js.map +1 -1
  24. package/dist/version.d.ts +1 -1
  25. package/dist/version.js +1 -1
  26. package/package.json +1 -1
  27. package/src/calculateBlocks.ts +58 -0
  28. package/src/components/ExportSVGDialog.tsx +61 -0
  29. package/src/components/MSAPanel/MSABlock.tsx +2 -3
  30. package/src/components/MSAPanel/renderMSABlock.ts +1 -1
  31. package/src/components/MinimapSVG.tsx +57 -0
  32. package/src/components/ZoomControls.tsx +15 -6
  33. package/src/model.ts +76 -90
  34. package/src/renderToSvg.tsx +50 -19
  35. package/src/version.ts +1 -1
package/src/model.ts CHANGED
@@ -36,6 +36,7 @@ import { StructureModel } from './StructureModel'
36
36
  import { DialogQueueSessionMixin } from './DialogQueue'
37
37
  import { renderToSvg } from './renderToSvg'
38
38
  import { Theme } from '@mui/material'
39
+ import { blocksX, blocksY } from './calculateBlocks'
39
40
 
40
41
  export interface RowDetails {
41
42
  [key: string]: unknown
@@ -594,85 +595,7 @@ const model = types
594
595
  },
595
596
  }))
596
597
 
597
- .views(self => {
598
- let oldBlocksX: number[] = []
599
- let oldBlocksY: number[] = []
600
- let oldValX = 0
601
- let oldValY = 0
602
- return {
603
- /**
604
- * #getter
605
- */
606
- get initialized() {
607
- return (
608
- (self.data.msa ||
609
- self.data.tree ||
610
- self.msaFilehandle ||
611
- self.treeFilehandle) &&
612
- !self.error
613
- )
614
- },
615
- /**
616
- * #getter
617
- */
618
- get blocksX() {
619
- const { scrollX, blockSize: size, colWidth } = self
620
- const ret = -(size * Math.floor(scrollX / size)) - size
621
-
622
- const b = []
623
- for (let i = ret; i < ret + size * 3; i += size) {
624
- if (i + size > 0) {
625
- b.push(i)
626
- }
627
- }
628
- if (
629
- JSON.stringify(b) !== JSON.stringify(oldBlocksX) ||
630
- colWidth !== oldValX
631
- ) {
632
- oldBlocksX = b
633
- oldValX = colWidth
634
- }
635
- return oldBlocksX
636
- },
637
- /**
638
- * #getter
639
- */
640
- get blocksY() {
641
- const { scrollY, blockSize: size, rowHeight } = self
642
- const ret = -(size * Math.floor(scrollY / size)) - 2 * size
643
-
644
- const b = []
645
- for (let i = ret; i < ret + size * 3; i += size) {
646
- if (i + size > 0) {
647
- b.push(i)
648
- }
649
- }
650
- if (
651
- JSON.stringify(b) !== JSON.stringify(oldBlocksY) ||
652
- rowHeight !== oldValY
653
- ) {
654
- oldBlocksY = b
655
- oldValY = rowHeight
656
- }
657
- return oldBlocksY
658
- },
659
- }
660
- })
661
598
  .views(self => ({
662
- /**
663
- * #getter
664
- */
665
- get blocks2d() {
666
- return self.blocksY.flatMap(by => self.blocksX.map(bx => [bx, by]))
667
- },
668
-
669
- /**
670
- * #getter
671
- */
672
- get done() {
673
- return self.initialized && (self.data.msa || self.data.tree)
674
- },
675
-
676
599
  /**
677
600
  * #getter
678
601
  */
@@ -933,6 +856,78 @@ const model = types
933
856
  return this.root.leaves().length * self.rowHeight
934
857
  },
935
858
  }))
859
+ .views(self => ({
860
+ /**
861
+ * #getter
862
+ */
863
+ get totalWidth() {
864
+ return self.numColumns * self.colWidth
865
+ },
866
+ }))
867
+
868
+ .views(self => ({
869
+ /**
870
+ * #getter
871
+ */
872
+ get initialized() {
873
+ return (
874
+ (self.data.msa ||
875
+ self.data.tree ||
876
+ self.msaFilehandle ||
877
+ self.treeFilehandle) &&
878
+ !self.error
879
+ )
880
+ },
881
+ /**
882
+ * #getter
883
+ */
884
+ get blocksX() {
885
+ return blocksX({
886
+ viewportWidth: self.msaAreaWidth,
887
+ viewportX: -self.scrollX,
888
+ blockSize: self.blockSize,
889
+ mapWidth: self.totalWidth,
890
+ })
891
+ },
892
+ /**
893
+ * #getter
894
+ */
895
+ get blocksY() {
896
+ return blocksY({
897
+ viewportHeight: self.height,
898
+ viewportY: -self.scrollY,
899
+ blockSize: self.blockSize,
900
+ mapHeight: self.totalHeight,
901
+ })
902
+ },
903
+ }))
904
+ .views(self => ({
905
+ /**
906
+ * #getter
907
+ */
908
+ get blocks2d() {
909
+ const ret = []
910
+ for (const by of self.blocksY) {
911
+ for (const bx of self.blocksX) {
912
+ ret.push([bx, by])
913
+ }
914
+ }
915
+ return ret
916
+ },
917
+
918
+ /**
919
+ * #getter
920
+ */
921
+ get done() {
922
+ return self.initialized && (self.data.msa || self.data.tree)
923
+ },
924
+ /**
925
+ * #getter
926
+ */
927
+ get maxScrollX() {
928
+ return -self.totalWidth + (self.msaAreaWidth - 100)
929
+ },
930
+ }))
936
931
  .actions(self => ({
937
932
  /**
938
933
  * #action
@@ -962,23 +957,14 @@ const model = types
962
957
  * #action
963
958
  */
964
959
  doScrollX(deltaX: number) {
965
- self.scrollX = clamp(
966
- -(self.numColumns * self.colWidth) + (self.msaAreaWidth - 100),
967
- self.scrollX + deltaX,
968
- 0,
969
- )
960
+ self.scrollX = clamp(self.maxScrollX, self.scrollX + deltaX, 0)
970
961
  },
971
962
 
972
963
  /**
973
964
  * #action
974
965
  */
975
966
  setScrollX(n: number) {
976
- console.log({ n })
977
- self.scrollX = clamp(
978
- -(self.numColumns * self.colWidth) + (self.msaAreaWidth - 100),
979
- n,
980
- 0,
981
- )
967
+ self.scrollX = clamp(self.maxScrollX, n, 0)
982
968
  },
983
969
 
984
970
  /**
@@ -1234,7 +1220,7 @@ const model = types
1234
1220
  /**
1235
1221
  * #action
1236
1222
  */
1237
- async exportSVG(opts: { theme: Theme }) {
1223
+ async exportSVG(opts: { theme: Theme; includeMinimap?: boolean }) {
1238
1224
  const html = await renderToSvg(self as MsaViewModel, opts)
1239
1225
  const blob = new Blob([html], { type: 'image/svg+xml' })
1240
1226
  saveAs(blob, 'image.svg')
@@ -7,15 +7,19 @@ import { Theme } from '@mui/material'
7
7
  // locals
8
8
  import { MsaViewModel } from './model'
9
9
  import { renderTreeCanvas } from './components/TreePanel/renderTreeCanvas'
10
- import { renderBlock } from './components/MSAPanel/renderMSABlock'
10
+ import { renderMSABlock } from './components/MSAPanel/renderMSABlock'
11
11
  import { colorContrast } from './util'
12
+ import MinimapSVG from './components/MinimapSVG'
12
13
 
13
14
  // render LGV to SVG
14
- export async function renderToSvg(model: MsaViewModel, opts: { theme: Theme }) {
15
+ export async function renderToSvg(
16
+ model: MsaViewModel,
17
+ opts: { theme: Theme; includeMinimap?: boolean },
18
+ ) {
15
19
  await when(() => !!model.initialized)
16
20
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
21
  const { width, height, scrollX, scrollY, colorScheme, treeAreaWidth } = model
18
- const { theme } = opts
22
+ const { theme, includeMinimap } = opts
19
23
 
20
24
  const { Context } = await import('svgcanvas')
21
25
  const ctx1 = Context(width, height)
@@ -29,7 +33,7 @@ export async function renderToSvg(model: MsaViewModel, opts: { theme: Theme }) {
29
33
  blockSizeYOverride: height,
30
34
  highResScaleFactorOverride: 1,
31
35
  })
32
- renderBlock({
36
+ renderMSABlock({
33
37
  model,
34
38
  offsetY: scrollY,
35
39
  offsetX: -scrollX,
@@ -40,6 +44,7 @@ export async function renderToSvg(model: MsaViewModel, opts: { theme: Theme }) {
40
44
  highResScaleFactorOverride: 1,
41
45
  })
42
46
 
47
+ const Wrapper = includeMinimap ? MinimapWrapper : NullWrapper
43
48
  const clipId = 'tree'
44
49
  // the xlink namespace is used for rendering <image> tag
45
50
  return renderToStaticMarkup(
@@ -50,22 +55,48 @@ export async function renderToSvg(model: MsaViewModel, opts: { theme: Theme }) {
50
55
  xmlnsXlink="http://www.w3.org/1999/xlink"
51
56
  viewBox={[0, 0, width, height].toString()}
52
57
  >
53
- <defs>
54
- <clipPath id={clipId}>
55
- <rect x={0} y={0} width={treeAreaWidth} height={height} />
56
- </clipPath>
57
- </defs>
58
- <g
59
- clipPath={`url(#${clipId})`}
60
- /* eslint-disable-next-line react/no-danger */
61
- dangerouslySetInnerHTML={{ __html: ctx1.getSvg().innerHTML }}
62
- />
63
- <g
64
- transform={`translate(${treeAreaWidth} 0)`}
65
- /* eslint-disable-next-line react/no-danger */
66
- dangerouslySetInnerHTML={{ __html: ctx2.getSvg().innerHTML }}
67
- />
58
+ <Wrapper model={model}>
59
+ <defs>
60
+ <clipPath id={clipId}>
61
+ <rect x={0} y={0} width={treeAreaWidth} height={height} />
62
+ </clipPath>
63
+ </defs>
64
+ <g
65
+ clipPath={`url(#${clipId})`}
66
+ /* eslint-disable-next-line react/no-danger */
67
+ dangerouslySetInnerHTML={{ __html: ctx1.getSvg().innerHTML }}
68
+ />
69
+ <g
70
+ transform={`translate(${treeAreaWidth} 0)`}
71
+ /* eslint-disable-next-line react/no-danger */
72
+ dangerouslySetInnerHTML={{ __html: ctx2.getSvg().innerHTML }}
73
+ />
74
+ </Wrapper>
68
75
  </svg>,
69
76
  createRoot,
70
77
  )
71
78
  }
79
+
80
+ function MinimapWrapper({
81
+ model,
82
+ children,
83
+ }: {
84
+ model: MsaViewModel
85
+ children: React.ReactNode
86
+ }) {
87
+ const { minimapHeight, treeAreaWidth } = model
88
+
89
+ return (
90
+ <>
91
+ <g transform={`translate(${treeAreaWidth} 0)`}>
92
+ <MinimapSVG model={model} />
93
+ </g>
94
+
95
+ <g transform={`translate(0 ${minimapHeight})`}>{children}</g>
96
+ </>
97
+ )
98
+ }
99
+
100
+ function NullWrapper({ children }: { children: React.ReactNode }) {
101
+ return <>{children}</>
102
+ }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '3.1.0'
1
+ export const version = '3.1.1'