react-msaview 3.1.5 → 3.1.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 (40) hide show
  1. package/bundle/index.js +30 -39
  2. package/dist/DataModel.d.ts +34 -0
  3. package/dist/DataModel.js +46 -0
  4. package/dist/DataModel.js.map +1 -0
  5. package/dist/SelectedStructuresMixin.d.ts +46 -0
  6. package/dist/SelectedStructuresMixin.js +52 -0
  7. package/dist/SelectedStructuresMixin.js.map +1 -0
  8. package/dist/components/MSAPanel/MSABlock.js +32 -16
  9. package/dist/components/MSAPanel/MSABlock.js.map +1 -1
  10. package/dist/components/MSAPanel/renderMSAMouseover.js +14 -4
  11. package/dist/components/MSAPanel/renderMSAMouseover.js.map +1 -1
  12. package/dist/components/TreePanel/TreeNodeMenu.js +65 -55
  13. package/dist/components/TreePanel/TreeNodeMenu.js.map +1 -1
  14. package/dist/components/TreePanel/renderTreeCanvas.js +4 -3
  15. package/dist/components/TreePanel/renderTreeCanvas.js.map +1 -1
  16. package/dist/components/dialogs/SettingsDialog.js +41 -31
  17. package/dist/components/dialogs/SettingsDialog.js.map +1 -1
  18. package/dist/measureTextCanvas.d.ts +1 -1
  19. package/dist/measureTextCanvas.js +1 -1
  20. package/dist/measureTextCanvas.js.map +1 -1
  21. package/dist/model.d.ts +113 -108
  22. package/dist/model.js +61 -93
  23. package/dist/model.js.map +1 -1
  24. package/dist/util.d.ts +0 -1
  25. package/dist/util.js +0 -7
  26. package/dist/util.js.map +1 -1
  27. package/dist/version.d.ts +1 -1
  28. package/dist/version.js +1 -1
  29. package/package.json +1 -2
  30. package/src/DataModel.ts +46 -0
  31. package/src/SelectedStructuresMixin.ts +59 -0
  32. package/src/components/MSAPanel/MSABlock.tsx +33 -18
  33. package/src/components/MSAPanel/renderMSAMouseover.ts +16 -3
  34. package/src/components/TreePanel/TreeNodeMenu.tsx +93 -84
  35. package/src/components/TreePanel/renderTreeCanvas.ts +3 -2
  36. package/src/components/dialogs/SettingsDialog.tsx +128 -116
  37. package/src/measureTextCanvas.ts +1 -1
  38. package/src/model.ts +72 -109
  39. package/src/util.ts +0 -11
  40. package/src/version.ts +1 -1
@@ -1,5 +1,8 @@
1
1
  import { MsaViewModel } from '../../model'
2
2
 
3
+ const hoverColor = 'rgba(0,0,0,0.15)'
4
+ const highlightColor = 'rgba(128,128,0,0.2)'
5
+
3
6
  export function renderMouseover({
4
7
  ctx,
5
8
  model,
@@ -18,19 +21,29 @@ export function renderMouseover({
18
21
  mouseRow,
19
22
  // @ts-expect-error
20
23
  mouseCol2,
24
+ mouseClickRow,
25
+ mouseClickCol,
21
26
  } = model
22
27
  ctx.resetTransform()
23
28
  ctx.clearRect(0, 0, width, height)
24
29
  if (mouseCol !== undefined) {
25
- ctx.fillStyle = 'rgba(0,0,0,0.15)'
30
+ ctx.fillStyle = hoverColor
26
31
  ctx.fillRect((mouseCol - 1) * colWidth + scrollX, 0, colWidth, height)
27
32
  }
28
33
  if (mouseRow !== undefined) {
29
- ctx.fillStyle = 'rgba(0,0,0,0.15)'
34
+ ctx.fillStyle = hoverColor
30
35
  ctx.fillRect(0, mouseRow * rowHeight + scrollY, width, rowHeight)
31
36
  }
37
+ if (mouseClickCol !== undefined) {
38
+ ctx.fillStyle = highlightColor
39
+ ctx.fillRect((mouseClickCol - 1) * colWidth + scrollX, 0, colWidth, height)
40
+ }
41
+ if (mouseClickRow !== undefined) {
42
+ ctx.fillStyle = highlightColor
43
+ ctx.fillRect(0, mouseClickRow * rowHeight + scrollY, width, rowHeight)
44
+ }
32
45
  if (mouseCol2 !== undefined) {
33
- ctx.fillStyle = 'rgba(255,255,0,0.2)'
46
+ ctx.fillStyle = highlightColor
34
47
  ctx.fillRect((mouseCol2 - 1) * colWidth + scrollX, 0, colWidth, height)
35
48
  }
36
49
  }
@@ -5,6 +5,7 @@ import { observer } from 'mobx-react'
5
5
  // locals
6
6
  import { MsaViewModel } from '../../model'
7
7
 
8
+ // lazies
8
9
  const TreeNodeInfoDialog = lazy(() => import('./dialogs/TreeNodeInfoDialog'))
9
10
 
10
11
  const TreeMenu = observer(function ({
@@ -16,104 +17,112 @@ const TreeMenu = observer(function ({
16
17
  model: MsaViewModel
17
18
  onClose: () => void
18
19
  }) {
19
- const { structures } = model
20
+ const { selectedStructures, collapsed, collapsed2, structures } = model
20
21
  const nodeDetails = node ? model.getRowData(node.name) : undefined
21
22
 
22
23
  return (
23
- <>
24
- <Menu
25
- anchorReference="anchorPosition"
26
- anchorPosition={{
27
- top: node.y,
28
- left: node.x,
24
+ <Menu
25
+ anchorReference="anchorPosition"
26
+ anchorPosition={{
27
+ top: node.y,
28
+ left: node.x,
29
+ }}
30
+ transitionDuration={0}
31
+ keepMounted
32
+ open={Boolean(node)}
33
+ onClose={onClose}
34
+ >
35
+ <MenuItem dense disabled>
36
+ {node.name}
37
+ </MenuItem>
38
+
39
+ <MenuItem
40
+ dense
41
+ onClick={() => {
42
+ model.queueDialog(onClose => [
43
+ TreeNodeInfoDialog,
44
+ {
45
+ info: model.getRowData(node.name),
46
+ model,
47
+ nodeName: node.name,
48
+ onClose,
49
+ },
50
+ ])
51
+ onClose()
29
52
  }}
30
- transitionDuration={0}
31
- keepMounted
32
- open={Boolean(node)}
33
- onClose={onClose}
34
53
  >
35
- <MenuItem dense disabled>
36
- {node.name}
37
- </MenuItem>
38
-
39
- <MenuItem
40
- dense
41
- onClick={() => {
42
- model.queueDialog(onClose => [
43
- TreeNodeInfoDialog,
44
- {
45
- info: model.getRowData(node.name),
46
- model,
47
- nodeName: node.name,
48
- onClose,
49
- },
50
- ])
51
- onClose()
52
- }}
53
- >
54
- More info...
55
- </MenuItem>
56
- <MenuItem
57
- dense
58
- onClick={() => {
59
- model.hideNode(node.id)
60
- onClose()
61
- }}
62
- >
63
- Hide node
64
- </MenuItem>
65
-
66
- {structures[node.name]?.map(entry => {
67
- return !model.selectedStructures.some(n => n.id === node.name) ? (
68
- <MenuItem
69
- key={JSON.stringify(entry)}
70
- dense
71
- onClick={() => {
72
- model.addStructureToSelection({
73
- structure: entry,
74
- id: node.name,
75
- })
76
- onClose()
77
- }}
78
- >
79
- Add PDB to selection ({entry.pdb})
80
- </MenuItem>
81
- ) : (
82
- <MenuItem
83
- key={JSON.stringify(entry)}
84
- dense
85
- onClick={() => {
86
- model.removeStructureFromSelection({
87
- structure: entry,
88
- id: node.name,
89
- })
90
- onClose()
91
- }}
92
- >
93
- Remove PDB from selection ({entry.pdb})
94
- </MenuItem>
95
- )
96
- })}
54
+ More info...
55
+ </MenuItem>
56
+ <MenuItem
57
+ dense
58
+ onClick={() => {
59
+ if (collapsed.includes(node.id)) {
60
+ model.toggleCollapsed(node.id)
61
+ } else {
62
+ if (node.id.endsWith('-leafnode')) {
63
+ model.toggleCollapsed2(`${node.id}`)
64
+ } else {
65
+ model.toggleCollapsed2(`${node.id}-leafnode`)
66
+ }
67
+ }
68
+ onClose()
69
+ }}
70
+ >
71
+ {collapsed.includes(node.id) || collapsed2.includes(node.id)
72
+ ? 'Show node'
73
+ : 'Hide node'}
74
+ </MenuItem>
97
75
 
98
- {// @ts-expect-error
99
- nodeDetails?.data.accession?.map(accession => (
76
+ {structures[node.name]?.map(entry =>
77
+ !selectedStructures.some(n => n.id === node.name) ? (
78
+ <MenuItem
79
+ key={JSON.stringify(entry)}
80
+ dense
81
+ onClick={() => {
82
+ model.addStructureToSelection({
83
+ structure: entry,
84
+ id: node.name,
85
+ })
86
+ onClose()
87
+ }}
88
+ >
89
+ Add PDB to selection ({entry.pdb})
90
+ </MenuItem>
91
+ ) : (
100
92
  <MenuItem
93
+ key={JSON.stringify(entry)}
101
94
  dense
102
- key={accession}
103
95
  onClick={() => {
104
- model.addUniprotTrack({
105
- // @ts-expect-error
106
- name: nodeDetails?.data.name,
107
- accession,
96
+ model.removeStructureFromSelection({
97
+ structure: entry,
98
+ id: node.name,
108
99
  })
109
100
  onClose()
110
101
  }}
111
102
  >
112
- Open UniProt track ({accession})
103
+ Remove PDB from selection ({entry.pdb})
113
104
  </MenuItem>
114
- ))}
115
- </Menu>
116
- </>
105
+ ),
106
+ )}
107
+
108
+ {// @ts-expect-error
109
+ nodeDetails?.data.accession?.map(accession => (
110
+ <MenuItem
111
+ dense
112
+ key={accession}
113
+ onClick={() => {
114
+ model.addUniprotTrack({
115
+ // @ts-expect-error
116
+ name: nodeDetails?.data.name,
117
+ accession,
118
+ })
119
+ onClose()
120
+ }}
121
+ >
122
+ Open UniProt track ({accession})
123
+ </MenuItem>
124
+ ))}
125
+ </Menu>
117
126
  )
118
127
  })
119
128
 
@@ -90,6 +90,7 @@ export function renderNodeBubbles({
90
90
  } = node
91
91
  const { branchset, id = '', name = '' } = data
92
92
  if (
93
+ !id.endsWith('-leafnode') &&
93
94
  branchset.length &&
94
95
  y > offsetY - extendBounds &&
95
96
  y < offsetY + by + extendBounds
@@ -195,8 +196,8 @@ export function renderTreeLabels({
195
196
  }
196
197
  ctx.fillText(displayName, offset, yp)
197
198
  clickMap?.insert({
198
- minX: treeAreaWidth - width + ml,
199
- maxX: treeAreaWidth + ml,
199
+ minX: treeAreaWidth - width,
200
+ maxX: treeAreaWidth,
200
201
  minY: yp - height,
201
202
  maxY: yp,
202
203
  name,
@@ -35,140 +35,152 @@ function FormControlLabel2(rest: FormControlLabelProps) {
35
35
  )
36
36
  }
37
37
 
38
+ function Checkbox2({
39
+ checked,
40
+ label,
41
+ onChange,
42
+ }: {
43
+ checked: boolean
44
+ label: string
45
+ onChange: () => void
46
+ }) {
47
+ return (
48
+ <FormControlLabel2
49
+ control={<Checkbox checked={checked} onChange={onChange} />}
50
+ label={label}
51
+ />
52
+ )
53
+ }
54
+
38
55
  const SettingsContent = observer(function ({ model }: { model: MsaViewModel }) {
56
+ return (
57
+ <>
58
+ <TreeSettings model={model} />
59
+ <MSASettings model={model} />
60
+ </>
61
+ )
62
+ })
63
+
64
+ const TreeSettings = observer(function TreeSettings({
65
+ model,
66
+ }: {
67
+ model: MsaViewModel
68
+ }) {
39
69
  const { classes } = useStyles()
40
70
  const {
41
- bgColor,
42
- colWidth,
43
- colorSchemeName,
44
71
  drawTree,
45
72
  drawNodeBubbles,
46
73
  labelsAlignRight,
47
74
  noTree,
48
- rowHeight,
49
75
  showBranchLen,
50
76
  treeWidthMatchesArea,
51
77
  treeWidth,
52
78
  } = model
79
+
53
80
  return (
54
- <>
55
- <div>
56
- <Button onClick={() => model.clearHidden()}>Clear hidden</Button>
57
- <h1>Tree options</h1>
58
- <FormControlLabel2
59
- control={
60
- <Checkbox
61
- checked={showBranchLen}
62
- onChange={() => model.setShowBranchLen(!showBranchLen)}
63
- />
64
- }
65
- label="Show branch length?"
66
- />
81
+ <div>
82
+ <h1>Tree options</h1>
83
+ <Checkbox2
84
+ checked={showBranchLen}
85
+ onChange={() => model.setShowBranchLen(!showBranchLen)}
86
+ label="Show branch length?"
87
+ />
67
88
 
68
- <FormControlLabel2
69
- control={
70
- <Checkbox
71
- checked={drawNodeBubbles}
72
- onChange={() => model.setDrawNodeBubbles(!drawNodeBubbles)}
73
- />
74
- }
75
- label="Draw clickable bubbles on tree branches?"
76
- />
77
- <FormControlLabel2
78
- control={
79
- <Checkbox
80
- checked={drawTree}
81
- onChange={() => model.setDrawTree(!drawTree)}
82
- />
83
- }
84
- label="Show tree?"
85
- />
89
+ <Checkbox2
90
+ checked={drawNodeBubbles}
91
+ onChange={() => model.setDrawNodeBubbles(!drawNodeBubbles)}
92
+ label="Draw clickable bubbles on tree branches?"
93
+ />
94
+ <Checkbox2
95
+ checked={drawTree}
96
+ onChange={() => model.setDrawTree(!drawTree)}
97
+ label="Show tree?"
98
+ />
86
99
 
87
- <FormControlLabel2
88
- control={
89
- <Checkbox
90
- checked={labelsAlignRight}
91
- onChange={() => model.setLabelsAlignRight(!labelsAlignRight)}
92
- />
93
- }
94
- label="Tree labels align right?"
95
- />
96
- {!noTree ? (
97
- <div>
98
- <FormControlLabel2
99
- control={
100
- <Checkbox
101
- checked={treeWidthMatchesArea}
102
- onChange={() =>
103
- model.setTreeWidthMatchesArea(!treeWidthMatchesArea)
104
- }
105
- />
106
- }
107
- label="Make tree width fit to tree area?"
108
- />
109
- {!treeWidthMatchesArea ? (
110
- <div className={classes.flex}>
111
- <Typography>Tree width ({treeWidth}px)</Typography>
112
- <Slider
113
- className={classes.field}
114
- min={50}
115
- max={600}
116
- value={treeWidth}
117
- onChange={(_, val) => model.setTreeWidth(val as number)}
118
- />
119
- </div>
120
- ) : null}
121
- </div>
122
- ) : null}
123
- </div>
124
- <div>
125
- <h1>MSA options</h1>
126
-
127
- <FormControlLabel2
128
- control={
129
- <Checkbox
130
- checked={bgColor}
131
- onChange={() => model.setBgColor(!bgColor)}
132
- />
133
- }
134
- label="Color background tiles of MSA?"
135
- />
136
-
137
- <div className={classes.flex}>
138
- <Typography>Column width ({colWidth}px)</Typography>
139
- <Slider
140
- className={classes.field}
141
- min={1}
142
- max={50}
143
- value={colWidth}
144
- onChange={(_, val) => model.setColWidth(val as number)}
145
- />
146
- </div>
147
- <div className={classes.flex}>
148
- <Typography>Row height ({rowHeight}px)</Typography>
149
- <Slider
150
- className={classes.field}
151
- min={1}
152
- max={50}
153
- value={rowHeight}
154
- onChange={(_, val) => model.setRowHeight(val as number)}
100
+ <Checkbox2
101
+ checked={labelsAlignRight}
102
+ onChange={() => model.setLabelsAlignRight(!labelsAlignRight)}
103
+ label="Tree labels align right?"
104
+ />
105
+ {!noTree ? (
106
+ <div>
107
+ <Checkbox2
108
+ checked={treeWidthMatchesArea}
109
+ onChange={() =>
110
+ model.setTreeWidthMatchesArea(!treeWidthMatchesArea)
111
+ }
112
+ label="Make tree width fit to tree area?"
155
113
  />
114
+ {!treeWidthMatchesArea ? (
115
+ <div className={classes.flex}>
116
+ <Typography>Tree width ({treeWidth}px)</Typography>
117
+ <Slider
118
+ className={classes.field}
119
+ min={50}
120
+ max={600}
121
+ value={treeWidth}
122
+ onChange={(_, val) => model.setTreeWidth(val as number)}
123
+ />
124
+ </div>
125
+ ) : null}
156
126
  </div>
127
+ ) : null}
128
+ </div>
129
+ )
130
+ })
157
131
 
158
- <TextField
159
- select
160
- label="Color scheme"
161
- value={colorSchemeName}
162
- onChange={event => model.setColorSchemeName(event.target.value)}
163
- >
164
- {Object.keys(colorSchemes).map(option => (
165
- <MenuItem key={option} value={option}>
166
- {option}
167
- </MenuItem>
168
- ))}
169
- </TextField>
132
+ const MSASettings = observer(function MSASettings({
133
+ model,
134
+ }: {
135
+ model: MsaViewModel
136
+ }) {
137
+ const { classes } = useStyles()
138
+ const { bgColor, colWidth, colorSchemeName, rowHeight } = model
139
+
140
+ return (
141
+ <div>
142
+ <h1>MSA options</h1>
143
+
144
+ <Checkbox2
145
+ checked={bgColor}
146
+ onChange={() => model.setBgColor(!bgColor)}
147
+ label="Color background tiles of MSA?"
148
+ />
149
+
150
+ <div className={classes.flex}>
151
+ <Typography>Column width ({colWidth}px)</Typography>
152
+ <Slider
153
+ className={classes.field}
154
+ min={1}
155
+ max={50}
156
+ value={colWidth}
157
+ onChange={(_, val) => model.setColWidth(val as number)}
158
+ />
170
159
  </div>
171
- </>
160
+ <div className={classes.flex}>
161
+ <Typography>Row height ({rowHeight}px)</Typography>
162
+ <Slider
163
+ className={classes.field}
164
+ min={1}
165
+ max={50}
166
+ value={rowHeight}
167
+ onChange={(_, val) => model.setRowHeight(val as number)}
168
+ />
169
+ </div>
170
+
171
+ <TextField
172
+ select
173
+ label="Color scheme"
174
+ value={colorSchemeName}
175
+ onChange={event => model.setColorSchemeName(event.target.value)}
176
+ >
177
+ {Object.keys(colorSchemes).map(option => (
178
+ <MenuItem key={option} value={option}>
179
+ {option}
180
+ </MenuItem>
181
+ ))}
182
+ </TextField>
183
+ </div>
172
184
  )
173
185
  })
174
186
 
@@ -1,6 +1,6 @@
1
1
  let canvasHandle: HTMLCanvasElement | undefined
2
2
 
3
- export default function measureTextCanvas(text: string, fontSize: number) {
3
+ export function measureTextCanvas(text: string, fontSize: number) {
4
4
  if (!canvasHandle) {
5
5
  canvasHandle = document.createElement('canvas')
6
6
  }