@jbrowse/plugin-linear-genome-view 1.6.4 → 1.6.7

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/BaseLinearDisplay/components/LinearBlocks.d.ts +0 -5
  2. package/dist/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.d.ts +1 -1
  3. package/dist/LinearBareDisplay/configSchema.d.ts +1 -1
  4. package/dist/LinearBasicDisplay/configSchema.d.ts +1 -1
  5. package/dist/LinearGenomeView/components/ScaleBar.d.ts +2 -0
  6. package/dist/LinearGenomeView/components/TrackContainer.d.ts +1 -1
  7. package/dist/LinearGenomeView/index.d.ts +1 -0
  8. package/dist/index.d.ts +1 -1
  9. package/dist/plugin-linear-genome-view.cjs.development.js +147 -249
  10. package/dist/plugin-linear-genome-view.cjs.development.js.map +1 -1
  11. package/dist/plugin-linear-genome-view.cjs.production.min.js +1 -1
  12. package/dist/plugin-linear-genome-view.cjs.production.min.js.map +1 -1
  13. package/dist/plugin-linear-genome-view.esm.js +149 -251
  14. package/dist/plugin-linear-genome-view.esm.js.map +1 -1
  15. package/package.json +2 -2
  16. package/src/BaseLinearDisplay/components/BaseLinearDisplay.tsx +2 -4
  17. package/src/BaseLinearDisplay/components/LinearBlocks.tsx +4 -8
  18. package/src/BaseLinearDisplay/models/BaseLinearDisplayModel.tsx +11 -3
  19. package/src/LinearBasicDisplay/model.ts +1 -1
  20. package/src/LinearGenomeView/components/HelpDialog.tsx +14 -1
  21. package/src/LinearGenomeView/components/LinearGenomeViewSvg.tsx +1 -13
  22. package/src/LinearGenomeView/components/OverviewScaleBar.tsx +7 -3
  23. package/src/LinearGenomeView/components/TrackContainer.tsx +35 -38
  24. package/src/LinearGenomeView/components/TrackLabel.tsx +5 -5
  25. package/src/LinearGenomeView/components/TracksContainer.tsx +0 -1
  26. package/src/LinearGenomeView/components/__snapshots__/LinearGenomeView.test.js.snap +23 -26
  27. package/src/LinearGenomeView/index.test.ts +44 -42
  28. package/src/LinearGenomeView/index.tsx +167 -242
  29. package/src/LinearGenomeView/volvoxDisplayedRegions.json +16 -0
  30. package/dist/LinearGenomeView/components/ReturnToImportFormDialog.d.ts +0 -9
  31. package/src/LinearGenomeView/components/ReturnToImportFormDialog.tsx +0 -83
@@ -1,4 +1,4 @@
1
- import { getConf } from '@jbrowse/core/configuration'
1
+ import { getConf, AnyConfigurationModel } from '@jbrowse/core/configuration'
2
2
  import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes/models'
3
3
  import { Region } from '@jbrowse/core/util/types'
4
4
  import { ElementId, Region as MUIRegion } from '@jbrowse/core/util/types/mst'
@@ -35,7 +35,6 @@ import {
35
35
  import Base1DView from '@jbrowse/core/util/Base1DViewModel'
36
36
  import PluginManager from '@jbrowse/core/PluginManager'
37
37
  import clone from 'clone'
38
- import { AnyConfigurationModel } from '@jbrowse/core/configuration/configurationSchema'
39
38
  import { saveAs } from 'file-saver'
40
39
 
41
40
  // icons
@@ -53,7 +52,7 @@ import { renderToSvg } from './components/LinearGenomeViewSvg'
53
52
  import RefNameAutocomplete from './components/RefNameAutocomplete'
54
53
  import SearchBox from './components/SearchBox'
55
54
  import ExportSvgDlg from './components/ExportSvgDialog'
56
- import ReturnToImportFormDlg from './components/ReturnToImportFormDialog'
55
+ import { ReturnToImportFormDialog } from '@jbrowse/core/ui'
57
56
 
58
57
  export interface BpOffset {
59
58
  refName?: string
@@ -86,7 +85,7 @@ function calculateVisibleLocStrings(contentBlocks: BaseBlock[]) {
86
85
  assemblyName: isSingleAssemblyName ? undefined : block.assemblyName,
87
86
  }),
88
87
  )
89
- return locs.join(';')
88
+ return locs.join(' ')
90
89
  }
91
90
 
92
91
  export interface NavLocation {
@@ -138,11 +137,11 @@ export function stateModelFactory(pluginManager: PluginManager) {
138
137
  ),
139
138
  showCenterLine: types.optional(types.boolean, () => {
140
139
  const setting = localStorageGetItem('lgv-showCenterLine')
141
- return setting !== undefined ? !!setting : false
140
+ return setting !== undefined && setting !== null ? !!+setting : false
142
141
  }),
143
142
  showCytobandsSetting: types.optional(types.boolean, () => {
144
143
  const setting = localStorageGetItem('lgv-showCytobands')
145
- return setting !== undefined ? !!setting : true
144
+ return setting !== undefined && setting !== null ? !!+setting : true
146
145
  }),
147
146
  }),
148
147
  )
@@ -672,101 +671,63 @@ export function stateModelFactory(pluginManager: PluginManager) {
672
671
  },
673
672
 
674
673
  navToLocString(locString: string, optAssemblyName?: string) {
674
+ const { assemblyNames } = self
675
675
  const { assemblyManager } = getSession(self)
676
676
  const { isValidRefName } = assemblyManager
677
- const locStrings = locString.split(';')
678
- if (self.displayedRegions.length > 1) {
679
- const locations = locStrings.map(ls =>
680
- parseLocString(ls, isValidRefName),
681
- )
682
- this.navToMultiple(locations)
683
- return
684
- }
685
- let assemblyName = optAssemblyName
686
- let defaultRefName = ''
687
- if (self.displayedRegions.length !== 0) {
688
- // defaults
689
- assemblyName = self.displayedRegions[0].assemblyName
690
- defaultRefName = self.displayedRegions[0].refName
691
- }
692
- let assembly = assemblyName && assemblyManager.get(assemblyName)
693
- if (!assembly) {
694
- throw new Error(`Could not find assembly ${assemblyName}`)
695
- }
696
- let { regions } = assembly
697
- if (!regions) {
698
- throw new Error(`Regions for assembly ${assemblyName} not yet loaded`)
699
- }
700
- if (locStrings.length > 1) {
701
- throw new Error(
702
- 'Navigating to multiple locations is not allowed when viewing a whole chromosome',
703
- )
704
- }
705
- const parsedLocString = parseLocString(locStrings[0], refName =>
706
- isValidRefName(refName, assemblyName),
707
- )
708
- let changedAssembly = false
709
- if (
710
- parsedLocString.assemblyName &&
711
- parsedLocString.assemblyName !== assemblyName
712
- ) {
713
- const newAssembly = assemblyManager.get(parsedLocString.assemblyName)
714
- if (!newAssembly) {
715
- throw new Error(
716
- `Could not find assembly ${parsedLocString.assemblyName}`,
717
- )
677
+ const assemblyName = optAssemblyName || assemblyNames[0]
678
+
679
+ const parsedLocStrings = locString
680
+ .split(' ')
681
+ .filter(f => !!f.trim())
682
+ .map(l => parseLocString(l, ref => isValidRefName(ref, assemblyName)))
683
+
684
+ const locations = parsedLocStrings.map(region => {
685
+ const asmName = region.assemblyName || assemblyName
686
+ const asm = assemblyManager.get(asmName)
687
+ const { refName } = region
688
+ if (!asm) {
689
+ throw new Error(`assembly ${asmName} not found`)
718
690
  }
719
- assembly = newAssembly
720
- changedAssembly = true
721
- const newRegions = newAssembly.regions
722
- if (!newRegions) {
723
- throw new Error(
724
- `Regions for assembly ${parsedLocString.assemblyName} not yet loaded`,
725
- )
691
+ const { regions } = asm
692
+ if (!regions) {
693
+ throw new Error(`regions not loaded yet for ${asmName}`)
726
694
  }
727
- regions = newRegions
728
- }
729
- const canonicalRefName = assembly.getCanonicalRefName(
730
- parsedLocString.refName,
731
- )
732
-
733
- if (!canonicalRefName) {
734
- throw new Error(
735
- `Could not find refName ${parsedLocString.refName} in ${assembly.name}`,
736
- )
737
- }
738
- if (changedAssembly || canonicalRefName !== defaultRefName) {
739
- const newDisplayedRegion = regions.find(
695
+ const canonicalRefName = asm.getCanonicalRefName(region.refName)
696
+ if (!canonicalRefName) {
697
+ throw new Error(`Could not find refName ${refName} in ${asm.name}`)
698
+ }
699
+ const parentRegion = regions.find(
740
700
  region => region.refName === canonicalRefName,
741
701
  )
742
- if (newDisplayedRegion) {
743
- this.setDisplayedRegions([newDisplayedRegion])
744
- } else {
745
- throw new Error(
746
- `Could not find refName ${parsedLocString.refName} in ${assembly.name}`,
747
- )
702
+ if (!parentRegion) {
703
+ throw new Error(`Could not find refName ${refName} in ${asmName}`)
748
704
  }
749
- }
750
- const displayedRegion = regions.find(
751
- region => region.refName === canonicalRefName,
752
- )
753
- if (displayedRegion) {
754
- const start = clamp(
755
- parsedLocString?.start ?? 0,
756
- 0,
757
- displayedRegion.end,
758
- )
759
- const end = clamp(
760
- parsedLocString?.end ?? displayedRegion.end,
761
- 0,
762
- displayedRegion.end,
763
- )
705
+
706
+ return {
707
+ ...region,
708
+ assemblyName: asmName,
709
+ parentRegion,
710
+ }
711
+ })
712
+
713
+ if (locations.length === 1) {
714
+ const loc = locations[0]
715
+ this.setDisplayedRegions([
716
+ { reversed: loc.reversed, ...loc.parentRegion },
717
+ ])
718
+ const { start, end, parentRegion } = loc
764
719
 
765
720
  this.navTo({
766
- ...parsedLocString,
767
- start,
768
- end,
721
+ ...loc,
722
+ start: clamp(start ?? 0, 0, parentRegion.end),
723
+ end: clamp(end ?? parentRegion.end, 0, parentRegion.end),
769
724
  })
725
+ } else {
726
+ this.setDisplayedRegions(
727
+ // @ts-ignore
728
+ locations.map(r => (r.start === undefined ? r.parentRegion : r)),
729
+ )
730
+ this.showAllRegions()
770
731
  }
771
732
  },
772
733
 
@@ -889,43 +850,6 @@ export function stateModelFactory(pluginManager: PluginManager) {
889
850
  )} does not match with displayed regions`,
890
851
  )
891
852
  }
892
- if (locationIndex > 0) {
893
- // does it reach the left side?
894
- const matchesLeft = region.reversed
895
- ? locationEnd === region.end
896
- : locationStart === region.start
897
- if (!matchesLeft) {
898
- throw new Error(
899
- `${
900
- region.reversed ? 'End' : 'Start'
901
- } of region ${assembleLocString(
902
- location,
903
- )} should be ${(region.reversed
904
- ? region.end
905
- : region.start + 1
906
- ).toLocaleString('en-US')}, but it is not`,
907
- )
908
- }
909
- }
910
- const isLast = locationIndex === locations.length - 1
911
- if (!isLast) {
912
- // does it reach the right side?
913
- const matchesRight = region.reversed
914
- ? locationStart === region.start
915
- : locationEnd === region.end
916
- if (!matchesRight) {
917
- throw new Error(
918
- `${
919
- region.reversed ? 'Start' : 'End'
920
- } of region ${assembleLocString(
921
- location,
922
- )} should be ${(region.reversed
923
- ? region.start + 1
924
- : region.end
925
- ).toLocaleString('en-US')}, but it is not`,
926
- )
927
- }
928
- }
929
853
  }
930
854
  locationIndex -= 1
931
855
  const startDisplayedRegion = self.displayedRegions[index]
@@ -1220,127 +1144,128 @@ export function stateModelFactory(pluginManager: PluginManager) {
1220
1144
  : 0
1221
1145
  },
1222
1146
  }))
1223
- .views(self => {
1224
- let currentlyCalculatedStaticBlocks: BlockSet | undefined
1225
- let stringifiedCurrentlyCalculatedStaticBlocks = ''
1226
- return {
1227
- menuItems(): MenuItem[] {
1228
- const { canShowCytobands, showCytobands } = self
1229
-
1230
- const menuItems: MenuItem[] = [
1231
- {
1232
- label: 'Return to import form',
1233
- onClick: () => {
1234
- getSession(self).queueDialog((doneCallback: Function) => [
1235
- ReturnToImportFormDlg,
1236
- { model: self, handleClose: doneCallback },
1237
- ])
1238
- },
1239
- icon: FolderOpenIcon,
1240
- },
1241
- {
1242
- label: 'Export SVG',
1243
- icon: PhotoCameraIcon,
1244
- onClick: () => {
1245
- getSession(self).queueDialog((doneCallback: Function) => [
1246
- ExportSvgDlg,
1247
- { model: self, handleClose: doneCallback },
1248
- ])
1249
- },
1250
- },
1251
- {
1252
- label: 'Open track selector',
1253
- onClick: self.activateTrackSelector,
1254
- icon: TrackSelectorIcon,
1255
- },
1256
- {
1257
- label: 'Horizontally flip',
1258
- icon: SyncAltIcon,
1259
- onClick: self.horizontallyFlip,
1260
- },
1261
- { type: 'divider' },
1262
- {
1263
- label: 'Show all regions in assembly',
1264
- icon: VisibilityIcon,
1265
- onClick: self.showAllRegionsInAssembly,
1266
- },
1267
- {
1268
- label: 'Show center line',
1269
- icon: VisibilityIcon,
1270
- type: 'checkbox',
1271
- checked: self.showCenterLine,
1272
- onClick: self.toggleCenterLine,
1273
- },
1274
- {
1275
- label: 'Show header',
1276
- icon: VisibilityIcon,
1277
- type: 'checkbox',
1278
- checked: !self.hideHeader,
1279
- onClick: self.toggleHeader,
1147
+ .views(self => ({
1148
+ menuItems(): MenuItem[] {
1149
+ const { canShowCytobands, showCytobands } = self
1150
+
1151
+ const menuItems: MenuItem[] = [
1152
+ {
1153
+ label: 'Return to import form',
1154
+ onClick: () => {
1155
+ getSession(self).queueDialog(handleClose => [
1156
+ ReturnToImportFormDialog,
1157
+ { model: self, handleClose },
1158
+ ])
1280
1159
  },
1281
- {
1282
- label: 'Show header overview',
1283
- icon: VisibilityIcon,
1284
- type: 'checkbox',
1285
- checked: !self.hideHeaderOverview,
1286
- onClick: self.toggleHeaderOverview,
1287
- disabled: self.hideHeader,
1160
+ icon: FolderOpenIcon,
1161
+ },
1162
+ {
1163
+ label: 'Export SVG',
1164
+ icon: PhotoCameraIcon,
1165
+ onClick: () => {
1166
+ getSession(self).queueDialog(handleClose => [
1167
+ ExportSvgDlg,
1168
+ { model: self, handleClose },
1169
+ ])
1288
1170
  },
1289
- {
1290
- label: 'Track labels',
1291
- icon: LabelIcon,
1292
- subMenu: [
1293
- {
1294
- label: 'Overlapping',
1295
- icon: VisibilityIcon,
1296
- type: 'radio',
1297
- checked: self.trackLabels === 'overlapping',
1298
- onClick: () => self.setTrackLabels('overlapping'),
1299
- },
1300
- {
1301
- label: 'Offset',
1302
- icon: VisibilityIcon,
1303
- type: 'radio',
1304
- checked: self.trackLabels === 'offset',
1305
- onClick: () => self.setTrackLabels('offset'),
1306
- },
1171
+ },
1172
+ {
1173
+ label: 'Open track selector',
1174
+ onClick: self.activateTrackSelector,
1175
+ icon: TrackSelectorIcon,
1176
+ },
1177
+ {
1178
+ label: 'Horizontally flip',
1179
+ icon: SyncAltIcon,
1180
+ onClick: self.horizontallyFlip,
1181
+ },
1182
+ { type: 'divider' },
1183
+ {
1184
+ label: 'Show all regions in assembly',
1185
+ icon: VisibilityIcon,
1186
+ onClick: self.showAllRegionsInAssembly,
1187
+ },
1188
+ {
1189
+ label: 'Show center line',
1190
+ icon: VisibilityIcon,
1191
+ type: 'checkbox',
1192
+ checked: self.showCenterLine,
1193
+ onClick: self.toggleCenterLine,
1194
+ },
1195
+ {
1196
+ label: 'Show header',
1197
+ icon: VisibilityIcon,
1198
+ type: 'checkbox',
1199
+ checked: !self.hideHeader,
1200
+ onClick: self.toggleHeader,
1201
+ },
1202
+ {
1203
+ label: 'Show header overview',
1204
+ icon: VisibilityIcon,
1205
+ type: 'checkbox',
1206
+ checked: !self.hideHeaderOverview,
1207
+ onClick: self.toggleHeaderOverview,
1208
+ disabled: self.hideHeader,
1209
+ },
1210
+ {
1211
+ label: 'Track labels',
1212
+ icon: LabelIcon,
1213
+ subMenu: [
1214
+ {
1215
+ label: 'Overlapping',
1216
+ icon: VisibilityIcon,
1217
+ type: 'radio',
1218
+ checked: self.trackLabels === 'overlapping',
1219
+ onClick: () => self.setTrackLabels('overlapping'),
1220
+ },
1221
+ {
1222
+ label: 'Offset',
1223
+ icon: VisibilityIcon,
1224
+ type: 'radio',
1225
+ checked: self.trackLabels === 'offset',
1226
+ onClick: () => self.setTrackLabels('offset'),
1227
+ },
1228
+ {
1229
+ label: 'Hidden',
1230
+ icon: VisibilityIcon,
1231
+ type: 'radio',
1232
+ checked: self.trackLabels === 'hidden',
1233
+ onClick: () => self.setTrackLabels('hidden'),
1234
+ },
1235
+ ],
1236
+ },
1237
+ ...(canShowCytobands
1238
+ ? [
1307
1239
  {
1308
- label: 'Hidden',
1309
- icon: VisibilityIcon,
1310
- type: 'radio',
1311
- checked: self.trackLabels === 'hidden',
1312
- onClick: () => self.setTrackLabels('hidden'),
1313
- },
1314
- ],
1315
- },
1316
- ...(canShowCytobands
1317
- ? [
1318
- {
1319
- label: showCytobands ? 'Hide ideogram' : 'Show ideograms',
1320
- onClick: () => {
1321
- self.setShowCytobands(!showCytobands)
1322
- },
1240
+ label: showCytobands ? 'Hide ideogram' : 'Show ideograms',
1241
+ onClick: () => {
1242
+ self.setShowCytobands(!showCytobands)
1323
1243
  },
1324
- ]
1325
- : []),
1326
- ]
1244
+ },
1245
+ ]
1246
+ : []),
1247
+ ]
1327
1248
 
1328
- // add track's view level menu options
1329
- for (const [key, value] of self.trackTypeActions.entries()) {
1330
- if (value.length) {
1331
- menuItems.push(
1332
- { type: 'divider' },
1333
- { type: 'subHeader', label: key },
1334
- )
1335
- value.forEach(action => {
1336
- menuItems.push(action)
1337
- })
1338
- }
1249
+ // add track's view level menu options
1250
+ for (const [key, value] of self.trackTypeActions.entries()) {
1251
+ if (value.length) {
1252
+ menuItems.push(
1253
+ { type: 'divider' },
1254
+ { type: 'subHeader', label: key },
1255
+ )
1256
+ value.forEach(action => {
1257
+ menuItems.push(action)
1258
+ })
1339
1259
  }
1260
+ }
1340
1261
 
1341
- return menuItems
1342
- },
1343
-
1262
+ return menuItems
1263
+ },
1264
+ }))
1265
+ .views(self => {
1266
+ let currentlyCalculatedStaticBlocks: BlockSet | undefined
1267
+ let stringifiedCurrentlyCalculatedStaticBlocks = ''
1268
+ return {
1344
1269
  get staticBlocks() {
1345
1270
  const ret = calculateStaticBlocks(self)
1346
1271
  const sret = JSON.stringify(ret)
@@ -0,0 +1,16 @@
1
+ [
2
+ {
3
+ "assemblyName": "volvox",
4
+ "start": 0,
5
+ "end": 50001,
6
+ "refName": "ctgA",
7
+ "reversed": false
8
+ },
9
+ {
10
+ "assemblyName": "volvox",
11
+ "start": 0,
12
+ "end": 6079,
13
+ "refName": "ctgB",
14
+ "reversed": false
15
+ }
16
+ ]
@@ -1,9 +0,0 @@
1
- /// <reference types="react" />
2
- declare function ReturnToImportFormDialog({ model, handleClose, }: {
3
- model: {
4
- clearView: Function;
5
- };
6
- handleClose: () => void;
7
- }): JSX.Element;
8
- declare const _default: typeof ReturnToImportFormDialog;
9
- export default _default;
@@ -1,83 +0,0 @@
1
- import React from 'react'
2
- import { observer } from 'mobx-react'
3
- import { makeStyles } from '@material-ui/core/styles'
4
- import {
5
- Button,
6
- Dialog,
7
- DialogActions,
8
- DialogContent,
9
- DialogTitle,
10
- Divider,
11
- IconButton,
12
- Typography,
13
- } from '@material-ui/core'
14
- import CloseIcon from '@material-ui/icons/Close'
15
-
16
- const useStyles = makeStyles(theme => ({
17
- closeButton: {
18
- position: 'absolute',
19
- right: theme.spacing(1),
20
- top: theme.spacing(1),
21
- color: theme.palette.grey[500],
22
- },
23
- }))
24
-
25
- function ReturnToImportFormDialog({
26
- model,
27
- handleClose,
28
- }: {
29
- model: { clearView: Function }
30
- handleClose: () => void
31
- }) {
32
- const classes = useStyles()
33
- return (
34
- <Dialog maxWidth="xl" open onClose={handleClose}>
35
- <DialogTitle>
36
- Reference sequence
37
- {handleClose ? (
38
- <IconButton
39
- className={classes.closeButton}
40
- onClick={() => {
41
- handleClose()
42
- }}
43
- >
44
- <CloseIcon />
45
- </IconButton>
46
- ) : null}
47
- </DialogTitle>
48
- <Divider />
49
-
50
- <DialogContent>
51
- <Typography>
52
- Are you sure you want to return to the import form? This will lose
53
- your current view
54
- </Typography>
55
- </DialogContent>
56
- <DialogActions>
57
- <Button
58
- onClick={() => {
59
- model.clearView()
60
- handleClose()
61
- }}
62
- variant="contained"
63
- color="primary"
64
- autoFocus
65
- >
66
- OK
67
- </Button>
68
- <Button
69
- onClick={() => {
70
- handleClose()
71
- }}
72
- color="secondary"
73
- variant="contained"
74
- autoFocus
75
- >
76
- Cancel
77
- </Button>
78
- </DialogActions>
79
- </Dialog>
80
- )
81
- }
82
-
83
- export default observer(ReturnToImportFormDialog)