@jbrowse/plugin-config 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.
- package/dist/ConfigurationEditorWidget/components/BooleanEditor.d.ts +10 -0
- package/dist/ConfigurationEditorWidget/components/BooleanEditor.js +14 -0
- package/dist/ConfigurationEditorWidget/components/BooleanEditor.js.map +1 -0
- package/dist/ConfigurationEditorWidget/components/ConfigurationEditor.js +25 -22
- package/dist/ConfigurationEditorWidget/components/ConfigurationEditor.js.map +1 -1
- package/dist/ConfigurationEditorWidget/components/ConfigurationTextField.d.ts +5 -0
- package/dist/ConfigurationEditorWidget/components/ConfigurationTextField.js +19 -0
- package/dist/ConfigurationEditorWidget/components/ConfigurationTextField.js.map +1 -0
- package/dist/ConfigurationEditorWidget/components/NumberEditor.d.ts +11 -0
- package/dist/ConfigurationEditorWidget/components/NumberEditor.js +46 -0
- package/dist/ConfigurationEditorWidget/components/NumberEditor.js.map +1 -0
- package/dist/ConfigurationEditorWidget/components/NumberMapEditor.d.ts +11 -0
- package/dist/ConfigurationEditorWidget/components/NumberMapEditor.js +66 -0
- package/dist/ConfigurationEditorWidget/components/NumberMapEditor.js.map +1 -0
- package/dist/ConfigurationEditorWidget/components/SlotEditor.js +15 -97
- package/dist/ConfigurationEditorWidget/components/SlotEditor.js.map +1 -1
- package/dist/ConfigurationEditorWidget/components/StringArrayEditor.js +1 -1
- package/dist/ConfigurationEditorWidget/components/StringArrayEditor.js.map +1 -1
- package/dist/ConfigurationEditorWidget/components/StringArrayMapEditor.d.ts +14 -0
- package/dist/ConfigurationEditorWidget/components/StringArrayMapEditor.js +71 -0
- package/dist/ConfigurationEditorWidget/components/StringArrayMapEditor.js.map +1 -0
- package/dist/RefNameAliasAdapter/RefNameAliasAdapter.js +3 -4
- package/dist/RefNameAliasAdapter/RefNameAliasAdapter.js.map +1 -1
- package/esm/ConfigurationEditorWidget/components/BooleanEditor.d.ts +10 -0
- package/esm/ConfigurationEditorWidget/components/BooleanEditor.js +9 -0
- package/esm/ConfigurationEditorWidget/components/BooleanEditor.js.map +1 -0
- package/esm/ConfigurationEditorWidget/components/ConfigurationEditor.js +25 -22
- package/esm/ConfigurationEditorWidget/components/ConfigurationEditor.js.map +1 -1
- package/esm/ConfigurationEditorWidget/components/ConfigurationTextField.d.ts +5 -0
- package/esm/ConfigurationEditorWidget/components/ConfigurationTextField.js +13 -0
- package/esm/ConfigurationEditorWidget/components/ConfigurationTextField.js.map +1 -0
- package/esm/ConfigurationEditorWidget/components/NumberEditor.d.ts +11 -0
- package/esm/ConfigurationEditorWidget/components/NumberEditor.js +18 -0
- package/esm/ConfigurationEditorWidget/components/NumberEditor.js.map +1 -0
- package/esm/ConfigurationEditorWidget/components/NumberMapEditor.d.ts +11 -0
- package/esm/ConfigurationEditorWidget/components/NumberMapEditor.js +38 -0
- package/esm/ConfigurationEditorWidget/components/NumberMapEditor.js.map +1 -0
- package/esm/ConfigurationEditorWidget/components/SlotEditor.js +13 -95
- package/esm/ConfigurationEditorWidget/components/SlotEditor.js.map +1 -1
- package/esm/ConfigurationEditorWidget/components/StringArrayEditor.js +1 -1
- package/esm/ConfigurationEditorWidget/components/StringArrayEditor.js.map +1 -1
- package/esm/ConfigurationEditorWidget/components/StringArrayMapEditor.d.ts +14 -0
- package/esm/ConfigurationEditorWidget/components/StringArrayMapEditor.js +43 -0
- package/esm/ConfigurationEditorWidget/components/StringArrayMapEditor.js.map +1 -0
- package/esm/RefNameAliasAdapter/RefNameAliasAdapter.js +3 -4
- package/esm/RefNameAliasAdapter/RefNameAliasAdapter.js.map +1 -1
- package/package.json +2 -2
- package/src/ConfigurationEditorWidget/components/BooleanEditor.tsx +35 -0
- package/src/ConfigurationEditorWidget/components/ConfigurationEditor.tsx +116 -123
- package/src/ConfigurationEditorWidget/components/ConfigurationTextField.tsx +22 -0
- package/src/ConfigurationEditorWidget/components/NumberEditor.tsx +34 -0
- package/src/ConfigurationEditorWidget/components/NumberMapEditor.tsx +94 -0
- package/src/ConfigurationEditorWidget/components/SlotEditor.tsx +57 -304
- package/src/ConfigurationEditorWidget/components/StringArrayEditor.tsx +1 -4
- package/src/ConfigurationEditorWidget/components/StringArrayMapEditor.tsx +104 -0
- package/src/ConfigurationEditorWidget/components/__snapshots__/ConfigurationEditor.test.tsx.snap +113 -58
- package/src/RefNameAliasAdapter/RefNameAliasAdapter.ts +3 -4
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
Checkbox,
|
|
6
|
+
FormControl,
|
|
7
|
+
FormControlLabel,
|
|
8
|
+
FormHelperText,
|
|
9
|
+
} from '@mui/material'
|
|
10
|
+
|
|
11
|
+
export default observer(function ({
|
|
12
|
+
slot,
|
|
13
|
+
}: {
|
|
14
|
+
slot: {
|
|
15
|
+
name: string
|
|
16
|
+
value: boolean
|
|
17
|
+
set: (arg: boolean) => void
|
|
18
|
+
description: string
|
|
19
|
+
}
|
|
20
|
+
}) {
|
|
21
|
+
return (
|
|
22
|
+
<FormControl>
|
|
23
|
+
<FormControlLabel
|
|
24
|
+
label={slot.name}
|
|
25
|
+
control={
|
|
26
|
+
<Checkbox
|
|
27
|
+
checked={slot.value}
|
|
28
|
+
onChange={evt => slot.set(evt.target.checked)}
|
|
29
|
+
/>
|
|
30
|
+
}
|
|
31
|
+
/>
|
|
32
|
+
<FormHelperText>{slot.description}</FormHelperText>
|
|
33
|
+
</FormControl>
|
|
34
|
+
)
|
|
35
|
+
})
|
|
@@ -28,7 +28,7 @@ import { AbstractSessionModel } from '@jbrowse/core/util'
|
|
|
28
28
|
|
|
29
29
|
const useStyles = makeStyles()(theme => ({
|
|
30
30
|
expandIcon: {
|
|
31
|
-
color: '#fff',
|
|
31
|
+
color: theme.palette.tertiary?.contrastText || '#fff',
|
|
32
32
|
},
|
|
33
33
|
root: {
|
|
34
34
|
padding: theme.spacing(1, 3, 1, 1),
|
|
@@ -37,7 +37,6 @@ const useStyles = makeStyles()(theme => ({
|
|
|
37
37
|
display: 'block',
|
|
38
38
|
padding: theme.spacing(1),
|
|
39
39
|
},
|
|
40
|
-
|
|
41
40
|
accordion: {
|
|
42
41
|
border: `1px solid ${theme.palette.text.primary}`,
|
|
43
42
|
},
|
|
@@ -47,133 +46,127 @@ const useStyles = makeStyles()(theme => ({
|
|
|
47
46
|
},
|
|
48
47
|
}))
|
|
49
48
|
|
|
50
|
-
const Member = observer(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
/>
|
|
89
|
-
)
|
|
90
|
-
}
|
|
91
|
-
return (
|
|
92
|
-
<Accordion defaultExpanded className={classes.accordion}>
|
|
93
|
-
<AccordionSummary
|
|
94
|
-
expandIcon={<ExpandMoreIcon className={classes.expandIcon} />}
|
|
95
|
-
>
|
|
96
|
-
<Typography>{[...path, slotName].join('➔')}</Typography>
|
|
97
|
-
</AccordionSummary>
|
|
98
|
-
<AccordionDetails className={classes.expansionPanelDetails}>
|
|
99
|
-
{typeSelector}
|
|
100
|
-
<FormGroup className={classes.noOverflow}>
|
|
101
|
-
<Schema schema={slot} path={[...path, slotName]} />
|
|
102
|
-
</FormGroup>
|
|
103
|
-
</AccordionDetails>
|
|
104
|
-
</Accordion>
|
|
49
|
+
const Member = observer(function (props: {
|
|
50
|
+
slotName: string
|
|
51
|
+
slotSchema: IAnyType
|
|
52
|
+
schema: AnyConfigurationModel
|
|
53
|
+
slot?: AnyConfigurationModel | AnyConfigurationModel[]
|
|
54
|
+
path?: string[]
|
|
55
|
+
}) {
|
|
56
|
+
const { classes } = useStyles()
|
|
57
|
+
const {
|
|
58
|
+
slotName,
|
|
59
|
+
slotSchema,
|
|
60
|
+
schema,
|
|
61
|
+
slot = schema[slotName],
|
|
62
|
+
path = [],
|
|
63
|
+
} = props
|
|
64
|
+
let typeSelector
|
|
65
|
+
if (isConfigurationSchemaType(slotSchema)) {
|
|
66
|
+
if (slot.length) {
|
|
67
|
+
return slot.map((subslot: AnyConfigurationModel, slotIndex: number) => {
|
|
68
|
+
const key = `${singular(slotName)} ${slotIndex + 1}`
|
|
69
|
+
return <Member {...props} key={key} slot={subslot} slotName={key} />
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
// if this is an explicitly typed schema, make a type-selecting dropdown
|
|
73
|
+
// that can be used to change its type
|
|
74
|
+
const typeNameChoices = getTypeNamesFromExplicitlyTypedUnion(slotSchema)
|
|
75
|
+
if (typeNameChoices.length) {
|
|
76
|
+
typeSelector = (
|
|
77
|
+
<TypeSelector
|
|
78
|
+
typeNameChoices={typeNameChoices}
|
|
79
|
+
slotName={slotName}
|
|
80
|
+
slot={slot}
|
|
81
|
+
onChange={evt => {
|
|
82
|
+
if (evt.target.value !== slot.type) {
|
|
83
|
+
schema.setSubschema(slotName, { type: evt.target.value })
|
|
84
|
+
}
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
105
87
|
)
|
|
106
88
|
}
|
|
89
|
+
return (
|
|
90
|
+
<Accordion defaultExpanded className={classes.accordion}>
|
|
91
|
+
<AccordionSummary
|
|
92
|
+
expandIcon={<ExpandMoreIcon className={classes.expandIcon} />}
|
|
93
|
+
>
|
|
94
|
+
<Typography>{[...path, slotName].join('➔')}</Typography>
|
|
95
|
+
</AccordionSummary>
|
|
96
|
+
<AccordionDetails className={classes.expansionPanelDetails}>
|
|
97
|
+
{typeSelector}
|
|
98
|
+
<FormGroup className={classes.noOverflow}>
|
|
99
|
+
<Schema schema={slot} path={[...path, slotName]} />
|
|
100
|
+
</FormGroup>
|
|
101
|
+
</AccordionDetails>
|
|
102
|
+
</Accordion>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
107
105
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
if (isConfigurationSlotType(slotSchema)) {
|
|
107
|
+
// this is a regular config slot
|
|
108
|
+
return <SlotEditor key={slotName} slot={slot} slotSchema={slotSchema} />
|
|
109
|
+
}
|
|
112
110
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
111
|
+
return null
|
|
112
|
+
})
|
|
116
113
|
|
|
117
|
-
const Schema = observer(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
},
|
|
140
|
-
)
|
|
114
|
+
const Schema = observer(function ({
|
|
115
|
+
schema,
|
|
116
|
+
path = [],
|
|
117
|
+
}: {
|
|
118
|
+
schema: AnyConfigurationModel
|
|
119
|
+
path?: string[]
|
|
120
|
+
}) {
|
|
121
|
+
const properties = getMembers(schema).properties
|
|
122
|
+
return (
|
|
123
|
+
<>
|
|
124
|
+
{Object.entries(properties).map(([slotName, slotSchema]) => (
|
|
125
|
+
<Member
|
|
126
|
+
key={slotName}
|
|
127
|
+
slotName={slotName}
|
|
128
|
+
slotSchema={slotSchema}
|
|
129
|
+
path={path}
|
|
130
|
+
schema={schema}
|
|
131
|
+
/>
|
|
132
|
+
))}
|
|
133
|
+
</>
|
|
134
|
+
)
|
|
135
|
+
})
|
|
141
136
|
|
|
142
|
-
const ConfigurationEditor = observer(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
<
|
|
159
|
-
<
|
|
160
|
-
|
|
161
|
-
>
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
</Accordion>
|
|
137
|
+
const ConfigurationEditor = observer(function ({
|
|
138
|
+
model,
|
|
139
|
+
}: {
|
|
140
|
+
model: { target: AnyConfigurationModel }
|
|
141
|
+
session?: AbstractSessionModel
|
|
142
|
+
}) {
|
|
143
|
+
const { classes } = useStyles()
|
|
144
|
+
// key forces a re-render, otherwise the same field can end up being used
|
|
145
|
+
// for different tracks since only the backing model changes for example
|
|
146
|
+
// see pr #804
|
|
147
|
+
const { target } = model
|
|
148
|
+
const key = target && readConfObject(target, 'trackId')
|
|
149
|
+
const name = target && readConfObject(target, 'name')
|
|
150
|
+
return (
|
|
151
|
+
<>
|
|
152
|
+
<Accordion key={key} defaultExpanded className={classes.accordion}>
|
|
153
|
+
<AccordionSummary
|
|
154
|
+
expandIcon={<ExpandMoreIcon className={classes.expandIcon} />}
|
|
155
|
+
>
|
|
156
|
+
<Typography>{name ?? 'Configuration'}</Typography>
|
|
157
|
+
</AccordionSummary>
|
|
158
|
+
<AccordionDetails
|
|
159
|
+
className={classes.expansionPanelDetails}
|
|
160
|
+
data-testid="configEditor"
|
|
161
|
+
>
|
|
162
|
+
{!target ? 'no target set' : <Schema schema={target} />}
|
|
163
|
+
</AccordionDetails>
|
|
164
|
+
</Accordion>
|
|
171
165
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
)
|
|
166
|
+
{/* blank space at the bottom of screen allows scroll */}
|
|
167
|
+
<div style={{ height: 300 }} />
|
|
168
|
+
</>
|
|
169
|
+
)
|
|
170
|
+
})
|
|
178
171
|
|
|
179
172
|
export default ConfigurationEditor
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { TextField, TextFieldProps } from '@mui/material'
|
|
3
|
+
import { SanitizedHTML } from '@jbrowse/core/ui'
|
|
4
|
+
|
|
5
|
+
// adds ability to have html in helperText. note that FormHelperTextProps is
|
|
6
|
+
// div because the default is p which does not like div children
|
|
7
|
+
export default function ConfigurationTextField(
|
|
8
|
+
props: { helperText?: string } & TextFieldProps,
|
|
9
|
+
) {
|
|
10
|
+
const { helperText } = props
|
|
11
|
+
return (
|
|
12
|
+
<TextField
|
|
13
|
+
{...props}
|
|
14
|
+
helperText={<SanitizedHTML html={helperText || ''} />}
|
|
15
|
+
FormHelperTextProps={{
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
component: 'div',
|
|
18
|
+
}}
|
|
19
|
+
fullWidth
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
import ConfigurationTextField from './ConfigurationTextField'
|
|
4
|
+
|
|
5
|
+
export default observer(function ({
|
|
6
|
+
slot,
|
|
7
|
+
}: {
|
|
8
|
+
slot: {
|
|
9
|
+
name?: string
|
|
10
|
+
value: string
|
|
11
|
+
description?: string
|
|
12
|
+
set: (val: number) => void
|
|
13
|
+
reset?: () => void
|
|
14
|
+
}
|
|
15
|
+
}) {
|
|
16
|
+
const [val, setVal] = useState(slot.value)
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const num = parseFloat(val)
|
|
19
|
+
if (!Number.isNaN(num)) {
|
|
20
|
+
slot.set(num)
|
|
21
|
+
} else {
|
|
22
|
+
slot.reset?.()
|
|
23
|
+
}
|
|
24
|
+
}, [slot, val])
|
|
25
|
+
return (
|
|
26
|
+
<ConfigurationTextField
|
|
27
|
+
label={slot.name}
|
|
28
|
+
helperText={slot.description}
|
|
29
|
+
value={val}
|
|
30
|
+
type="number"
|
|
31
|
+
onChange={evt => setVal(evt.target.value)}
|
|
32
|
+
/>
|
|
33
|
+
)
|
|
34
|
+
})
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
Card,
|
|
6
|
+
CardContent,
|
|
7
|
+
CardHeader,
|
|
8
|
+
FormHelperText,
|
|
9
|
+
IconButton,
|
|
10
|
+
InputAdornment,
|
|
11
|
+
InputLabel,
|
|
12
|
+
TextField,
|
|
13
|
+
} from '@mui/material'
|
|
14
|
+
import { makeStyles } from 'tss-react/mui'
|
|
15
|
+
|
|
16
|
+
// icons
|
|
17
|
+
import DeleteIcon from '@mui/icons-material/Delete'
|
|
18
|
+
import AddIcon from '@mui/icons-material/Add'
|
|
19
|
+
import NumberEditor from './NumberEditor'
|
|
20
|
+
|
|
21
|
+
const useStyles = makeStyles()(theme => ({
|
|
22
|
+
card: {
|
|
23
|
+
marginTop: theme.spacing(1),
|
|
24
|
+
},
|
|
25
|
+
}))
|
|
26
|
+
|
|
27
|
+
export default observer(function ({
|
|
28
|
+
slot,
|
|
29
|
+
}: {
|
|
30
|
+
slot: {
|
|
31
|
+
name: string
|
|
32
|
+
value: Map<string, string>
|
|
33
|
+
remove: (key: string) => void
|
|
34
|
+
add: (key: string, val: number) => void
|
|
35
|
+
description: string
|
|
36
|
+
}
|
|
37
|
+
}) {
|
|
38
|
+
const { classes } = useStyles()
|
|
39
|
+
const [value, setValue] = useState('')
|
|
40
|
+
return (
|
|
41
|
+
<>
|
|
42
|
+
<InputLabel>{slot.name}</InputLabel>
|
|
43
|
+
{Array.from(slot.value, ([key, val]) => (
|
|
44
|
+
<Card raised key={key} className={classes.card}>
|
|
45
|
+
<CardHeader
|
|
46
|
+
title={key}
|
|
47
|
+
action={
|
|
48
|
+
<IconButton onClick={() => slot.remove(key)}>
|
|
49
|
+
<DeleteIcon />
|
|
50
|
+
</IconButton>
|
|
51
|
+
}
|
|
52
|
+
/>
|
|
53
|
+
<CardContent>
|
|
54
|
+
<NumberEditor
|
|
55
|
+
slot={{
|
|
56
|
+
value: val,
|
|
57
|
+
set: (val: number) => slot.add(key, val),
|
|
58
|
+
}}
|
|
59
|
+
/>
|
|
60
|
+
</CardContent>
|
|
61
|
+
</Card>
|
|
62
|
+
))}
|
|
63
|
+
<Card raised className={classes.card}>
|
|
64
|
+
<CardHeader
|
|
65
|
+
disableTypography
|
|
66
|
+
title={
|
|
67
|
+
<TextField
|
|
68
|
+
fullWidth
|
|
69
|
+
value={value}
|
|
70
|
+
placeholder="add new"
|
|
71
|
+
onChange={event => setValue(event.target.value)}
|
|
72
|
+
InputProps={{
|
|
73
|
+
endAdornment: (
|
|
74
|
+
<InputAdornment position="end">
|
|
75
|
+
<IconButton
|
|
76
|
+
disabled={value === ''}
|
|
77
|
+
onClick={() => {
|
|
78
|
+
slot.add(value, 0)
|
|
79
|
+
setValue('')
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
<AddIcon />
|
|
83
|
+
</IconButton>
|
|
84
|
+
</InputAdornment>
|
|
85
|
+
),
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
}
|
|
89
|
+
/>
|
|
90
|
+
</Card>
|
|
91
|
+
<FormHelperText>{slot.description}</FormHelperText>
|
|
92
|
+
</>
|
|
93
|
+
)
|
|
94
|
+
})
|