@jbrowse/plugin-linear-genome-view 2.3.4 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/dist/BaseLinearDisplay/components/Block.js +1 -1
  2. package/dist/BaseLinearDisplay/components/Block.js.map +1 -1
  3. package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +11 -11
  4. package/dist/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -1
  5. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +74 -4
  6. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js +41 -22
  7. package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -1
  8. package/dist/BaseLinearDisplay/models/configSchema.js +8 -0
  9. package/dist/BaseLinearDisplay/models/configSchema.js.map +1 -1
  10. package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +1 -1
  11. package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.js +5 -0
  12. package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.js.map +1 -1
  13. package/dist/LinearBareDisplay/model.d.ts +62 -4
  14. package/dist/LinearBasicDisplay/model.d.ts +72 -14
  15. package/dist/LinearGenomeView/components/CenterLine.js +2 -2
  16. package/dist/LinearGenomeView/components/CenterLine.js.map +1 -1
  17. package/dist/LinearGenomeView/components/Cytobands.d.ts +118 -0
  18. package/dist/LinearGenomeView/components/Cytobands.js +92 -0
  19. package/dist/LinearGenomeView/components/Cytobands.js.map +1 -0
  20. package/dist/LinearGenomeView/components/ExportSvgDialog.d.ts +1 -1
  21. package/dist/LinearGenomeView/components/ExportSvgDialog.js +24 -3
  22. package/dist/LinearGenomeView/components/ExportSvgDialog.js.map +1 -1
  23. package/dist/LinearGenomeView/components/Gridlines.js +1 -1
  24. package/dist/LinearGenomeView/components/Gridlines.js.map +1 -1
  25. package/dist/LinearGenomeView/components/Header.js +3 -4
  26. package/dist/LinearGenomeView/components/Header.js.map +1 -1
  27. package/dist/LinearGenomeView/components/LinearGenomeView.js +1 -1
  28. package/dist/LinearGenomeView/components/MiniControls.js +5 -4
  29. package/dist/LinearGenomeView/components/MiniControls.js.map +1 -1
  30. package/dist/LinearGenomeView/components/OverviewScalebar.d.ts +1 -115
  31. package/dist/LinearGenomeView/components/OverviewScalebar.js +15 -99
  32. package/dist/LinearGenomeView/components/OverviewScalebar.js.map +1 -1
  33. package/dist/LinearGenomeView/components/RubberbandSpan.js +2 -2
  34. package/dist/LinearGenomeView/components/RubberbandSpan.js.map +1 -1
  35. package/dist/LinearGenomeView/components/TrackLabel.js +4 -5
  36. package/dist/LinearGenomeView/components/TrackLabel.js.map +1 -1
  37. package/dist/LinearGenomeView/components/ZoomControls.js +5 -4
  38. package/dist/LinearGenomeView/components/ZoomControls.js.map +1 -1
  39. package/dist/LinearGenomeView/components/util.d.ts +6 -0
  40. package/dist/LinearGenomeView/components/util.js +11 -1
  41. package/dist/LinearGenomeView/components/util.js.map +1 -1
  42. package/dist/LinearGenomeView/model.d.ts +10 -2
  43. package/dist/LinearGenomeView/model.js +4 -4
  44. package/dist/LinearGenomeView/model.js.map +1 -1
  45. package/dist/LinearGenomeView/svgcomponents/SVGBackground.d.ts +6 -0
  46. package/dist/LinearGenomeView/svgcomponents/SVGBackground.js +13 -0
  47. package/dist/LinearGenomeView/svgcomponents/SVGBackground.js.map +1 -0
  48. package/dist/LinearGenomeView/svgcomponents/SVGHeader.d.ts +10 -0
  49. package/dist/LinearGenomeView/svgcomponents/SVGHeader.js +55 -0
  50. package/dist/LinearGenomeView/svgcomponents/SVGHeader.js.map +1 -0
  51. package/dist/LinearGenomeView/svgcomponents/SVGLinearGenomeView.d.ts +13 -0
  52. package/dist/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +56 -0
  53. package/dist/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js.map +1 -0
  54. package/dist/LinearGenomeView/svgcomponents/SVGRegionSeparators.d.ts +8 -0
  55. package/dist/LinearGenomeView/svgcomponents/SVGRegionSeparators.js +13 -0
  56. package/dist/LinearGenomeView/svgcomponents/SVGRegionSeparators.js.map +1 -0
  57. package/dist/LinearGenomeView/svgcomponents/SVGRuler.d.ts +8 -0
  58. package/dist/LinearGenomeView/svgcomponents/SVGRuler.js +51 -0
  59. package/dist/LinearGenomeView/svgcomponents/SVGRuler.js.map +1 -0
  60. package/dist/LinearGenomeView/svgcomponents/SVGScalebar.d.ts +8 -0
  61. package/dist/LinearGenomeView/svgcomponents/SVGScalebar.js +22 -0
  62. package/dist/LinearGenomeView/svgcomponents/SVGScalebar.js.map +1 -0
  63. package/dist/LinearGenomeView/svgcomponents/SVGTrackLabel.d.ts +8 -0
  64. package/dist/LinearGenomeView/svgcomponents/SVGTrackLabel.js +15 -0
  65. package/dist/LinearGenomeView/svgcomponents/SVGTrackLabel.js.map +1 -0
  66. package/dist/LinearGenomeView/svgcomponents/SVGTracks.d.ts +23 -0
  67. package/dist/LinearGenomeView/svgcomponents/SVGTracks.js +30 -0
  68. package/dist/LinearGenomeView/svgcomponents/SVGTracks.js.map +1 -0
  69. package/dist/LinearGenomeView/util.js +1 -1
  70. package/dist/LinearGenomeView/util.js.map +1 -1
  71. package/dist/index.d.ts +133 -18
  72. package/dist/index.js +6 -2
  73. package/dist/index.js.map +1 -1
  74. package/esm/BaseLinearDisplay/components/Block.js +1 -1
  75. package/esm/BaseLinearDisplay/components/Block.js.map +1 -1
  76. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js +11 -11
  77. package/esm/BaseLinearDisplay/components/ServerSideRenderedBlockContent.js.map +1 -1
  78. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +74 -4
  79. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js +42 -23
  80. package/esm/BaseLinearDisplay/models/BaseLinearDisplayModel.js.map +1 -1
  81. package/esm/BaseLinearDisplay/models/configSchema.js +8 -0
  82. package/esm/BaseLinearDisplay/models/configSchema.js.map +1 -1
  83. package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +1 -1
  84. package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.js +5 -0
  85. package/esm/BaseLinearDisplay/models/serverSideRenderedBlock.js.map +1 -1
  86. package/esm/LinearBareDisplay/model.d.ts +62 -4
  87. package/esm/LinearBasicDisplay/model.d.ts +72 -14
  88. package/esm/LinearGenomeView/components/CenterLine.js +2 -2
  89. package/esm/LinearGenomeView/components/CenterLine.js.map +1 -1
  90. package/esm/LinearGenomeView/components/Cytobands.d.ts +118 -0
  91. package/esm/LinearGenomeView/components/Cytobands.js +87 -0
  92. package/esm/LinearGenomeView/components/Cytobands.js.map +1 -0
  93. package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +1 -1
  94. package/esm/LinearGenomeView/components/ExportSvgDialog.js +25 -4
  95. package/esm/LinearGenomeView/components/ExportSvgDialog.js.map +1 -1
  96. package/esm/LinearGenomeView/components/Gridlines.js +1 -1
  97. package/esm/LinearGenomeView/components/Gridlines.js.map +1 -1
  98. package/esm/LinearGenomeView/components/Header.js +5 -6
  99. package/esm/LinearGenomeView/components/Header.js.map +1 -1
  100. package/esm/LinearGenomeView/components/LinearGenomeView.js +1 -1
  101. package/esm/LinearGenomeView/components/MiniControls.js +5 -4
  102. package/esm/LinearGenomeView/components/MiniControls.js.map +1 -1
  103. package/esm/LinearGenomeView/components/OverviewScalebar.d.ts +1 -115
  104. package/esm/LinearGenomeView/components/OverviewScalebar.js +12 -96
  105. package/esm/LinearGenomeView/components/OverviewScalebar.js.map +1 -1
  106. package/esm/LinearGenomeView/components/RubberbandSpan.js +2 -2
  107. package/esm/LinearGenomeView/components/RubberbandSpan.js.map +1 -1
  108. package/esm/LinearGenomeView/components/TrackLabel.js +4 -5
  109. package/esm/LinearGenomeView/components/TrackLabel.js.map +1 -1
  110. package/esm/LinearGenomeView/components/ZoomControls.js +5 -4
  111. package/esm/LinearGenomeView/components/ZoomControls.js.map +1 -1
  112. package/esm/LinearGenomeView/components/util.d.ts +6 -0
  113. package/esm/LinearGenomeView/components/util.js +9 -0
  114. package/esm/LinearGenomeView/components/util.js.map +1 -1
  115. package/esm/LinearGenomeView/model.d.ts +10 -2
  116. package/esm/LinearGenomeView/model.js +2 -2
  117. package/esm/LinearGenomeView/model.js.map +1 -1
  118. package/esm/LinearGenomeView/svgcomponents/SVGBackground.d.ts +6 -0
  119. package/esm/LinearGenomeView/svgcomponents/SVGBackground.js +7 -0
  120. package/esm/LinearGenomeView/svgcomponents/SVGBackground.js.map +1 -0
  121. package/esm/LinearGenomeView/svgcomponents/SVGHeader.d.ts +10 -0
  122. package/esm/LinearGenomeView/svgcomponents/SVGHeader.js +49 -0
  123. package/esm/LinearGenomeView/svgcomponents/SVGHeader.js.map +1 -0
  124. package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.d.ts +13 -0
  125. package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +47 -0
  126. package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js.map +1 -0
  127. package/esm/LinearGenomeView/svgcomponents/SVGRegionSeparators.d.ts +8 -0
  128. package/esm/LinearGenomeView/svgcomponents/SVGRegionSeparators.js +7 -0
  129. package/esm/LinearGenomeView/svgcomponents/SVGRegionSeparators.js.map +1 -0
  130. package/esm/LinearGenomeView/svgcomponents/SVGRuler.d.ts +8 -0
  131. package/esm/LinearGenomeView/svgcomponents/SVGRuler.js +45 -0
  132. package/esm/LinearGenomeView/svgcomponents/SVGRuler.js.map +1 -0
  133. package/esm/LinearGenomeView/svgcomponents/SVGScalebar.d.ts +8 -0
  134. package/esm/LinearGenomeView/svgcomponents/SVGScalebar.js +16 -0
  135. package/esm/LinearGenomeView/svgcomponents/SVGScalebar.js.map +1 -0
  136. package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.d.ts +8 -0
  137. package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.js +9 -0
  138. package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.js.map +1 -0
  139. package/esm/LinearGenomeView/svgcomponents/SVGTracks.d.ts +23 -0
  140. package/esm/LinearGenomeView/svgcomponents/SVGTracks.js +24 -0
  141. package/esm/LinearGenomeView/svgcomponents/SVGTracks.js.map +1 -0
  142. package/esm/LinearGenomeView/util.js +1 -1
  143. package/esm/LinearGenomeView/util.js.map +1 -1
  144. package/esm/index.d.ts +133 -18
  145. package/esm/index.js +3 -2
  146. package/esm/index.js.map +1 -1
  147. package/package.json +3 -3
  148. package/src/BaseLinearDisplay/components/Block.tsx +1 -1
  149. package/src/BaseLinearDisplay/components/ServerSideRenderedBlockContent.tsx +11 -12
  150. package/src/BaseLinearDisplay/models/BaseLinearDisplayModel.tsx +53 -35
  151. package/src/BaseLinearDisplay/models/configSchema.ts +8 -0
  152. package/src/BaseLinearDisplay/models/serverSideRenderedBlock.ts +9 -17
  153. package/src/LinearGenomeView/components/CenterLine.tsx +2 -2
  154. package/src/LinearGenomeView/components/Cytobands.tsx +154 -0
  155. package/src/LinearGenomeView/components/ExportSvgDialog.tsx +56 -4
  156. package/src/LinearGenomeView/components/Gridlines.tsx +1 -1
  157. package/src/LinearGenomeView/components/Header.tsx +6 -13
  158. package/src/LinearGenomeView/components/LinearGenomeView.test.tsx +8 -11
  159. package/src/LinearGenomeView/components/LinearGenomeView.tsx +1 -1
  160. package/src/LinearGenomeView/components/MiniControls.tsx +6 -7
  161. package/src/LinearGenomeView/components/OverviewScalebar.tsx +218 -381
  162. package/src/LinearGenomeView/components/RubberbandSpan.tsx +2 -2
  163. package/src/LinearGenomeView/components/TrackLabel.tsx +2 -5
  164. package/src/LinearGenomeView/components/ZoomControls.tsx +3 -4
  165. package/src/LinearGenomeView/components/__snapshots__/LinearGenomeView.test.tsx.snap +1234 -1200
  166. package/src/LinearGenomeView/components/util.ts +13 -0
  167. package/src/LinearGenomeView/index.test.ts +9 -5
  168. package/src/LinearGenomeView/model.ts +10 -3
  169. package/src/LinearGenomeView/svgcomponents/SVGBackground.tsx +21 -0
  170. package/src/LinearGenomeView/svgcomponents/SVGHeader.tsx +93 -0
  171. package/src/LinearGenomeView/svgcomponents/SVGLinearGenomeView.tsx +114 -0
  172. package/src/LinearGenomeView/svgcomponents/SVGRegionSeparators.tsx +31 -0
  173. package/src/LinearGenomeView/svgcomponents/SVGRuler.tsx +125 -0
  174. package/src/LinearGenomeView/svgcomponents/SVGScalebar.tsx +57 -0
  175. package/src/LinearGenomeView/svgcomponents/SVGTrackLabel.tsx +45 -0
  176. package/src/LinearGenomeView/svgcomponents/SVGTracks.tsx +67 -0
  177. package/src/LinearGenomeView/util.test.ts +7 -4
  178. package/src/LinearGenomeView/util.ts +2 -2
  179. package/src/index.ts +10 -1
  180. package/dist/LinearGenomeView/components/LinearGenomeViewSvg.d.ts +0 -4
  181. package/dist/LinearGenomeView/components/LinearGenomeViewSvg.js +0 -141
  182. package/dist/LinearGenomeView/components/LinearGenomeViewSvg.js.map +0 -1
  183. package/dist/LinearGenomeView/components/Ruler.d.ts +0 -11
  184. package/dist/LinearGenomeView/components/Ruler.js +0 -39
  185. package/dist/LinearGenomeView/components/Ruler.js.map +0 -1
  186. package/esm/LinearGenomeView/components/LinearGenomeViewSvg.d.ts +0 -4
  187. package/esm/LinearGenomeView/components/LinearGenomeViewSvg.js +0 -134
  188. package/esm/LinearGenomeView/components/LinearGenomeViewSvg.js.map +0 -1
  189. package/esm/LinearGenomeView/components/Ruler.d.ts +0 -11
  190. package/esm/LinearGenomeView/components/Ruler.js +0 -34
  191. package/esm/LinearGenomeView/components/Ruler.js.map +0 -1
  192. package/src/LinearGenomeView/components/LinearGenomeViewSvg.tsx +0 -307
  193. package/src/LinearGenomeView/components/Ruler.tsx +0 -78
@@ -60,3 +60,16 @@ export function getRelativeX<
60
60
  >(event: T, element: HTMLElement | null) {
61
61
  return event.clientX - (element?.getBoundingClientRect().left || 0)
62
62
  }
63
+
64
+ export function getCytobands(assembly: Assembly | undefined, refName: string) {
65
+ return (
66
+ assembly?.cytobands
67
+ ?.map(f => ({
68
+ refName: assembly.getCanonicalRefName(f.get('refName')),
69
+ start: f.get('start'),
70
+ end: f.get('end'),
71
+ type: f.get('type'),
72
+ }))
73
+ .filter(f => f.refName === refName) || []
74
+ )
75
+ }
@@ -7,13 +7,17 @@ import {
7
7
  } from '@jbrowse/core/pluggableElementTypes/models'
8
8
  import TrackType from '@jbrowse/core/pluggableElementTypes/TrackType'
9
9
  import PluginManager from '@jbrowse/core/PluginManager'
10
- import { Instance, types } from 'mobx-state-tree'
11
- import { LinearGenomeViewStateModel, stateModelFactory } from '.'
10
+ import { types } from 'mobx-state-tree'
11
+
12
+ // locals
13
+ import { LinearGenomeViewModel, stateModelFactory } from '.'
12
14
  import { BaseLinearDisplayComponent } from '..'
13
15
  import { stateModelFactory as LinearBasicDisplayStateModelFactory } from '../LinearBareDisplay'
14
16
  import hg38Regions from './hg38DisplayedRegions.json'
15
17
  import volvoxDisplayedRegions from './volvoxDisplayedRegions.json'
16
18
 
19
+ type LGV = LinearGenomeViewModel
20
+
17
21
  // use initializer function to avoid having console.warn jest.fn in a global
18
22
  function initialize() {
19
23
  console.warn = jest.fn()
@@ -99,7 +103,7 @@ function initialize() {
99
103
  }),
100
104
  })
101
105
  .actions(self => ({
102
- setView(view: Instance<LinearGenomeViewStateModel>) {
106
+ setView(view: LGV) {
103
107
  self.view = view
104
108
  return view
105
109
  },
@@ -306,7 +310,7 @@ test('can navToMultiple', () => {
306
310
 
307
311
  describe('Zoom to selected displayed regions', () => {
308
312
  const { Session, LinearGenomeModel } = initialize()
309
- let model: Instance<ReturnType<typeof stateModelFactory>>
313
+ let model: LGV
310
314
  let largestBpPerPx: number
311
315
  beforeEach(() => {
312
316
  const session = Session.create({
@@ -674,7 +678,7 @@ describe('get sequence for selected displayed regions', () => {
674
678
  /* the start of all the results should be +1
675
679
  the sequence dialog then handles converting from 1-based closed to interbase
676
680
  */
677
- let model: Instance<ReturnType<typeof stateModelFactory>>
681
+ let model: LGV
678
682
  beforeEach(() => {
679
683
  const session = Session.create({
680
684
  configuration: {},
@@ -51,7 +51,7 @@ import ZoomInIcon from '@mui/icons-material/ZoomIn'
51
51
  import MenuOpenIcon from '@mui/icons-material/MenuOpen'
52
52
 
53
53
  // locals
54
- import { renderToSvg } from './components/LinearGenomeViewSvg'
54
+ import { renderToSvg } from './svgcomponents/SVGLinearGenomeView'
55
55
  import RefNameAutocomplete from './components/RefNameAutocomplete'
56
56
  import SearchBox from './components/SearchBox'
57
57
  import ExportSvgDlg from './components/ExportSvgDialog'
@@ -76,12 +76,19 @@ export interface BpOffset {
76
76
  assemblyName?: string
77
77
  oob?: boolean
78
78
  }
79
-
80
79
  export interface ExportSvgOptions {
81
80
  rasterizeLayers?: boolean
82
81
  filename?: string
83
82
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
83
  Wrapper?: React.FC<any>
84
+ fontSize?: number
85
+ rulerHeight?: number
86
+ textHeight?: number
87
+ paddingHeight?: number
88
+ headerHeight?: number
89
+ cytobandHeight?: number
90
+ trackLabels?: string
91
+ themeName?: string
85
92
  }
86
93
 
87
94
  function calculateVisibleLocStrings(contentBlocks: BaseBlock[]) {
@@ -907,7 +914,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
907
914
  },
908
915
 
909
916
  /**
910
- * #action
917
+ * #method
911
918
  * creates an svg export and save using FileSaver
912
919
  */
913
920
  async exportSvg(opts: ExportSvgOptions = {}) {
@@ -0,0 +1,21 @@
1
+ import React from 'react'
2
+ import { useTheme } from '@mui/material'
3
+
4
+ export default function SVGBackground({
5
+ width,
6
+ height,
7
+ shift,
8
+ }: {
9
+ width: number
10
+ height: number
11
+ shift: number
12
+ }) {
13
+ const theme = useTheme()
14
+ return (
15
+ <rect
16
+ width={width + shift * 2}
17
+ height={height}
18
+ fill={theme.palette.background.default}
19
+ />
20
+ )
21
+ }
@@ -0,0 +1,93 @@
1
+ import React from 'react'
2
+ import { getSession } from '@jbrowse/core/util'
3
+ import Base1DView from '@jbrowse/core/util/Base1DViewModel'
4
+ import { useTheme } from '@mui/material'
5
+
6
+ // locals
7
+ import { LinearGenomeViewModel, HEADER_OVERVIEW_HEIGHT } from '..'
8
+ import Cytobands from '../components/Cytobands'
9
+ import { Polygon } from '../components/OverviewScalebar'
10
+ import SVGRuler from './SVGRuler'
11
+ import SVGScalebar from './SVGScalebar'
12
+
13
+ type LGV = LinearGenomeViewModel
14
+
15
+ export default function SVGHeader({
16
+ model,
17
+ fontSize,
18
+ cytobandHeight,
19
+ rulerHeight,
20
+ }: {
21
+ model: LGV
22
+ rulerHeight: number
23
+ fontSize: number
24
+ cytobandHeight: number
25
+ }) {
26
+ const { width, assemblyNames, showCytobands, displayedRegions } = model
27
+ const { assemblyManager } = getSession(model)
28
+ const assemblyName = assemblyNames.length > 1 ? '' : assemblyNames[0]
29
+ const assembly = assemblyManager.get(assemblyName)
30
+ const theme = useTheme()
31
+
32
+ const overview = Base1DView.create({
33
+ displayedRegions: JSON.parse(JSON.stringify(displayedRegions)),
34
+ interRegionPaddingWidth: 0,
35
+ minimumBlockWidth: model.minimumBlockWidth,
36
+ })
37
+ const visibleRegions = model.dynamicBlocks.contentBlocks
38
+
39
+ overview.setVolatileWidth(width)
40
+ overview.showAllRegions()
41
+ const block = overview.dynamicBlocks.contentBlocks[0]
42
+
43
+ const first = visibleRegions[0]
44
+ const firstOverviewPx =
45
+ overview.bpToPx({
46
+ ...first,
47
+ coord: first.reversed ? first.end : first.start,
48
+ }) || 0
49
+
50
+ const last = visibleRegions[visibleRegions.length - 1]
51
+ const lastOverviewPx =
52
+ overview.bpToPx({
53
+ ...last,
54
+ coord: last.reversed ? last.start : last.end,
55
+ }) || 0
56
+ const c = +showCytobands * cytobandHeight
57
+ return (
58
+ <g id="header">
59
+ <text
60
+ x={0}
61
+ y={fontSize}
62
+ fontSize={fontSize}
63
+ fill={theme.palette.text.primary}
64
+ >
65
+ {assemblyName}
66
+ </text>
67
+
68
+ {showCytobands ? (
69
+ <g transform={`translate(0 ${rulerHeight})`}>
70
+ <Cytobands overview={overview} assembly={assembly} block={block} />
71
+ <rect
72
+ stroke="red"
73
+ fill="rgb(255,0,0,0.1)"
74
+ width={Math.max(lastOverviewPx - firstOverviewPx, 0.5)}
75
+ height={HEADER_OVERVIEW_HEIGHT - 1}
76
+ x={firstOverviewPx}
77
+ y={0.5}
78
+ />
79
+ <g transform={`translate(0,${HEADER_OVERVIEW_HEIGHT})`}>
80
+ <Polygon overview={overview} model={model} useOffset={false} />
81
+ </g>
82
+ </g>
83
+ ) : null}
84
+
85
+ <g transform={`translate(0 ${fontSize + c})`}>
86
+ <SVGScalebar model={model} fontSize={fontSize} />
87
+ </g>
88
+ <g transform={`translate(0 ${rulerHeight + c})`}>
89
+ <SVGRuler model={model} fontSize={fontSize} />
90
+ </g>
91
+ </g>
92
+ )
93
+ }
@@ -0,0 +1,114 @@
1
+ import React from 'react'
2
+ import { renderToStaticMarkup } from 'react-dom/server'
3
+ import { when } from 'mobx'
4
+ import { getSession, max, measureText, sum } from '@jbrowse/core/util'
5
+ import { ThemeProvider } from '@mui/material'
6
+ import { createJBrowseTheme } from '@jbrowse/core/ui'
7
+
8
+ // locals
9
+ import { LinearGenomeViewModel, ExportSvgOptions } from '..'
10
+ import SVGBackground from './SVGBackground'
11
+ import SVGTracks from './SVGTracks'
12
+ import SVGHeader from './SVGHeader'
13
+ import SVGRuler from './SVGRuler'
14
+ import { getTrackName } from '@jbrowse/core/util/tracks'
15
+
16
+ type LGV = LinearGenomeViewModel
17
+
18
+ interface Display {
19
+ height: number
20
+ }
21
+ interface Track {
22
+ displays: Display[]
23
+ }
24
+
25
+ export function totalHeight(
26
+ tracks: Track[],
27
+ textHeight: number,
28
+ trackLabels: string,
29
+ ) {
30
+ return sum(
31
+ tracks.map(
32
+ t =>
33
+ t.displays[0].height +
34
+ (['none', 'left'].includes(trackLabels) ? 0 : textHeight),
35
+ ),
36
+ )
37
+ }
38
+
39
+ // render LGV to SVG
40
+ export async function renderToSvg(model: LGV, opts: ExportSvgOptions) {
41
+ await when(() => model.initialized)
42
+ const {
43
+ textHeight = 18,
44
+ headerHeight = 40,
45
+ rulerHeight = 50,
46
+ fontSize = 13,
47
+ cytobandHeight = 100,
48
+ trackLabels = 'offset',
49
+ themeName = 'default',
50
+ Wrapper = ({ children }) => <>{children}</>,
51
+ } = opts
52
+ const session = getSession(model)
53
+ const theme = session.allThemes?.()[themeName]
54
+ const { width, tracks, showCytobands } = model
55
+ const shift = 50
56
+ const c = +showCytobands * cytobandHeight
57
+ const offset = headerHeight + rulerHeight + c + 10
58
+ const height = totalHeight(tracks, textHeight, trackLabels) + offset + 100
59
+ const displayResults = await Promise.all(
60
+ tracks.map(async track => {
61
+ const display = track.displays[0]
62
+ await when(() => (display.ready !== undefined ? display.ready : true))
63
+ return { track, result: await display.renderSvg({ ...opts, theme }) }
64
+ }),
65
+ )
66
+ const trackLabelMaxLen =
67
+ max(
68
+ tracks.map(
69
+ t => measureText(getTrackName(t.configuration, session), fontSize),
70
+ fontSize,
71
+ ),
72
+ 0,
73
+ ) + 40
74
+ const trackLabelOffset = trackLabels === 'left' ? trackLabelMaxLen : 0
75
+ const w = width + trackLabelOffset
76
+
77
+ // the xlink namespace is used for rendering <image> tag
78
+ return renderToStaticMarkup(
79
+ <ThemeProvider theme={createJBrowseTheme(theme)}>
80
+ <Wrapper>
81
+ <svg
82
+ width={w}
83
+ height={height}
84
+ xmlns="http://www.w3.org/2000/svg"
85
+ xmlnsXlink="http://www.w3.org/1999/xlink"
86
+ viewBox={[0, 0, w + shift * 2, height].toString()}
87
+ >
88
+ <SVGBackground width={w} height={height} shift={shift} />
89
+ <g transform={`translate(${shift} 0)`}>
90
+ <g transform={`translate(${trackLabelOffset})`}>
91
+ <SVGHeader
92
+ model={model}
93
+ fontSize={fontSize}
94
+ rulerHeight={rulerHeight}
95
+ cytobandHeight={cytobandHeight}
96
+ />
97
+ </g>
98
+ <SVGTracks
99
+ textHeight={textHeight}
100
+ fontSize={fontSize}
101
+ model={model}
102
+ displayResults={displayResults}
103
+ offset={offset}
104
+ trackLabels={trackLabels}
105
+ trackLabelOffset={trackLabelOffset}
106
+ />
107
+ </g>
108
+ </svg>
109
+ </Wrapper>
110
+ </ThemeProvider>,
111
+ )
112
+ }
113
+
114
+ export { SVGTracks, SVGRuler }
@@ -0,0 +1,31 @@
1
+ import React from 'react'
2
+
3
+ // locals
4
+ import { LinearGenomeViewModel } from '..'
5
+
6
+ type LGV = LinearGenomeViewModel
7
+
8
+ // SVG component, region separator
9
+ export default function SVGRegionSeparators({
10
+ model,
11
+ height,
12
+ }: {
13
+ height: number
14
+ model: LGV
15
+ }) {
16
+ const { dynamicBlocks, offsetPx, interRegionPaddingWidth } = model
17
+ return (
18
+ <>
19
+ {dynamicBlocks.contentBlocks.slice(1).map(block => (
20
+ <rect
21
+ key={block.key}
22
+ x={block.offsetPx - offsetPx - interRegionPaddingWidth}
23
+ width={interRegionPaddingWidth}
24
+ y={0}
25
+ height={height}
26
+ fill="grey"
27
+ />
28
+ ))}
29
+ </>
30
+ )
31
+ }
@@ -0,0 +1,125 @@
1
+ import React from 'react'
2
+ import { getTickDisplayStr } from '@jbrowse/core/util'
3
+ import { useTheme } from '@mui/material'
4
+
5
+ // locals
6
+ import { makeTicks } from '../util'
7
+
8
+ import { LinearGenomeViewModel } from '..'
9
+ import SVGRegionSeparators from './SVGRegionSeparators'
10
+
11
+ type LGV = LinearGenomeViewModel
12
+
13
+ function Ruler({
14
+ start,
15
+ end,
16
+ bpPerPx,
17
+ reversed = false,
18
+ major = true,
19
+ minor = true,
20
+ hideText = false,
21
+ }: {
22
+ start: number
23
+ end: number
24
+ bpPerPx: number
25
+ reversed?: boolean
26
+ major?: boolean
27
+ minor?: boolean
28
+ hideText?: boolean
29
+ }) {
30
+ const ticks = makeTicks(start, end, bpPerPx, major, minor)
31
+ const theme = useTheme()
32
+ return (
33
+ <>
34
+ {ticks.map(tick => {
35
+ const x = (reversed ? end - tick.base : tick.base - start) / bpPerPx
36
+ return (
37
+ <line
38
+ key={tick.base}
39
+ x1={x}
40
+ x2={x}
41
+ y1={0}
42
+ y2={tick.type === 'major' ? 6 : 4}
43
+ strokeWidth={1}
44
+ stroke={theme.palette.text.secondary}
45
+ />
46
+ )
47
+ })}
48
+ {!hideText
49
+ ? ticks
50
+ .filter(tick => tick.type === 'major')
51
+ .map(tick => {
52
+ const x =
53
+ (reversed ? end - tick.base : tick.base - start) / bpPerPx
54
+ return (
55
+ <text
56
+ x={x - 3}
57
+ y={7 + 11}
58
+ key={`label-${tick.base}`}
59
+ fontSize={11}
60
+ fill={theme.palette.text.primary}
61
+ >
62
+ {getTickDisplayStr(tick.base + 1, bpPerPx)}
63
+ </text>
64
+ )
65
+ })
66
+ : null}
67
+ </>
68
+ )
69
+ }
70
+
71
+ export default function SVGRuler({
72
+ model,
73
+ fontSize,
74
+ }: {
75
+ model: LGV
76
+ fontSize: number
77
+ }) {
78
+ const {
79
+ dynamicBlocks: { contentBlocks },
80
+ offsetPx: viewOffsetPx,
81
+ bpPerPx,
82
+ } = model
83
+ const renderRuler = contentBlocks.length < 5
84
+ const theme = useTheme()
85
+ return (
86
+ <>
87
+ <SVGRegionSeparators model={model} height={30} />
88
+ {contentBlocks.map(block => {
89
+ const { start, end, key, reversed, offsetPx, refName, widthPx } = block
90
+ const offset = offsetPx - viewOffsetPx
91
+ const clipid = `clip-${key}`
92
+ return (
93
+ <g key={key}>
94
+ <defs>
95
+ <clipPath id={clipid}>
96
+ <rect x={0} y={0} width={widthPx} height={100} />
97
+ </clipPath>
98
+ </defs>
99
+ <g transform={`translate(${offset} 0)`}>
100
+ <g clipPath={`url(#${clipid})`}>
101
+ <text
102
+ x={4}
103
+ y={fontSize}
104
+ fontSize={fontSize}
105
+ fill={theme.palette.text.primary}
106
+ >
107
+ {refName}
108
+ </text>
109
+ <g transform="translate(0 20)">
110
+ <Ruler
111
+ hideText={!renderRuler}
112
+ start={start}
113
+ end={end}
114
+ bpPerPx={bpPerPx}
115
+ reversed={reversed}
116
+ />
117
+ </g>
118
+ </g>
119
+ </g>
120
+ </g>
121
+ )
122
+ })}
123
+ </>
124
+ )
125
+ }
@@ -0,0 +1,57 @@
1
+ import React from 'react'
2
+ import { getBpDisplayStr } from '@jbrowse/core/util'
3
+ import { useTheme } from '@mui/material'
4
+ import { LinearGenomeViewModel } from '..'
5
+
6
+ type LGV = LinearGenomeViewModel
7
+
8
+ export default function SVGScalebar({
9
+ model,
10
+ fontSize,
11
+ }: {
12
+ model: LGV
13
+ fontSize: number
14
+ }) {
15
+ const {
16
+ offsetPx,
17
+ dynamicBlocks: { totalWidthPxWithoutBorders: totalWidthPx, totalBp },
18
+ } = model
19
+ const theme = useTheme()
20
+ const displayBp = getBpDisplayStr(totalBp)
21
+ const x0 = Math.max(-offsetPx, 0)
22
+ const x1 = x0 + totalWidthPx
23
+ return (
24
+ <>
25
+ <line
26
+ x1={x0}
27
+ x2={x1}
28
+ y1={10}
29
+ y2={10}
30
+ stroke={theme.palette.text.secondary}
31
+ />
32
+ <line
33
+ x1={x0}
34
+ x2={x0}
35
+ y1={5}
36
+ y2={15}
37
+ stroke={theme.palette.text.secondary}
38
+ />
39
+ <line
40
+ x1={x1}
41
+ x2={x1}
42
+ y1={5}
43
+ y2={15}
44
+ stroke={theme.palette.text.secondary}
45
+ />
46
+ <text
47
+ x={x0 + (x1 - x0) / 2}
48
+ y={fontSize * 2}
49
+ textAnchor="middle"
50
+ fontSize={fontSize}
51
+ fill={theme.palette.text.primary}
52
+ >
53
+ {displayBp}
54
+ </text>
55
+ </>
56
+ )
57
+ }
@@ -0,0 +1,45 @@
1
+ import React from 'react'
2
+ import { useTheme } from '@mui/material'
3
+
4
+ export default function SVGTrackLabel({
5
+ trackLabels,
6
+ trackName,
7
+ fontSize,
8
+ trackLabelOffset,
9
+ x,
10
+ }: {
11
+ trackName: string
12
+ trackLabels: string
13
+ fontSize: number
14
+ trackLabelOffset: number
15
+ x: number
16
+ }) {
17
+ const theme = useTheme()
18
+ const fill = theme.palette.text.primary
19
+ const xoff = trackLabels === 'overlay' ? 5 : 0
20
+ return trackLabels !== 'none' ? (
21
+ <g>
22
+ {trackLabels === 'left' ? (
23
+ <text
24
+ x={trackLabelOffset - 40}
25
+ y={20}
26
+ fill={fill}
27
+ fontSize={fontSize}
28
+ dominantBaseline="hanging"
29
+ textAnchor="end"
30
+ >
31
+ {trackName}
32
+ </text>
33
+ ) : (
34
+ <text
35
+ x={x + xoff}
36
+ fill={fill}
37
+ fontSize={fontSize}
38
+ dominantBaseline="hanging"
39
+ >
40
+ {trackName}
41
+ </text>
42
+ )}
43
+ </g>
44
+ ) : null
45
+ }
@@ -0,0 +1,67 @@
1
+ import React from 'react'
2
+ import { AnyConfigurationModel } from '@jbrowse/core/configuration'
3
+ import { getTrackName } from '@jbrowse/core/util/tracks'
4
+ import { getSession } from '@jbrowse/core/util'
5
+ // locals
6
+ import { LinearGenomeViewModel } from '..'
7
+ import SVGRegionSeparators from './SVGRegionSeparators'
8
+ import SVGTrackLabel from './SVGTrackLabel'
9
+
10
+ type LGV = LinearGenomeViewModel
11
+
12
+ interface DisplayResult {
13
+ track: {
14
+ configuration: AnyConfigurationModel
15
+ displays: { height: number }[]
16
+ }
17
+ result: string
18
+ }
19
+
20
+ // SVG component, tracks
21
+ export default function SVGTracks({
22
+ displayResults,
23
+ model,
24
+ offset,
25
+ textHeight,
26
+ fontSize,
27
+ trackLabels = 'offset',
28
+ trackLabelOffset = 0,
29
+ }: {
30
+ displayResults: DisplayResult[]
31
+ model: LGV
32
+ offset: number
33
+ textHeight: number
34
+ fontSize: number
35
+ trackLabels?: string
36
+ trackLabelOffset?: number
37
+ }) {
38
+ const session = getSession(model)
39
+ const textOffset = trackLabels === 'offset' ? textHeight : 0
40
+ return (
41
+ <>
42
+ {displayResults.map(({ track, result }) => {
43
+ const current = offset
44
+ const conf = track.configuration
45
+ const trackName = getTrackName(conf, session)
46
+ const display = track.displays[0]
47
+ const x = Math.max(-model.offsetPx, 0)
48
+ offset += display.height + textOffset
49
+ return (
50
+ <g key={conf.trackId} transform={`translate(0 ${current})`}>
51
+ <g transform={`translate(${trackLabelOffset} ${textOffset})`}>
52
+ <SVGRegionSeparators model={model} height={display.height} />
53
+ {result}
54
+ </g>
55
+ <SVGTrackLabel
56
+ trackName={trackName}
57
+ fontSize={fontSize}
58
+ trackLabels={trackLabels}
59
+ trackLabelOffset={trackLabelOffset}
60
+ x={x}
61
+ />
62
+ </g>
63
+ )
64
+ })}
65
+ </>
66
+ )
67
+ }
@@ -16,15 +16,18 @@ describe('tick calculation', () => {
16
16
  { type: 'minor', base: 8, index: 9 },
17
17
  { type: 'major', base: 9, index: 10 },
18
18
  { type: 'minor', base: 10, index: 11 },
19
+ { type: 'minor', base: 11, index: 12 },
19
20
  ])
20
21
  })
21
22
  test('two', () => {
22
23
  const result = Array.from(makeTicks(0, 50, 1))
23
24
  expect(result).toEqual([
24
- { type: 'major', base: -1, index: 0 },
25
- { type: 'minor', base: 19, index: 1 },
26
- { type: 'minor', base: 39, index: 2 },
27
- { type: 'minor', base: 59, index: 3 },
25
+ { type: 'minor', base: -21, index: 0 },
26
+ { type: 'major', base: -1, index: 1 },
27
+ { type: 'minor', base: 19, index: 2 },
28
+ { type: 'minor', base: 39, index: 3 },
29
+ { type: 'minor', base: 59, index: 4 },
30
+ { type: 'minor', base: 79, index: 5 },
28
31
  ])
29
32
  })
30
33
  })
@@ -68,8 +68,8 @@ export function makeTicks(
68
68
  let index = 0
69
69
  const ticks = []
70
70
  for (
71
- let base = Math.ceil(minBase / iterPitch) * iterPitch;
72
- base < maxBase;
71
+ let base = Math.floor(minBase / iterPitch) * iterPitch;
72
+ base < Math.ceil(maxBase / iterPitch) * iterPitch + 1;
73
73
  base += iterPitch
74
74
  ) {
75
75
  if (emitMinor && base % (gridPitch.majorPitch * 2)) {