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

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 (31) hide show
  1. package/dist/index.esm.js +235 -120
  2. package/dist/index.esm.js.map +1 -1
  3. package/dist/jbrowse-plugin-apollo.cjs.development.js +233 -118
  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 +562 -298
  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 +4 -4
  12. package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +1 -1
  13. package/src/ApolloInternetAccount/model.ts +6 -2
  14. package/src/BackendDrivers/CollaborationServerDriver.ts +11 -5
  15. package/src/ChangeManager.ts +19 -4
  16. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +29 -9
  17. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +2 -2
  18. package/src/LinearApolloDisplay/glyphs/util.ts +17 -0
  19. package/src/LinearApolloReferenceSequenceDisplay/drawSequenceOverlay.ts +18 -25
  20. package/src/LinearApolloReferenceSequenceDisplay/drawSequenceTrack.ts +41 -59
  21. package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +33 -2
  22. package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +101 -3
  23. package/src/components/AddAssembly.tsx +1 -1
  24. package/src/components/ImportFeatures.tsx +1 -1
  25. package/src/components/OntologyTermAutocomplete.tsx +2 -2
  26. package/src/components/OntologyTermMultiSelect.tsx +2 -2
  27. package/src/makeDisplayComponent.tsx +1 -1
  28. package/src/session/ClientDataStore.ts +1 -1
  29. package/src/session/session.ts +4 -0
  30. package/src/util/displayUtils.ts +28 -0
  31. package/src/util/glyphUtils.ts +18 -0
@@ -1,15 +1,62 @@
1
1
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
2
2
  import type PluginManager from '@jbrowse/core/PluginManager'
3
3
  import { type AnyConfigurationSchemaType } from '@jbrowse/core/configuration/configurationSchema'
4
- import { doesIntersect2 } from '@jbrowse/core/util'
4
+ import {
5
+ defaultCodonTable,
6
+ doesIntersect2,
7
+ getFrame,
8
+ revcom,
9
+ } from '@jbrowse/core/util'
5
10
  import { type Theme, createTheme } from '@mui/material'
6
11
  import { autorun } from 'mobx'
7
12
  import { type Instance, addDisposer, types } from 'mobx-state-tree'
8
13
 
9
14
  import { type ApolloSessionModel } from '../../session'
15
+ import { codonColorCode } from '../../util/displayUtils'
10
16
 
11
17
  import { layoutsModelFactory } from './layouts'
12
18
 
19
+ function drawCodon(
20
+ ctx: CanvasRenderingContext2D,
21
+ codon: string,
22
+ leftPx: number,
23
+ index: number,
24
+ theme: Theme,
25
+ highContrast: boolean,
26
+ bpPerPx: number,
27
+ bp: number,
28
+ rowHeight: number,
29
+ showFeatureLabels: boolean,
30
+ showStartCodons: boolean,
31
+ showStopCodons: boolean,
32
+ ) {
33
+ const frameOffsets = (
34
+ showFeatureLabels ? [0, 4, 2, 0, 14, 12, 10] : [0, 2, 1, 0, 7, 6, 5]
35
+ ).map((b) => b * rowHeight)
36
+ const strands = [-1, 1] as const
37
+ for (const strand of strands) {
38
+ const frame = getFrame(bp, bp + 3, strand, 0)
39
+ const top = frameOffsets.at(frame)
40
+ if (top === undefined) {
41
+ continue
42
+ }
43
+ const left = Math.round(leftPx + index / bpPerPx)
44
+ const width = Math.round(3 / bpPerPx) === 0 ? 1 : Math.round(3 / bpPerPx)
45
+ const codonCode = strand === 1 ? codon : revcom(codon)
46
+ const aminoAcidCode =
47
+ defaultCodonTable[codonCode as keyof typeof defaultCodonTable]
48
+ const fillColor = codonColorCode(aminoAcidCode, theme, highContrast)
49
+ if (
50
+ fillColor &&
51
+ ((showStopCodons && aminoAcidCode == '*') ||
52
+ (showStartCodons && aminoAcidCode != '*'))
53
+ ) {
54
+ ctx.fillStyle = fillColor
55
+ ctx.fillRect(left, top, width, rowHeight)
56
+ }
57
+ }
58
+ }
59
+
13
60
  export function renderingModelFactory(
14
61
  pluginManager: PluginManager,
15
62
  configSchema: AnyConfigurationSchemaType,
@@ -135,17 +182,29 @@ export function renderingModelFactory(
135
182
  self,
136
183
  autorun(
137
184
  () => {
138
- const { canvas, featureLayouts, featuresHeight, lgv } = self
185
+ const {
186
+ apolloRowHeight,
187
+ canvas,
188
+ featureLayouts,
189
+ featuresHeight,
190
+ lgv,
191
+ session,
192
+ theme,
193
+ showFeatureLabels,
194
+ showStartCodons,
195
+ showStopCodons,
196
+ } = self
139
197
  if (!lgv.initialized || self.regionCannotBeRendered()) {
140
198
  return
141
199
  }
142
- const { displayedRegions, dynamicBlocks } = lgv
200
+ const { bpPerPx, offsetPx, displayedRegions, dynamicBlocks } = lgv
143
201
 
144
202
  const ctx = canvas?.getContext('2d')
145
203
  if (!ctx) {
146
204
  return
147
205
  }
148
206
  ctx.clearRect(0, 0, dynamicBlocks.totalWidthPx, featuresHeight)
207
+
149
208
  for (const [idx, featureLayout] of featureLayouts.entries()) {
150
209
  const displayedRegion = displayedRegions[idx]
151
210
  for (const [row, featureLayoutRow] of featureLayout.entries()) {
@@ -171,6 +230,45 @@ export function renderingModelFactory(
171
230
  }
172
231
  }
173
232
  }
233
+
234
+ if (showStartCodons || showStopCodons) {
235
+ const { apolloDataStore } = session
236
+ for (const block of dynamicBlocks.contentBlocks) {
237
+ const assembly = apolloDataStore.assemblies.get(
238
+ block.assemblyName,
239
+ )
240
+ const ref = assembly?.getByRefName(block.refName)
241
+ const roundedStart = Math.floor(block.start)
242
+ const roundedEnd = Math.ceil(block.end)
243
+ let seq = ref?.getSequence(roundedStart, roundedEnd)
244
+ if (!seq) {
245
+ break
246
+ }
247
+ seq = seq.toUpperCase()
248
+ const baseOffsetPx = (block.start - roundedStart) / bpPerPx
249
+ const seqLeftPx = Math.round(
250
+ block.offsetPx - offsetPx - baseOffsetPx,
251
+ )
252
+ for (let i = 0; i < seq.length; i++) {
253
+ const bp = roundedStart + i
254
+ const codon = seq.slice(i, i + 3)
255
+ drawCodon(
256
+ ctx,
257
+ codon,
258
+ seqLeftPx,
259
+ i,
260
+ theme,
261
+ true,
262
+ bpPerPx,
263
+ bp,
264
+ apolloRowHeight,
265
+ showFeatureLabels,
266
+ showStartCodons,
267
+ showStopCodons,
268
+ )
269
+ }
270
+ }
271
+ }
174
272
  },
175
273
  { name: 'LinearApolloSixFrameDisplayRenderFeatures' },
176
274
  ),
@@ -200,7 +200,7 @@ export function AddAssembly({
200
200
  statusMessage: 'Pre-validating',
201
201
  progressPct: 0,
202
202
  cancelCallback: () => {
203
- controller.abort()
203
+ controller.abort('AddAssembly')
204
204
  jobsManager.abortJob(job.name)
205
205
  },
206
206
  }
@@ -182,7 +182,7 @@ export function ImportFeatures({
182
182
  statusMessage: 'Uploading file, this may take awhile',
183
183
  progressPct: 0,
184
184
  cancelCallback: () => {
185
- controller.abort()
185
+ controller.abort('ImportFeatures')
186
186
  jobsManager.abortJob(job.name)
187
187
  },
188
188
  }
@@ -93,7 +93,7 @@ export function OntologyTermAutocomplete({
93
93
  )
94
94
  }
95
95
  return () => {
96
- controller.abort()
96
+ controller.abort('OntologyTermAutocomplete matcher')
97
97
  }
98
98
  }, [session, valueString, filterTerms, ontologyStore, needToLoadCurrentTerm])
99
99
 
@@ -119,7 +119,7 @@ export function OntologyTermAutocomplete({
119
119
  )
120
120
  }
121
121
  return () => {
122
- controller.abort()
122
+ controller.abort('OntologyTermAutocomplete loader')
123
123
  }
124
124
  }, [
125
125
  needToLoadTermChoices,
@@ -89,7 +89,7 @@ function TermTagWithTooltip({
89
89
  })
90
90
 
91
91
  return () => {
92
- controller.abort()
92
+ controller.abort('TermTagWithTooltip ')
93
93
  }
94
94
  }, [termId, ontology, manager])
95
95
 
@@ -211,7 +211,7 @@ export function OntologyTermMultiSelect({
211
211
  })
212
212
 
213
213
  return () => {
214
- aborter.abort()
214
+ aborter.abort('OntologyTermMultiSelect')
215
215
  }
216
216
  }, [getOntologyTerms, ontology, includeDeprecated, inputValue, value])
217
217
 
@@ -97,7 +97,7 @@ const ResizeHandle = ({
97
97
  const controller = new AbortController()
98
98
  const { signal } = controller
99
99
  function abortDrag() {
100
- controller.abort()
100
+ controller.abort('makeDisplayComponent')
101
101
  }
102
102
  globalThis.addEventListener('mousemove', mouseMove, { signal })
103
103
  globalThis.addEventListener('mouseup', abortDrag, { signal })
@@ -204,7 +204,7 @@ export function clientDataStoreFactory(
204
204
  statusMessage: `Loading ontology "${name}", version "${version}", this may take a while`,
205
205
  progressPct: 0,
206
206
  cancelCallback: () => {
207
- controller.abort()
207
+ controller.abort('ClientDataStore')
208
208
  jobsManager.abortJob(job.name)
209
209
  },
210
210
  }
@@ -74,6 +74,7 @@ export function extendSession(
74
74
  apolloSelectedFeature: types.safeReference(AnnotationFeatureExtended),
75
75
  jobsManager: types.optional(ApolloJobModel, {}),
76
76
  isLocked: types.optional(types.boolean, false),
77
+ changeInProgress: types.optional(types.boolean, false),
77
78
  })
78
79
  .volatile(() => ({
79
80
  apolloHoveredFeature: undefined as HoveredFeature | undefined,
@@ -141,6 +142,9 @@ export function extendSession(
141
142
  toggleLocked() {
142
143
  self.isLocked = !self.isLocked
143
144
  },
145
+ setChangeInProgress(changeInProgress: boolean) {
146
+ self.changeInProgress = changeInProgress
147
+ },
144
148
  getPluginConfiguration() {
145
149
  const { jbrowse } = getRoot<ApolloRootModel>(self)
146
150
  const pluginConfiguration =
@@ -1,4 +1,5 @@
1
1
  import { type CheckResultIdsType } from '@apollo-annotation/mst'
2
+ import { type Theme } from '@mui/material'
2
3
  import { makeStyles } from 'tss-react/mui'
3
4
 
4
5
  export { default as EditZoomThresholdDialog } from '../components/EditZoomThresholdDialog'
@@ -147,3 +148,30 @@ export function clusterResultByMessage<
147
148
  )
148
149
  return clusters
149
150
  }
151
+
152
+ export function codonColorCode(
153
+ letter: string,
154
+ theme: Theme,
155
+ highContrast?: boolean,
156
+ ) {
157
+ if (letter === 'M') {
158
+ return theme.palette.startCodon
159
+ }
160
+ if (letter === '*') {
161
+ return highContrast ? theme.palette.text.primary : theme.palette.stopCodon
162
+ }
163
+ return
164
+ }
165
+
166
+ export function colorCode(letter: string, theme: Theme) {
167
+ const letterUpper = letter.toUpperCase()
168
+ if (
169
+ letterUpper === 'A' ||
170
+ letterUpper === 'C' ||
171
+ letterUpper === 'G' ||
172
+ letterUpper === 'T'
173
+ ) {
174
+ return theme.palette.bases[letterUpper].main.toString()
175
+ }
176
+ return 'lightgray'
177
+ }
@@ -7,6 +7,7 @@ import { type MenuItem } from '@jbrowse/core/ui'
7
7
  import {
8
8
  type AbstractSessionModel,
9
9
  getContainingView,
10
+ isSessionModelWithWidgets,
10
11
  } from '@jbrowse/core/util'
11
12
  import { type LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
12
13
  import SkipNextRoundedIcon from '@mui/icons-material/SkipNextRounded'
@@ -363,6 +364,23 @@ export function getContextMenuItemsForFeature(
363
364
  },
364
365
  },
365
366
  )
367
+ if (isSessionModelWithWidgets(session)) {
368
+ menuItems.push({
369
+ label: 'Open feature details',
370
+ onClick: () => {
371
+ const apolloGeneWidget = session.addWidget(
372
+ 'ApolloFeatureDetailsWidget',
373
+ 'apolloFeatureDetailsWidget',
374
+ {
375
+ feature: sourceFeature,
376
+ assembly: currentAssemblyId,
377
+ refName: region.refName,
378
+ },
379
+ )
380
+ session.showWidget(apolloGeneWidget)
381
+ },
382
+ })
383
+ }
366
384
  return menuItems
367
385
  }
368
386