@apollo-annotation/jbrowse-plugin-apollo 0.3.7 → 0.3.9

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 (86) hide show
  1. package/dist/index.esm.js +11212 -10483
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +11251 -10509
  4. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
  5. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
  6. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
  7. package/dist/jbrowse-plugin-apollo.umd.development.js +7726 -9014
  8. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
  9. package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
  10. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
  11. package/package.json +18 -18
  12. package/src/ApolloInternetAccount/model.ts +123 -70
  13. package/src/ApolloRefNameAliasAdapter/ApolloRefNameAliasAdapter.ts +4 -4
  14. package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +9 -7
  15. package/src/BackendDrivers/CollaborationServerDriver.ts +72 -20
  16. package/src/BackendDrivers/DesktopFileDriver.ts +2 -2
  17. package/src/ChangeManager.ts +36 -14
  18. package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +64 -5
  19. package/src/FeatureDetailsWidget/BasicInformation.tsx +6 -4
  20. package/src/FeatureDetailsWidget/NumberTextField.tsx +5 -2
  21. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +70 -73
  22. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +72 -234
  23. package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +92 -0
  24. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +23 -131
  25. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +50 -194
  26. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +279 -217
  27. package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +53 -34
  28. package/src/LinearApolloDisplay/glyphs/Glyph.ts +7 -9
  29. package/src/LinearApolloDisplay/glyphs/util.ts +19 -0
  30. package/src/LinearApolloDisplay/stateModel/base.ts +34 -43
  31. package/src/LinearApolloDisplay/stateModel/layouts.ts +3 -2
  32. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +32 -261
  33. package/src/LinearApolloDisplay/stateModel/rendering.ts +43 -343
  34. package/src/LinearApolloReferenceSequenceDisplay/components/LinearApolloReferenceSequenceDisplay.tsx +87 -0
  35. package/src/LinearApolloReferenceSequenceDisplay/components/index.ts +1 -0
  36. package/src/LinearApolloReferenceSequenceDisplay/configSchema.ts +7 -0
  37. package/src/LinearApolloReferenceSequenceDisplay/drawSequenceOverlay.ts +181 -0
  38. package/src/LinearApolloReferenceSequenceDisplay/drawSequenceTrack.ts +218 -0
  39. package/src/LinearApolloReferenceSequenceDisplay/index.ts +3 -0
  40. package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +227 -0
  41. package/src/LinearApolloReferenceSequenceDisplay/stateModel/index.ts +25 -0
  42. package/src/LinearApolloReferenceSequenceDisplay/stateModel/rendering.ts +157 -0
  43. package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +101 -38
  44. package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +334 -262
  45. package/src/LinearApolloSixFrameDisplay/glyphs/Glyph.ts +12 -8
  46. package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +42 -4
  47. package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +4 -8
  48. package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +73 -97
  49. package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +49 -61
  50. package/src/TabularEditor/HybridGrid/Feature.tsx +16 -14
  51. package/src/TabularEditor/HybridGrid/HybridGrid.tsx +7 -5
  52. package/src/components/AddAssembly.tsx +34 -38
  53. package/src/components/AddAssemblyAliases.tsx +1 -1
  54. package/src/components/AddChildFeature.tsx +5 -2
  55. package/src/components/AddFeature.tsx +30 -21
  56. package/src/components/AddRefSeqAliases.tsx +64 -50
  57. package/src/components/CopyFeature.tsx +4 -2
  58. package/src/components/CreateApolloAnnotation.tsx +22 -9
  59. package/src/components/DeleteAssembly.tsx +3 -10
  60. package/src/components/DownloadGFF3.tsx +2 -2
  61. package/src/components/EditZoomThresholdDialog.tsx +69 -0
  62. package/src/components/FilterFeatures.tsx +7 -7
  63. package/src/components/FilterTranscripts.tsx +6 -6
  64. package/src/components/ImportFeatures.tsx +1 -1
  65. package/src/components/ManageChecks.tsx +3 -10
  66. package/src/components/ManageUsers.tsx +23 -22
  67. package/src/components/MergeTranscripts.tsx +12 -15
  68. package/src/components/OntologyTermAutocomplete.tsx +1 -8
  69. package/src/components/OntologyTermMultiSelect.tsx +11 -11
  70. package/src/components/OpenLocalFile.tsx +11 -7
  71. package/src/components/ViewChangeLog.tsx +25 -50
  72. package/src/components/ViewCheckResults.tsx +2 -8
  73. package/src/components/index.ts +1 -0
  74. package/src/config.ts +6 -0
  75. package/src/index.ts +53 -115
  76. package/src/makeDisplayComponent.tsx +9 -14
  77. package/src/menus/index.ts +1 -0
  78. package/src/{ApolloInternetAccount/addMenuItems.ts → menus/topLevelMenu.ts} +56 -47
  79. package/src/menus/topLevelMenuAdmin.ts +154 -0
  80. package/src/session/ClientDataStore.ts +32 -14
  81. package/src/session/session.ts +159 -121
  82. package/src/util/annotationFeatureUtils.ts +15 -21
  83. package/src/util/displayUtils.ts +149 -0
  84. package/src/util/glyphUtils.ts +329 -0
  85. package/src/util/loadAssemblyIntoClient.ts +3 -2
  86. package/src/util/mouseEventsUtils.ts +32 -0
@@ -0,0 +1,181 @@
1
+ import { type AnnotationFeature } from '@apollo-annotation/mst'
2
+ import { type Frame, getFrame } from '@jbrowse/core/util'
3
+ import { type BlockSet, type ContentBlock } from '@jbrowse/core/util/blockTypes'
4
+ import { type Theme } from '@mui/material'
5
+
6
+ import { type ApolloSessionModel, type HoveredFeature } from '../session'
7
+
8
+ function getSeqRow(
9
+ strand: 1 | -1 | undefined,
10
+ bpPerPx: number,
11
+ ): number | undefined {
12
+ if (bpPerPx > 1 || strand === undefined) {
13
+ return
14
+ }
15
+ return strand === 1 ? 3 : 4
16
+ }
17
+
18
+ function getTranslationRow(frame: Frame, bpPerPx: number) {
19
+ const offset = bpPerPx <= 1 ? 2 : 0
20
+ switch (frame) {
21
+ case 3: {
22
+ return 0
23
+ }
24
+ case 2: {
25
+ return 1
26
+ }
27
+ case 1: {
28
+ return 2
29
+ }
30
+ case -1: {
31
+ return 3 + offset
32
+ }
33
+ case -2: {
34
+ return 4 + offset
35
+ }
36
+ case -3: {
37
+ return 5 + offset
38
+ }
39
+ }
40
+ }
41
+
42
+ function getLeftPx(
43
+ feature: { min: number; max: number },
44
+ bpPerPx: number,
45
+ offsetPx: number,
46
+ block: ContentBlock,
47
+ ) {
48
+ const blockLeftPx = block.offsetPx - offsetPx
49
+ const featureLeftBpDistanceFromBlockLeftBp = block.reversed
50
+ ? block.end - feature.max
51
+ : feature.min - block.start
52
+ const featureLeftPxDistanceFromBlockLeftPx =
53
+ featureLeftBpDistanceFromBlockLeftBp / bpPerPx
54
+ return blockLeftPx + featureLeftPxDistanceFromBlockLeftPx
55
+ }
56
+
57
+ function fillAndStrokeRect(
58
+ ctx: CanvasRenderingContext2D,
59
+ left: number,
60
+ top: number,
61
+ width: number,
62
+ height: number,
63
+ theme: Theme,
64
+ selected = false,
65
+ ) {
66
+ ctx.fillStyle = selected
67
+ ? theme.palette.action.disabled
68
+ : theme.palette.action.focus
69
+ ctx.fillRect(left, top, width, height)
70
+ ctx.strokeStyle = selected
71
+ ? theme.palette.text.secondary
72
+ : theme.palette.text.primary
73
+ ctx.strokeStyle = theme.palette.text.primary
74
+ ctx.strokeRect(left, top, width, height)
75
+ }
76
+
77
+ function drawHighlight(
78
+ ctx: CanvasRenderingContext2D,
79
+ feature: AnnotationFeature,
80
+ bpPerPx: number,
81
+ offsetPx: number,
82
+ rowHeight: number,
83
+ block: ContentBlock,
84
+ theme: Theme,
85
+ selected = false,
86
+ ) {
87
+ const row = getSeqRow(feature.strand, bpPerPx)
88
+ if (!row) {
89
+ return
90
+ }
91
+ const left = getLeftPx(feature, bpPerPx, offsetPx, block)
92
+ const width = feature.length / bpPerPx
93
+ const top = row * rowHeight
94
+ fillAndStrokeRect(ctx, left, top, width, rowHeight, theme, selected)
95
+ }
96
+
97
+ function drawCDSHighlight(
98
+ ctx: CanvasRenderingContext2D,
99
+ feature: AnnotationFeature,
100
+ bpPerPx: number,
101
+ offsetPx: number,
102
+ rowHeight: number,
103
+ block: ContentBlock,
104
+ theme: Theme,
105
+ selected = false,
106
+ ) {
107
+ const parentFeature = feature.parent
108
+ if (!parentFeature) {
109
+ return
110
+ }
111
+ const cdsLocs = parentFeature.cdsLocations.find((loc) => {
112
+ const min = loc.at(feature.strand === 1 ? 0 : -1)?.min
113
+ const max = loc.at(feature.strand === 1 ? -1 : 0)?.max
114
+ return feature.min === min && feature.max === max
115
+ })
116
+ if (!cdsLocs) {
117
+ return
118
+ }
119
+ for (const loc of cdsLocs) {
120
+ const frame = getFrame(loc.min, loc.max, feature.strand ?? 1, loc.phase)
121
+ const row = getTranslationRow(frame, bpPerPx)
122
+ const left = getLeftPx(loc, bpPerPx, offsetPx, block)
123
+ const top = row * rowHeight
124
+ const width = (loc.max - loc.min) / bpPerPx
125
+ fillAndStrokeRect(ctx, left, top, width, rowHeight, theme, selected)
126
+ }
127
+ }
128
+
129
+ export function drawSequenceOverlay(
130
+ canvas: HTMLCanvasElement,
131
+ ctx: CanvasRenderingContext2D,
132
+ hoveredFeature: HoveredFeature | undefined,
133
+ selectedFeature: AnnotationFeature | undefined,
134
+ rowHeight: number,
135
+ theme: Theme,
136
+ session: ApolloSessionModel,
137
+ bpPerPx: number,
138
+ offsetPx: number,
139
+ dynamicBlocks: BlockSet,
140
+ ) {
141
+ const { featureTypeOntology } = session.apolloDataStore.ontologyManager
142
+ if (!featureTypeOntology) {
143
+ throw new Error('featureTypeOntology is undefined')
144
+ }
145
+ for (const block of dynamicBlocks.contentBlocks) {
146
+ ctx.save()
147
+ ctx.beginPath()
148
+ const blockLeftPx = block.offsetPx - offsetPx
149
+ ctx.rect(blockLeftPx, 0, block.widthPx, canvas.height)
150
+ ctx.clip()
151
+ for (const feature of [
152
+ selectedFeature,
153
+ hoveredFeature?.feature,
154
+ ].filter<AnnotationFeature>((f) => f !== undefined)) {
155
+ if (featureTypeOntology.isTypeOf(feature.type, 'CDS')) {
156
+ drawCDSHighlight(
157
+ ctx,
158
+ feature,
159
+ bpPerPx,
160
+ offsetPx,
161
+ rowHeight,
162
+ block,
163
+ theme,
164
+ true,
165
+ )
166
+ } else {
167
+ drawHighlight(
168
+ ctx,
169
+ feature,
170
+ bpPerPx,
171
+ offsetPx,
172
+ rowHeight,
173
+ block,
174
+ theme,
175
+ true,
176
+ )
177
+ }
178
+ }
179
+ ctx.restore()
180
+ }
181
+ }
@@ -0,0 +1,218 @@
1
+ import { defaultCodonTable, getFrame, revcom } from '@jbrowse/core/util'
2
+ import { type BlockSet } from '@jbrowse/core/util/blockTypes'
3
+ import { type Theme } from '@mui/material'
4
+
5
+ import { type ApolloSessionModel } from '../session'
6
+
7
+ function colorCode(letter: string, theme: Theme) {
8
+ const letterUpper = letter.toUpperCase()
9
+ if (
10
+ letterUpper === 'A' ||
11
+ letterUpper === 'C' ||
12
+ letterUpper === 'G' ||
13
+ letterUpper === 'T'
14
+ ) {
15
+ return theme.palette.bases[letterUpper].main.toString()
16
+ }
17
+ return 'lightgray'
18
+ }
19
+
20
+ function codonColorCode(letter: string, theme: Theme, highContrast?: boolean) {
21
+ if (letter === 'M') {
22
+ return theme.palette.startCodon
23
+ }
24
+ if (letter === '*') {
25
+ return highContrast ? theme.palette.text.primary : theme.palette.stopCodon
26
+ }
27
+ return
28
+ }
29
+
30
+ function drawLetter(
31
+ seqTrackctx: CanvasRenderingContext2D,
32
+ left: number,
33
+ top: number,
34
+ width: number,
35
+ letter: string,
36
+ ) {
37
+ const fontSize = Math.min(width, 10)
38
+ seqTrackctx.fillStyle = '#000'
39
+ seqTrackctx.font = `${fontSize}px`
40
+ const textWidth = seqTrackctx.measureText(letter).width
41
+ const textX = left + (width - textWidth) / 2
42
+ seqTrackctx.fillText(letter, textX, top + 10)
43
+ }
44
+
45
+ function drawTranslationFrameBackgrounds(
46
+ canvas: HTMLCanvasElement,
47
+ ctx: CanvasRenderingContext2D,
48
+ bpPerPx: number,
49
+ theme: Theme,
50
+ dynamicBlocks: BlockSet,
51
+ highContrast: boolean,
52
+ sequenceRowHeight: number,
53
+ ) {
54
+ const frames =
55
+ bpPerPx <= 1 ? [3, 2, 1, 0, 0, -1, -2, -3] : [3, 2, 1, -1, -2, -3]
56
+ for (const [idx, frame] of frames.entries()) {
57
+ const frameColor = theme.palette.framesCDS.at(frame)?.main
58
+ if (!frameColor) {
59
+ continue
60
+ }
61
+ const top = idx * sequenceRowHeight
62
+ const { offsetPx } = dynamicBlocks
63
+ const left = Math.max(0, -offsetPx)
64
+ const width = dynamicBlocks.totalWidthPx
65
+ ctx.fillStyle = highContrast ? theme.palette.background.default : frameColor
66
+ ctx.fillRect(left, top, width, sequenceRowHeight)
67
+ if (highContrast) {
68
+ // eslint-disable-next-line prefer-destructuring
69
+ ctx.strokeStyle = theme.palette.grey[200]
70
+ ctx.strokeRect(left, top, width, sequenceRowHeight)
71
+ }
72
+ }
73
+ // allows inter-region padding lines to show through
74
+ for (const block of dynamicBlocks.getBlocks()) {
75
+ if (block.type === 'InterRegionPaddingBlock') {
76
+ const left = block.offsetPx - dynamicBlocks.offsetPx
77
+ ctx.clearRect(left, 0, block.widthPx, canvas.height)
78
+ }
79
+ }
80
+ }
81
+
82
+ function drawBase(
83
+ ctx: CanvasRenderingContext2D,
84
+ base: string,
85
+ index: number,
86
+ leftPx: number,
87
+ bpPerPx: number,
88
+ rowHeight: number,
89
+ theme: Theme,
90
+ ) {
91
+ const width = 1 / bpPerPx
92
+ if (width < 1) {
93
+ return
94
+ }
95
+ const left = leftPx + index / bpPerPx
96
+ const strands = [-1, 1] as const
97
+ for (const strand of strands) {
98
+ const top = (strand === 1 ? 3 : 4) * rowHeight
99
+ const baseCode = strand === 1 ? base : revcom(base)
100
+ ctx.fillStyle = colorCode(baseCode, theme)
101
+ ctx.fillRect(left, top, width, rowHeight)
102
+ if (1 / bpPerPx >= 12) {
103
+ ctx.strokeStyle = theme.palette.text.disabled
104
+ ctx.strokeRect(left, top, width, rowHeight)
105
+ drawLetter(ctx, left, top, width, baseCode)
106
+ }
107
+ }
108
+ }
109
+
110
+ function drawCodon(
111
+ ctx: CanvasRenderingContext2D,
112
+ codon: string,
113
+ leftPx: number,
114
+ index: number,
115
+ theme: Theme,
116
+ highContrast: boolean,
117
+ bpPerPx: number,
118
+ bp: number,
119
+ rowHeight: number,
120
+ showStartCodons: boolean,
121
+ showStopCodons: boolean,
122
+ ) {
123
+ const frameOffsets = (
124
+ bpPerPx <= 1 ? [0, 2, 1, 0, 7, 6, 5] : [0, 2, 1, 0, 5, 4, 3]
125
+ ).map((b) => b * rowHeight)
126
+ const strands = [-1, 1] as const
127
+ for (const strand of strands) {
128
+ const frame = getFrame(bp, bp + 3, strand, 0)
129
+ const top = frameOffsets.at(frame)
130
+ if (top === undefined) {
131
+ continue
132
+ }
133
+ const left = Math.round(leftPx + index / bpPerPx)
134
+ const width = Math.round(3 / bpPerPx)
135
+ const codonCode = strand === 1 ? codon : revcom(codon)
136
+ const aminoAcidCode =
137
+ defaultCodonTable[codonCode as keyof typeof defaultCodonTable]
138
+ const fillColor = codonColorCode(aminoAcidCode, theme, highContrast)
139
+ if (
140
+ fillColor &&
141
+ ((showStopCodons && aminoAcidCode == '*') ||
142
+ (showStartCodons && aminoAcidCode != '*'))
143
+ ) {
144
+ ctx.fillStyle = fillColor
145
+ ctx.fillRect(left, top, width, rowHeight)
146
+ }
147
+ if (1 / bpPerPx >= 4) {
148
+ ctx.strokeStyle = theme.palette.text.disabled
149
+ ctx.strokeRect(left, top, width, rowHeight)
150
+ drawLetter(ctx, left, top, width, aminoAcidCode)
151
+ }
152
+ }
153
+ }
154
+
155
+ export function drawSequenceTrack(
156
+ canvas: HTMLCanvasElement,
157
+ theme: Theme,
158
+ bpPerPx: number,
159
+ offsetPx: number,
160
+ dynamicBlocks: BlockSet,
161
+ highContrast: boolean,
162
+ showStartCodons: boolean,
163
+ showStopCodons: boolean,
164
+ sequenceRowHeight: number,
165
+ session: ApolloSessionModel,
166
+ ) {
167
+ const ctx = canvas.getContext('2d')
168
+ if (!ctx) {
169
+ return
170
+ }
171
+ ctx.clearRect(0, 0, canvas.width, canvas.height)
172
+
173
+ drawTranslationFrameBackgrounds(
174
+ canvas,
175
+ ctx,
176
+ bpPerPx,
177
+ theme,
178
+ dynamicBlocks,
179
+ highContrast,
180
+ sequenceRowHeight,
181
+ )
182
+
183
+ const { apolloDataStore } = session
184
+ for (const block of dynamicBlocks.contentBlocks) {
185
+ const assembly = apolloDataStore.assemblies.get(block.assemblyName)
186
+ const ref = assembly?.getByRefName(block.refName)
187
+ const roundedStart = Math.floor(block.start)
188
+ const roundedEnd = Math.ceil(block.end)
189
+ let seq = ref?.getSequence(roundedStart, roundedEnd)
190
+ if (!seq) {
191
+ return
192
+ }
193
+ seq = seq.toUpperCase()
194
+ const baseOffsetPx = (block.start - roundedStart) / bpPerPx
195
+ const seqLeftPx = Math.round(block.offsetPx - offsetPx - baseOffsetPx)
196
+ for (let i = 0; i < seq.length; i++) {
197
+ const bp = roundedStart + i
198
+ const codon = seq.slice(i, i + 3)
199
+ drawBase(ctx, seq[i], i, seqLeftPx, bpPerPx, sequenceRowHeight, theme)
200
+ if (codon.length !== 3) {
201
+ continue
202
+ }
203
+ drawCodon(
204
+ ctx,
205
+ codon,
206
+ seqLeftPx,
207
+ i,
208
+ theme,
209
+ highContrast,
210
+ bpPerPx,
211
+ bp,
212
+ sequenceRowHeight,
213
+ showStartCodons,
214
+ showStopCodons,
215
+ )
216
+ }
217
+ }
218
+ }
@@ -0,0 +1,3 @@
1
+ export { configSchema } from './configSchema'
2
+ export { stateModelFactory } from './stateModel'
3
+ export { LinearApolloReferenceSequenceDisplay } from './components/LinearApolloReferenceSequenceDisplay'
@@ -0,0 +1,227 @@
1
+ /* eslint-disable @typescript-eslint/unbound-method */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
3
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
4
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
5
+ /* eslint-disable @typescript-eslint/no-unnecessary-condition */
6
+ import { type AnnotationFeature } from '@apollo-annotation/mst'
7
+ import type PluginManager from '@jbrowse/core/PluginManager'
8
+ import { ConfigurationReference, getConf } from '@jbrowse/core/configuration'
9
+ import { type AnyConfigurationSchemaType } from '@jbrowse/core/configuration/configurationSchema'
10
+ import { BaseDisplay } from '@jbrowse/core/pluggableElementTypes'
11
+ import {
12
+ type AbstractSessionModel,
13
+ getContainingView,
14
+ getSession,
15
+ } from '@jbrowse/core/util'
16
+ import { getParentRenderProps } from '@jbrowse/core/util/tracks'
17
+ // import type LinearGenomeViewPlugin from '@jbrowse/plugin-linear-genome-view'
18
+ import { type LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
19
+ import { autorun } from 'mobx'
20
+ import { addDisposer, getRoot, types } from 'mobx-state-tree'
21
+
22
+ import { type ApolloInternetAccountModel } from '../../ApolloInternetAccount/model'
23
+ import { type ApolloSessionModel, type HoveredFeature } from '../../session'
24
+ import { type ApolloRootModel } from '../../types'
25
+
26
+ const minDisplayHeight = 20
27
+
28
+ export function baseModelFactory(
29
+ _pluginManager: PluginManager,
30
+ configSchema: AnyConfigurationSchemaType,
31
+ ) {
32
+ return BaseDisplay.named('BaseLinearApolloReferenceSequenceDisplay')
33
+ .props({
34
+ type: types.literal('LinearApolloReferenceSequenceDisplay'),
35
+ configuration: ConfigurationReference(configSchema),
36
+ showStartCodons: false,
37
+ showStopCodons: true,
38
+ highContrast: false,
39
+ heightPreConfig: types.maybe(
40
+ types.refinement(
41
+ 'displayHeight',
42
+ types.number,
43
+ (n) => n >= minDisplayHeight,
44
+ ),
45
+ ),
46
+ sequenceRowHeight: 15,
47
+ })
48
+ .views((self) => {
49
+ const { configuration, renderProps: superRenderProps } = self
50
+ return {
51
+ renderProps() {
52
+ return {
53
+ ...superRenderProps(),
54
+ ...getParentRenderProps(self),
55
+ config: configuration.renderer,
56
+ }
57
+ },
58
+ }
59
+ })
60
+ .views((self) => ({
61
+ get lgv() {
62
+ return getContainingView(self) as unknown as LinearGenomeViewModel
63
+ },
64
+ }))
65
+ .views((self) => ({
66
+ get rendererTypeName() {
67
+ return self.configuration.renderer.type
68
+ },
69
+ get session() {
70
+ return getSession(self) as unknown as ApolloSessionModel
71
+ },
72
+ get regions() {
73
+ const regions = self.lgv.dynamicBlocks.contentBlocks.map(
74
+ ({ assemblyName, end, refName, start }) => ({
75
+ assemblyName,
76
+ refName,
77
+ start: Math.round(start),
78
+ end: Math.round(end),
79
+ }),
80
+ )
81
+ return regions
82
+ },
83
+ regionCannotBeRendered(/* region */) {
84
+ if (self.lgv && self.lgv.bpPerPx >= 3) {
85
+ return 'Zoom in to see sequence'
86
+ }
87
+ return
88
+ },
89
+ }))
90
+ .views((self) => ({
91
+ get apolloInternetAccount() {
92
+ const [region] = self.regions
93
+ const { internetAccounts } = getRoot<ApolloRootModel>(self)
94
+ const { assemblyName } = region
95
+ const { assemblyManager } =
96
+ self.session as unknown as AbstractSessionModel
97
+ const assembly = assemblyManager.get(assemblyName)
98
+ if (!assembly) {
99
+ throw new Error(`No assembly found with name ${assemblyName}`)
100
+ }
101
+ const { internetAccountConfigId } = getConf(assembly, [
102
+ 'sequence',
103
+ 'metadata',
104
+ ]) as { internetAccountConfigId: string }
105
+ return internetAccounts.find(
106
+ (ia) => getConf(ia, 'internetAccountId') === internetAccountConfigId,
107
+ ) as ApolloInternetAccountModel | undefined
108
+ },
109
+ get changeManager() {
110
+ return (self.session as unknown as ApolloSessionModel).apolloDataStore
111
+ .changeManager
112
+ },
113
+ getAssemblyId(assemblyName: string) {
114
+ const { assemblyManager } =
115
+ self.session as unknown as AbstractSessionModel
116
+ const assembly = assemblyManager.get(assemblyName)
117
+ if (!assembly) {
118
+ throw new Error(`Could not find assembly named ${assemblyName}`)
119
+ }
120
+ return assembly.name
121
+ },
122
+ get selectedFeature(): AnnotationFeature | undefined {
123
+ return (self.session as unknown as ApolloSessionModel)
124
+ .apolloSelectedFeature
125
+ },
126
+ get hoveredFeature(): HoveredFeature | undefined {
127
+ return (self.session as unknown as ApolloSessionModel)
128
+ .apolloHoveredFeature
129
+ },
130
+ get height() {
131
+ const { sequenceRowHeight } = self
132
+ return self.lgv.bpPerPx <= 1
133
+ ? sequenceRowHeight * 8
134
+ : sequenceRowHeight * 6
135
+ },
136
+ }))
137
+ .volatile(() => ({
138
+ scrollTop: 0,
139
+ }))
140
+ .actions((self) => ({
141
+ setScrollTop(scrollTop: number) {
142
+ self.scrollTop = scrollTop
143
+ },
144
+ setHeight(displayHeight: number) {
145
+ self.heightPreConfig = Math.max(displayHeight, minDisplayHeight)
146
+ return self.height
147
+ },
148
+ resizeHeight(distance: number) {
149
+ const oldHeight = self.height
150
+ const newHeight = this.setHeight(self.height + distance)
151
+ return newHeight - oldHeight
152
+ },
153
+ toggleShowStartCodons() {
154
+ self.showStartCodons = !self.showStartCodons
155
+ },
156
+ toggleShowStopCodons() {
157
+ self.showStopCodons = !self.showStopCodons
158
+ },
159
+ toggleHighContrast() {
160
+ self.highContrast = !self.highContrast
161
+ },
162
+ }))
163
+ .views((self) => {
164
+ const { trackMenuItems: superTrackMenuItems } = self
165
+ return {
166
+ trackMenuItems() {
167
+ const { showStartCodons, showStopCodons, highContrast } = self
168
+ return [
169
+ ...superTrackMenuItems(),
170
+ {
171
+ type: 'subMenu',
172
+ label: 'Appearance',
173
+ subMenu: [
174
+ {
175
+ label: 'Show start codons',
176
+ type: 'checkbox',
177
+ checked: showStartCodons,
178
+ onClick: () => {
179
+ self.toggleShowStartCodons()
180
+ },
181
+ },
182
+ {
183
+ label: 'Show stop codons',
184
+ type: 'checkbox',
185
+ checked: showStopCodons,
186
+ onClick: () => {
187
+ self.toggleShowStopCodons()
188
+ },
189
+ },
190
+ {
191
+ label: 'Use high contrast colors',
192
+ type: 'checkbox',
193
+ checked: highContrast,
194
+ onClick: () => {
195
+ self.toggleHighContrast()
196
+ },
197
+ },
198
+ ],
199
+ },
200
+ ]
201
+ },
202
+ }
203
+ })
204
+ .actions((self) => ({
205
+ afterAttach() {
206
+ addDisposer(
207
+ self,
208
+ autorun(
209
+ () => {
210
+ if (!self.lgv.initialized || self.regionCannotBeRendered()) {
211
+ return
212
+ }
213
+ if (self.lgv.bpPerPx <= 3) {
214
+ void (
215
+ self.session as unknown as ApolloSessionModel
216
+ ).apolloDataStore.loadRefSeq(self.regions)
217
+ }
218
+ },
219
+ {
220
+ name: 'LinearApolloReferenceSequenceDisplayLoadFeatures',
221
+ delay: 1000,
222
+ },
223
+ ),
224
+ )
225
+ },
226
+ }))
227
+ }
@@ -0,0 +1,25 @@
1
+ import type PluginManager from '@jbrowse/core/PluginManager'
2
+ import { type AnyConfigurationSchemaType } from '@jbrowse/core/configuration/configurationSchema'
3
+ import { type Instance } from 'mobx-state-tree'
4
+
5
+ import { renderingModelFactory } from './rendering'
6
+
7
+ export function stateModelFactory(
8
+ pluginManager: PluginManager,
9
+ configSchema: AnyConfigurationSchemaType,
10
+ ) {
11
+ // TODO: this needs to be refactored so that the final composition of the
12
+ // state model mixins happens here in one central place
13
+ return renderingModelFactory(pluginManager, configSchema).named(
14
+ 'LinearApolloReferenceSequenceDisplay',
15
+ )
16
+ }
17
+
18
+ export type LinearApolloReferenceSequenceDisplayStateModel = ReturnType<
19
+ typeof stateModelFactory
20
+ >
21
+ // eslint disable because of
22
+ // https://mobx-state-tree.js.org/tips/typescript#using-a-mst-type-at-design-time
23
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
24
+ export interface LinearApolloReferenceSequenceDisplay
25
+ extends Instance<LinearApolloReferenceSequenceDisplayStateModel> {}