@jbrowse/plugin-config 5.0.0-beta.7 → 5.0.0-beta.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.
- package/esm/ConfigurationEditorWidget/components/ColorEditor.d.ts +2 -1
- package/esm/ConfigurationEditorWidget/components/ColorEditor.js +25 -8
- package/esm/ConfigurationEditorWidget/components/ConfigurationEditor.js +67 -14
- package/esm/ConfigurationEditorWidget/components/EntryListEditor.d.ts +17 -0
- package/esm/ConfigurationEditorWidget/components/EntryListEditor.js +35 -0
- package/esm/ConfigurationEditorWidget/components/SlotEditor.js +15 -5
- package/esm/ConfigurationEditorWidget/components/TypeSelector.d.ts +1 -1
- package/esm/ConfigurationEditorWidget/components/TypeSelector.js +1 -1
- package/esm/FromConfigAdapter/configSchema.d.ts +12 -17
- package/esm/FromConfigRegionsAdapter/configSchema.d.ts +12 -17
- package/esm/FromConfigSequenceAdapter/configSchema.d.ts +12 -17
- package/esm/NcbiSequenceReportAliasAdapter/configSchema.d.ts +12 -18
- package/esm/RefNameAliasAdapter/configSchema.d.ts +15 -29
- package/esm/RefNameAliasAdapter/configSchema.js +1 -1
- package/package.json +3 -3
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/** #slotEditor text field beside a swatch that opens a color picker */
|
|
2
|
-
declare const ColorEditor: (
|
|
2
|
+
declare const ColorEditor: ({ slot, }: {
|
|
3
3
|
slot: {
|
|
4
4
|
name: string;
|
|
5
5
|
value: string | undefined;
|
|
6
6
|
description: string;
|
|
7
|
+
contextVariable?: string[];
|
|
7
8
|
set: (arg: string) => void;
|
|
8
9
|
};
|
|
9
10
|
}) => import("react").JSX.Element;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Suspense, lazy } from 'react';
|
|
2
|
+
import { Suspense, lazy, useState } from 'react';
|
|
3
|
+
import { isCssColor } from '@jbrowse/core/util/colorBits';
|
|
4
|
+
import { isJexl } from '@jbrowse/core/util/jexlStrings';
|
|
3
5
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
6
|
import { observer } from 'mobx-react';
|
|
5
7
|
import ConfigurationTextField from './ConfigurationTextField.js';
|
|
@@ -17,18 +19,33 @@ const useStyles = makeStyles()({
|
|
|
17
19
|
});
|
|
18
20
|
// Also drives `maybeColor`, whose value is `undefined` while unset. An empty
|
|
19
21
|
// string keeps the field controlled and the picker fed, the same way
|
|
20
|
-
// NumberEditor renders an unset `maybeNumber` as an empty field.
|
|
21
|
-
//
|
|
22
|
-
//
|
|
22
|
+
// NumberEditor renders an unset `maybeNumber` as an empty field. The slot
|
|
23
|
+
// refuses what is not a color, so text that does not parse yet stays a draft
|
|
24
|
+
// in the field and reaches the slot once it does; leaving the field drops an
|
|
25
|
+
// unparsed draft. "Unset" is reachable through the slot's reset button, which
|
|
26
|
+
// is where every other slot type puts it. Only a slot declaring a
|
|
27
|
+
// `contextVariable` takes a `jexl:` callback, which an entry of a colour list
|
|
28
|
+
// never does.
|
|
23
29
|
/** #slotEditor text field beside a swatch that opens a color picker */
|
|
24
|
-
const ColorEditor = observer(function ColorEditor(
|
|
25
|
-
const
|
|
30
|
+
const ColorEditor = observer(function ColorEditor({ slot, }) {
|
|
31
|
+
const admitsJexl = !!slot.contextVariable?.length;
|
|
26
32
|
const { classes } = useStyles();
|
|
33
|
+
const [draft, setDraft] = useState();
|
|
27
34
|
const value = slot.value ?? '';
|
|
28
|
-
return (_jsxs("div", { className: classes.root, children: [_jsx(ConfigurationTextField, { value: value, label: slot.name, helperText: slot.description
|
|
29
|
-
|
|
35
|
+
return (_jsxs("div", { className: classes.root, children: [_jsx(ConfigurationTextField, { value: draft ?? value, label: slot.name, error: draft !== undefined, helperText: draft === undefined ? slot.description : `"${draft}" is not a color`, className: classes.field, onChange: event => {
|
|
36
|
+
const text = event.target.value;
|
|
37
|
+
if (isCssColor(text) || (admitsJexl && isJexl(text))) {
|
|
38
|
+
slot.set(text);
|
|
39
|
+
setDraft(undefined);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
setDraft(text);
|
|
43
|
+
}
|
|
44
|
+
}, onBlur: () => {
|
|
45
|
+
setDraft(undefined);
|
|
30
46
|
} }), _jsx(Suspense, { fallback: null, children: _jsx(PopoverPicker, { color: value, onChange: color => {
|
|
31
47
|
slot.set(color);
|
|
48
|
+
setDraft(undefined);
|
|
32
49
|
} }) })] }));
|
|
33
50
|
});
|
|
34
51
|
export default ColorEditor;
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
-
import { getSlotDefinition, getTypeNamesFromExplicitlyTypedUnion, isConfigurationModel, isConfigurationSchemaType, isConfigurationSlot, makeSlotFacade, readConfObject, } from '@jbrowse/core/configuration';
|
|
3
|
+
import { arraySlotUnion, getSlotDefinition, getTypeNamesFromExplicitlyTypedUnion, isConfigurationModel, isConfigurationSchemaType, isConfigurationSlot, makeSlotFacade, readConfObject, setConf, } from '@jbrowse/core/configuration';
|
|
4
4
|
import SanitizedHTML from '@jbrowse/core/ui/SanitizedHTML';
|
|
5
5
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
6
|
-
import { getMembers } from '@jbrowse/mobx-state-tree';
|
|
6
|
+
import { getMembers, getSnapshot } from '@jbrowse/mobx-state-tree';
|
|
7
|
+
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
|
|
8
|
+
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
|
|
7
9
|
import ClearIcon from '@mui/icons-material/Clear';
|
|
10
|
+
import DeleteIcon from '@mui/icons-material/Delete';
|
|
8
11
|
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
|
9
12
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
|
10
13
|
import SearchIcon from '@mui/icons-material/Search';
|
|
11
|
-
import { Accordion, AccordionDetails, AccordionSummary, Button, IconButton, InputAdornment, TextField, Typography, } from '@mui/material';
|
|
14
|
+
import { Accordion, AccordionDetails, AccordionSummary, Button, IconButton, InputAdornment, MenuItem, TextField, Typography, } from '@mui/material';
|
|
12
15
|
import { observer } from 'mobx-react';
|
|
13
16
|
import SlotEditor from './SlotEditor.js';
|
|
14
17
|
import TypeSelector from './TypeSelector.js';
|
|
@@ -39,7 +42,15 @@ const useStyles = makeStyles()(theme => ({
|
|
|
39
42
|
margin: theme.spacing(1, 0),
|
|
40
43
|
textTransform: 'none',
|
|
41
44
|
},
|
|
45
|
+
entryControls: {
|
|
46
|
+
display: 'flex',
|
|
47
|
+
justifyContent: 'flex-end',
|
|
48
|
+
},
|
|
42
49
|
}));
|
|
50
|
+
// The entries of an array of sub-schemas as snapshots, for a whole-list write.
|
|
51
|
+
function listSnapshot(schema, listName) {
|
|
52
|
+
return [...getSnapshot(schema[listName])];
|
|
53
|
+
}
|
|
43
54
|
// matches a slot or sub-schema against a lowercased filter string, recursing
|
|
44
55
|
// into sub-schemas so a nested slot name keeps its ancestors visible
|
|
45
56
|
function memberMatches(schema, slotName, slotSchema, query) {
|
|
@@ -53,7 +64,7 @@ function memberMatches(schema, slotName, slotSchema, query) {
|
|
|
53
64
|
return (slotName.toLowerCase().includes(query) ||
|
|
54
65
|
(Array.isArray(slot)
|
|
55
66
|
? slot.some(subslot => schemaMatches(subslot, query))
|
|
56
|
-
: schemaMatches(slot, query)));
|
|
67
|
+
: slot !== undefined && schemaMatches(slot, query)));
|
|
57
68
|
}
|
|
58
69
|
else {
|
|
59
70
|
return false;
|
|
@@ -77,9 +88,32 @@ function displayDefaultExpanded(slot, expandedDisplayId) {
|
|
|
77
88
|
: undefined;
|
|
78
89
|
return !displayId || !expandedDisplayId || displayId === expandedDisplayId;
|
|
79
90
|
}
|
|
91
|
+
// A union list entry's own controls: its type, and its place in the list. Each
|
|
92
|
+
// is one whole-list write, so every state it leaves loads again.
|
|
93
|
+
const EntryControls = observer(function EntryControls({ schema, title, slot, entry: { listName, choices, index, count }, }) {
|
|
94
|
+
const { classes } = useStyles();
|
|
95
|
+
const write = (edit) => {
|
|
96
|
+
setConf(schema, listName, edit(listSnapshot(schema, listName)));
|
|
97
|
+
};
|
|
98
|
+
const swap = (at) => {
|
|
99
|
+
write(list => list.toSpliced(at, 2, list[at + 1], list[at]));
|
|
100
|
+
};
|
|
101
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: classes.entryControls, children: [_jsx(IconButton, { "aria-label": `move ${title} up`, disabled: index === 0, onClick: () => {
|
|
102
|
+
swap(index - 1);
|
|
103
|
+
}, children: _jsx(ArrowUpwardIcon, { fontSize: "small" }) }), _jsx(IconButton, { "aria-label": `move ${title} down`, disabled: index === count - 1, onClick: () => {
|
|
104
|
+
swap(index);
|
|
105
|
+
}, children: _jsx(ArrowDownwardIcon, { fontSize: "small" }) }), _jsx(IconButton, { "aria-label": `remove ${title}`, onClick: () => {
|
|
106
|
+
write(list => list.toSpliced(index, 1));
|
|
107
|
+
}, children: _jsx(DeleteIcon, { fontSize: "small" }) })] }), _jsx(TypeSelector, { typeNameChoices: choices, slotName: title, slot: slot, onChange: evt => {
|
|
108
|
+
const type = evt.target.value;
|
|
109
|
+
if (type !== slot.type) {
|
|
110
|
+
write(list => list.with(index, { type }));
|
|
111
|
+
}
|
|
112
|
+
} })] }));
|
|
113
|
+
});
|
|
80
114
|
const Member = observer(function Member(props) {
|
|
81
115
|
const { classes } = useStyles();
|
|
82
|
-
const { slotName, slotSchema, schema, slot = schema[slotName], path = [], filter = '', expandedDisplayId, } = props;
|
|
116
|
+
const { slotName, slotSchema, schema, slot = schema[slotName], path = [], filter = '', expandedDisplayId, entry, } = props;
|
|
83
117
|
// when the sub-schema's own name matches, drop the filter for its children so
|
|
84
118
|
// the whole group stays visible; otherwise keep filtering descendants
|
|
85
119
|
const childFilter = slotName.toLowerCase().includes(filter.toLowerCase())
|
|
@@ -91,12 +125,7 @@ const Member = observer(function Member(props) {
|
|
|
91
125
|
// keyed on Array.isArray, not `.length` truthiness, so an empty array maps
|
|
92
126
|
// to nothing instead of being mis-handled as a single schema
|
|
93
127
|
if (Array.isArray(slot)) {
|
|
94
|
-
return
|
|
95
|
-
const key = subslot.type
|
|
96
|
-
? `${singularSlotName(slotName)} ${subslot.type}`
|
|
97
|
-
: `${singularSlotName(slotName)} ${slotIndex + 1}`;
|
|
98
|
-
return _jsx(Member, { ...props, slot: subslot, slotName: key }, key);
|
|
99
|
-
});
|
|
128
|
+
return _jsx(EntryList, { ...props, entries: slot });
|
|
100
129
|
}
|
|
101
130
|
// if this is an explicitly typed schema, make a type-selecting dropdown
|
|
102
131
|
// that can be used to change its type
|
|
@@ -107,13 +136,13 @@ const Member = observer(function Member(props) {
|
|
|
107
136
|
// visible, not just present in the collapsed DOM. The key flips the
|
|
108
137
|
// accordion between filtered/unfiltered mounts so the new
|
|
109
138
|
// defaultExpanded takes effect (Accordion reads it only on mount).
|
|
110
|
-
, { defaultExpanded: filter ? true : displayDefaultExpanded(slot, expandedDisplayId), className: classes.accordion, children: [_jsx(AccordionSummary, { expandIcon: _jsx(ExpandMoreIcon, { className: classes.icon }), children: _jsx(Typography, { children: [...path, slotName].join('➔') }) }), _jsxs(AccordionDetails, { className: classes.expansionPanelDetails, children: [typeNameChoices.length ? (_jsx(TypeSelector, { typeNameChoices: typeNameChoices, slotName: slotName, slot: slot, onChange: evt => {
|
|
111
|
-
if (evt.target.value !== slot
|
|
139
|
+
, { defaultExpanded: filter ? true : displayDefaultExpanded(slot, expandedDisplayId), className: classes.accordion, children: [_jsx(AccordionSummary, { expandIcon: _jsx(ExpandMoreIcon, { className: classes.icon }), children: _jsx(Typography, { children: [...path, slotName].join('➔') }) }), _jsxs(AccordionDetails, { className: classes.expansionPanelDetails, children: [entry && slot ? (_jsx(EntryControls, { schema: schema, title: slotName, slot: slot, entry: entry })) : null, typeNameChoices.length ? (_jsx(TypeSelector, { typeNameChoices: typeNameChoices, slotName: slotName, slot: slot, onChange: evt => {
|
|
140
|
+
if (evt.target.value !== slot?.type) {
|
|
112
141
|
schema.setSubschema(slotName, {
|
|
113
142
|
type: evt.target.value,
|
|
114
143
|
});
|
|
115
144
|
}
|
|
116
|
-
} })) : null, _jsx("div", { className: classes.noOverflow, children: _jsx(Schema, { schema: slot, path: [...path, slotName], filter: childFilter, expandedDisplayId: expandedDisplayId }) })] })] }, filter ? 'filtered' : 'unfiltered'));
|
|
145
|
+
} })) : null, slot ? (_jsx("div", { className: classes.noOverflow, children: _jsx(Schema, { schema: slot, path: [...path, slotName], filter: childFilter, expandedDisplayId: expandedDisplayId }) })) : null] })] }, filter ? 'filtered' : 'unfiltered'));
|
|
117
146
|
}
|
|
118
147
|
else if (isConfigurationSlot(schema, slotName)) {
|
|
119
148
|
return _jsx(SlotEditor, { slot: makeSlotFacade(schema, slotName) }, slotName);
|
|
@@ -122,6 +151,30 @@ const Member = observer(function Member(props) {
|
|
|
122
151
|
return null;
|
|
123
152
|
}
|
|
124
153
|
});
|
|
154
|
+
// An array of sub-schemas, one accordion per entry. A list whose entries are a
|
|
155
|
+
// `ConfigurationSchemaUnion` also offers each entry's type and place, and a new
|
|
156
|
+
// entry of any member type; a pluggable union such as a track's `displays` is
|
|
157
|
+
// not one, since a display is added through the track.
|
|
158
|
+
const EntryList = observer(function EntryList(props) {
|
|
159
|
+
const { classes } = useStyles();
|
|
160
|
+
const { slotName, slotSchema, schema, entries } = props;
|
|
161
|
+
const singular = singularSlotName(slotName);
|
|
162
|
+
const union = arraySlotUnion(slotSchema);
|
|
163
|
+
const choices = union ? Object.keys(union.members) : [];
|
|
164
|
+
return (_jsxs(_Fragment, { children: [entries.map((subslot, index) => {
|
|
165
|
+
const title = `${singular} ${index + 1}`;
|
|
166
|
+
return (_jsx(Member
|
|
167
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key -- a config entry has no identity but its place
|
|
168
|
+
, { ...props, slot: subslot, slotName: union || !subslot.type ? title : `${title} ${subslot.type}`, entry: union
|
|
169
|
+
? { listName: slotName, choices, index, count: entries.length }
|
|
170
|
+
: undefined }, index));
|
|
171
|
+
}), union ? (_jsx(TextField, { className: classes.filter, select: true, size: "small", label: `Add ${singular}`, value: "", onChange: evt => {
|
|
172
|
+
setConf(schema, slotName, [
|
|
173
|
+
...listSnapshot(schema, slotName),
|
|
174
|
+
{ type: evt.target.value },
|
|
175
|
+
]);
|
|
176
|
+
}, fullWidth: true, children: choices.map(type => (_jsx(MenuItem, { value: type, children: type }, type))) })) : null] }));
|
|
177
|
+
});
|
|
125
178
|
const Schema = observer(function Schema({ schema, path = [], filter = '', expandedDisplayId, }) {
|
|
126
179
|
const { classes } = useStyles();
|
|
127
180
|
const [showAdvanced, setShowAdvanced] = useState(false);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface ListSlot {
|
|
2
|
+
name: string;
|
|
3
|
+
value: string[];
|
|
4
|
+
description: string;
|
|
5
|
+
set: (arg: string[]) => void;
|
|
6
|
+
}
|
|
7
|
+
/** #slotEditor a text field and color picker per entry, with add and delete */
|
|
8
|
+
declare const ColorArrayEditor: ({ slot, }: {
|
|
9
|
+
slot: ListSlot;
|
|
10
|
+
}) => import("react").JSX.Element;
|
|
11
|
+
/** #slotEditor a dropdown of the `model`'s members per entry, with add and delete */
|
|
12
|
+
declare const StringEnumArrayEditor: ({ slot, }: {
|
|
13
|
+
slot: ListSlot & {
|
|
14
|
+
choices?: string[];
|
|
15
|
+
};
|
|
16
|
+
}) => import("react").JSX.Element;
|
|
17
|
+
export { ColorArrayEditor, StringEnumArrayEditor };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import AddIcon from '@mui/icons-material/Add';
|
|
3
|
+
import DeleteIcon from '@mui/icons-material/Delete';
|
|
4
|
+
import { FormHelperText, IconButton, InputLabel, List, ListItem, MenuItem, } from '@mui/material';
|
|
5
|
+
import { observer } from 'mobx-react';
|
|
6
|
+
import ColorEditor from './ColorEditor.js';
|
|
7
|
+
import ConfigurationTextField from './ConfigurationTextField.js';
|
|
8
|
+
// A list slot whose entries each take one control: the list is written whole
|
|
9
|
+
// on every change, and "add" appends an entry the slot already admits, so no
|
|
10
|
+
// write can hold an entry the slot refuses.
|
|
11
|
+
const EntryList = observer(function EntryList({ slot, entry, added, }) {
|
|
12
|
+
const value = [...slot.value];
|
|
13
|
+
const addition = added(value);
|
|
14
|
+
return (_jsxs(_Fragment, { children: [slot.name ? _jsx(InputLabel, { children: slot.name }) : null, _jsxs(List, { disablePadding: true, children: [value.map((v, idx) => (_jsxs(ListItem, { disableGutters: true, children: [entry(v, next => {
|
|
15
|
+
slot.set(value.map((old, i) => (i === idx ? next : old)));
|
|
16
|
+
}), _jsx(IconButton, { "aria-label": "delete entry", onClick: () => {
|
|
17
|
+
slot.set(value.filter((_, i) => i !== idx));
|
|
18
|
+
}, children: _jsx(DeleteIcon, {}) })] }, idx))), _jsx(ListItem, { disableGutters: true, children: _jsx(IconButton, { "data-testid": `entryAdd-${slot.name}`, "aria-label": "add entry", disabled: addition === undefined, onClick: () => {
|
|
19
|
+
if (addition !== undefined) {
|
|
20
|
+
slot.set([...value, addition]);
|
|
21
|
+
}
|
|
22
|
+
}, children: _jsx(AddIcon, {}) }) })] }), _jsx(FormHelperText, { children: slot.description })] }));
|
|
23
|
+
});
|
|
24
|
+
/** #slotEditor a text field and color picker per entry, with add and delete */
|
|
25
|
+
const ColorArrayEditor = observer(function ColorArrayEditor({ slot, }) {
|
|
26
|
+
return (_jsx(EntryList, { slot: slot, added: value => value.at(-1) ?? 'black', entry: (color, set) => (_jsx(ColorEditor, { slot: { name: '', description: '', value: color, set } })) }));
|
|
27
|
+
});
|
|
28
|
+
/** #slotEditor a dropdown of the `model`'s members per entry, with add and delete */
|
|
29
|
+
const StringEnumArrayEditor = observer(function StringEnumArrayEditor({ slot, }) {
|
|
30
|
+
const choices = slot.choices ?? [];
|
|
31
|
+
return (_jsx(EntryList, { slot: slot, added: () => choices[0], entry: (member, set) => (_jsx(ConfigurationTextField, { select: true, value: member, onChange: evt => {
|
|
32
|
+
set(evt.target.value);
|
|
33
|
+
}, children: choices.map(choice => (_jsx(MenuItem, { value: choice, children: choice }, choice))) })) }));
|
|
34
|
+
});
|
|
35
|
+
export { ColorArrayEditor, StringEnumArrayEditor };
|
|
@@ -10,6 +10,7 @@ import BooleanEditor from './BooleanEditor.js';
|
|
|
10
10
|
import CallbackEditor from './CallbackEditor.js';
|
|
11
11
|
import ColorEditor from './ColorEditor.js';
|
|
12
12
|
import ConfigurationTextField from './ConfigurationTextField.js';
|
|
13
|
+
import { ColorArrayEditor, StringEnumArrayEditor } from './EntryListEditor.js';
|
|
13
14
|
import IntegerEditor from './IntegerEditor.js';
|
|
14
15
|
import JsonEditor from './JsonEditor.js';
|
|
15
16
|
import NumberEditor from './NumberEditor.js';
|
|
@@ -17,9 +18,12 @@ import NumberMapEditor from './NumberMapEditor.js';
|
|
|
17
18
|
import StringArrayEditor from './StringArrayEditor.js';
|
|
18
19
|
import StringArrayMapEditor from './StringArrayMapEditor.js';
|
|
19
20
|
import { useSlotEditorStyles } from './useSlotEditorStyles.js';
|
|
21
|
+
// Also drives `maybeString`, whose value is `undefined` while unset: clearing
|
|
22
|
+
// the field leaves the empty string, which is a value, and the reset button
|
|
23
|
+
// returns the slot to unset.
|
|
20
24
|
/** #slotEditor single-line text field */
|
|
21
25
|
const StringEditor = observer(function StringEditor({ slot, }) {
|
|
22
|
-
return (_jsx(ConfigurationTextField, { label: slot.name, helperText: slot.description, value: slot.value, onChange: evt => {
|
|
26
|
+
return (_jsx(ConfigurationTextField, { label: slot.name, helperText: slot.description, value: slot.value ?? '', onChange: evt => {
|
|
23
27
|
slot.set(evt.target.value);
|
|
24
28
|
} }));
|
|
25
29
|
});
|
|
@@ -54,9 +58,13 @@ const FileSelectorWrapper = observer(function FileSelectorWrapper({ slot, }) {
|
|
|
54
58
|
// The editors stay fully typed internally; only this lookup is untyped.
|
|
55
59
|
const valueComponents = {
|
|
56
60
|
string: StringEditor,
|
|
61
|
+
maybeString: StringEditor,
|
|
62
|
+
featureField: StringEditor,
|
|
57
63
|
text: TextEditor,
|
|
58
64
|
fileLocation: FileSelectorWrapper,
|
|
59
65
|
stringArray: StringArrayEditor,
|
|
66
|
+
colorArray: ColorArrayEditor,
|
|
67
|
+
stringEnumArray: StringEnumArrayEditor,
|
|
60
68
|
stringArrayMap: StringArrayMapEditor,
|
|
61
69
|
numberMap: NumberMapEditor,
|
|
62
70
|
number: NumberEditor,
|
|
@@ -77,8 +85,10 @@ const SlotEditor = observer(function SlotEditor({ slot, }) {
|
|
|
77
85
|
// editor mode is UI-only state, derived once from whether the stored value is
|
|
78
86
|
// a jexl callback. Toggling rewrites the value; typing a "jexl:" prefix into a
|
|
79
87
|
// value field does not auto-swap the editor, since this state only changes on
|
|
80
|
-
// the explicit toggle.
|
|
81
|
-
|
|
88
|
+
// the explicit toggle. A featureField's `jexl:` expression is a field, typed
|
|
89
|
+
// in its own text field.
|
|
90
|
+
const callback = slot.contextVariable.length > 0;
|
|
91
|
+
const [callbackMode, setCallbackMode] = useState(() => callback && isCallbackValue(slot.value));
|
|
82
92
|
// bumped on reset to remount the value editor: the buffered editors (number,
|
|
83
93
|
// json, callback) seed internal state from slot.value only on mount, so an
|
|
84
94
|
// external set wouldn't otherwise show in the field
|
|
@@ -93,9 +103,9 @@ const SlotEditor = observer(function SlotEditor({ slot, }) {
|
|
|
93
103
|
const { modified } = slot;
|
|
94
104
|
return (_jsxs(Paper, { className: classes.paper, children: [_jsx("div", { className: classes.paperContent, children: _jsx(ValueComponent, { slot: slot }, resetNonce) }), modified ? (_jsx("div", { className: classes.resetButton, children: _jsx(Tooltip, { title: "Reset to default", children: _jsx(IconButton, { "aria-label": "reset to default", onClick: () => {
|
|
95
105
|
slot.set(slot.defaultValue);
|
|
96
|
-
setCallbackMode(isCallbackValue(slot.defaultValue));
|
|
106
|
+
setCallbackMode(callback && isCallbackValue(slot.defaultValue));
|
|
97
107
|
setResetNonce(nonce => nonce + 1);
|
|
98
|
-
}, children: _jsx(RestartAltIcon, {}) }) }) })) : null,
|
|
108
|
+
}, children: _jsx(RestartAltIcon, {}) }) }) })) : null, callback ? (_jsx("div", { className: classes.slotModeSwitch, children: _jsx(Tooltip, { title: callbackMode
|
|
99
109
|
? 'Editing as a callback (Jexl expression). Click to use a fixed value.'
|
|
100
110
|
: 'Editing as a fixed value. Click to use a callback (Jexl expression).', children: _jsx(IconButton, { color: callbackMode ? 'primary' : 'default', onClick: () => {
|
|
101
111
|
if (callbackMode) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
2
2
|
declare const TypeSelector: ({ typeNameChoices, slot, slotName, onChange, }: {
|
|
3
3
|
typeNameChoices: string[];
|
|
4
|
-
slot: AnyConfigurationModel;
|
|
4
|
+
slot: AnyConfigurationModel | undefined;
|
|
5
5
|
slotName: string;
|
|
6
6
|
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
7
7
|
}) => import("react").JSX.Element;
|
|
@@ -4,6 +4,6 @@ import { observer } from 'mobx-react';
|
|
|
4
4
|
import { useSlotEditorStyles } from './useSlotEditorStyles.js';
|
|
5
5
|
const TypeSelector = observer(function TypeSelector({ typeNameChoices, slot, slotName, onChange, }) {
|
|
6
6
|
const { classes } = useSlotEditorStyles();
|
|
7
|
-
return (_jsx(Paper, { className: classes.paper, children: _jsx("div", { className: classes.paperContent, children: _jsx(TextField, { value: slot
|
|
7
|
+
return (_jsx(Paper, { className: classes.paper, children: _jsx("div", { className: classes.paperContent, children: _jsx(TextField, { value: slot?.type ?? '', label: `Type of ${slotName}`, select: true, variant: "outlined", fullWidth: true, onChange: onChange, children: typeNameChoices.map(str => (_jsx(MenuItem, { value: str, children: str }, str))) }) }) }));
|
|
8
8
|
});
|
|
9
9
|
export default TypeSelector;
|
|
@@ -16,25 +16,20 @@
|
|
|
16
16
|
* ```
|
|
17
17
|
*/
|
|
18
18
|
declare const configSchema: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
type: {
|
|
20
|
+
type: "string";
|
|
21
|
+
} & {
|
|
22
|
+
defaultValue: string;
|
|
23
|
+
};
|
|
24
24
|
readonly adapterId: {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
type: "string";
|
|
26
|
+
} & {
|
|
27
|
+
defaultValue: string;
|
|
27
28
|
};
|
|
28
|
-
/**
|
|
29
|
-
* #slot
|
|
30
|
-
* the features themselves, as an array of plain objects. Each needs a
|
|
31
|
-
* `uniqueId`, `refName`, `start` and `end` (0-based half-open); anything
|
|
32
|
-
* else — `name`, `type`, `score`, `strand`, nested `subfeatures` — is
|
|
33
|
-
* carried through onto the feature as-is.
|
|
34
|
-
*/
|
|
35
29
|
readonly features: {
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
type: "frozen";
|
|
31
|
+
} & {
|
|
32
|
+
defaultValue: unknown;
|
|
38
33
|
};
|
|
39
|
-
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined
|
|
34
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, true, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
|
|
40
35
|
export default configSchema;
|
|
@@ -22,25 +22,20 @@
|
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
declare const regionsConfigSchema: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
type: {
|
|
26
|
+
type: "string";
|
|
27
|
+
} & {
|
|
28
|
+
defaultValue: string;
|
|
29
|
+
};
|
|
30
30
|
readonly adapterId: {
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
type: "string";
|
|
32
|
+
} & {
|
|
33
|
+
defaultValue: string;
|
|
33
34
|
};
|
|
34
|
-
/**
|
|
35
|
-
* #slot
|
|
36
|
-
* one entry per reference sequence, each with a `uniqueId`, `refName`,
|
|
37
|
-
* `start: 0` and `end` set to that sequence's length. This is what defines
|
|
38
|
-
* the assembly's reference names and sizes; no bases are supplied, so
|
|
39
|
-
* base-level views are empty.
|
|
40
|
-
*/
|
|
41
35
|
readonly features: {
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
type: "frozen";
|
|
37
|
+
} & {
|
|
38
|
+
defaultValue: unknown;
|
|
44
39
|
};
|
|
45
|
-
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined
|
|
40
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, true, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
|
|
46
41
|
export default regionsConfigSchema;
|
|
@@ -21,25 +21,20 @@
|
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
23
|
declare const sequenceConfigSchema: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
type: {
|
|
25
|
+
type: "string";
|
|
26
|
+
} & {
|
|
27
|
+
defaultValue: string;
|
|
28
|
+
};
|
|
29
29
|
readonly adapterId: {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
type: "string";
|
|
31
|
+
} & {
|
|
32
|
+
defaultValue: string;
|
|
32
33
|
};
|
|
33
|
-
/**
|
|
34
|
-
* #slot
|
|
35
|
-
* one entry per reference sequence, each with a `uniqueId`, `refName`,
|
|
36
|
-
* `start`, `end`, and a `seq` string holding the bases for that span. The
|
|
37
|
-
* bases live in the config, so this is for small sequences — a plasmid, a
|
|
38
|
-
* test contig — not a genome.
|
|
39
|
-
*/
|
|
40
34
|
readonly features: {
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
type: "frozen";
|
|
36
|
+
} & {
|
|
37
|
+
defaultValue: unknown;
|
|
43
38
|
};
|
|
44
|
-
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined
|
|
39
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, true, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
|
|
45
40
|
export default sequenceConfigSchema;
|
|
@@ -57,27 +57,21 @@ export declare const normalizeSnapshot: typeof normalizeUriSnapshot;
|
|
|
57
57
|
* ```
|
|
58
58
|
*/
|
|
59
59
|
declare const NcbiSequenceReportAliasAdapterConfigSchema: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
*/
|
|
60
|
+
type: {
|
|
61
|
+
type: "string";
|
|
62
|
+
} & {
|
|
63
|
+
defaultValue: string;
|
|
64
|
+
};
|
|
66
65
|
readonly location: {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
readonly locationType: "UriLocation";
|
|
71
|
-
};
|
|
66
|
+
type: "fileLocation";
|
|
67
|
+
} & {
|
|
68
|
+
defaultValue: unknown;
|
|
72
69
|
};
|
|
73
|
-
/**
|
|
74
|
-
* #slot
|
|
75
|
-
*/
|
|
76
70
|
readonly useNameOverride: {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
type: "boolean";
|
|
72
|
+
} & {
|
|
73
|
+
defaultValue: boolean;
|
|
80
74
|
};
|
|
81
|
-
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined
|
|
75
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, true, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
|
|
82
76
|
export type NcbiSequenceReportAliasAdapterConfig = Instance<typeof NcbiSequenceReportAliasAdapterConfigSchema>;
|
|
83
77
|
export default NcbiSequenceReportAliasAdapterConfigSchema;
|
|
@@ -55,40 +55,26 @@ export declare const normalizeSnapshot: typeof normalizeUriSnapshot;
|
|
|
55
55
|
* ```
|
|
56
56
|
*/
|
|
57
57
|
declare const RefNameAliasAdapter: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
* `1`-named file load against a `chr1`-named assembly.
|
|
64
|
-
*/
|
|
58
|
+
type: {
|
|
59
|
+
type: "string";
|
|
60
|
+
} & {
|
|
61
|
+
defaultValue: string;
|
|
62
|
+
};
|
|
65
63
|
readonly location: {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
readonly locationType: "UriLocation";
|
|
70
|
-
};
|
|
64
|
+
type: "fileLocation";
|
|
65
|
+
} & {
|
|
66
|
+
defaultValue: unknown;
|
|
71
67
|
};
|
|
72
|
-
/**
|
|
73
|
-
* #slot
|
|
74
|
-
* by default, the "ref names that match the fasta" are assumed to be in the
|
|
75
|
-
* first column (0), change this variable if needed
|
|
76
|
-
*/
|
|
77
68
|
readonly refNameColumn: {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
69
|
+
type: "number";
|
|
70
|
+
} & {
|
|
71
|
+
defaultValue: number;
|
|
81
72
|
};
|
|
82
|
-
/**
|
|
83
|
-
* #slot
|
|
84
|
-
* refNameColumnHeaderName
|
|
85
|
-
*/
|
|
86
73
|
readonly refNameColumnHeaderName: {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
readonly advanced: true;
|
|
74
|
+
type: "string";
|
|
75
|
+
} & {
|
|
76
|
+
defaultValue: string;
|
|
91
77
|
};
|
|
92
|
-
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined
|
|
78
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, true, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
|
|
93
79
|
export type RefNameAliasAdapterConfig = Instance<typeof RefNameAliasAdapter>;
|
|
94
80
|
export default RefNameAliasAdapter;
|
|
@@ -59,7 +59,7 @@ const RefNameAliasAdapter = ConfigurationSchema('RefNameAliasAdapter', {
|
|
|
59
59
|
* #slot
|
|
60
60
|
* location of the alias table: a UCSC `chromAlias.txt`, or any
|
|
61
61
|
* tab-separated file whose rows each list the alternate names of one
|
|
62
|
-
* reference sequence (`chr1<TAB>1<TAB>NC_000001.11`).
|
|
62
|
+
* reference sequence (`chr1<TAB>1<TAB>NC_000001.11`). The table lets a
|
|
63
63
|
* `1`-named file load against a `chr1`-named assembly.
|
|
64
64
|
*/
|
|
65
65
|
location: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-config",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "JBrowse 2 config utilities",
|
|
6
6
|
"keywords": [
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
"esm"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@jbrowse/mobx-state-tree": "^6.5.
|
|
24
|
+
"@jbrowse/mobx-state-tree": "^6.5.2",
|
|
25
25
|
"@mui/icons-material": "^9.4.0",
|
|
26
26
|
"@mui/material": "^9.4.0",
|
|
27
27
|
"mobx": "^7.0.3",
|
|
28
28
|
"mobx-react": "^10.0.2",
|
|
29
29
|
"rxjs": "^7.8.2",
|
|
30
|
-
"@jbrowse/core": "^5.0.0-beta.
|
|
30
|
+
"@jbrowse/core": "^5.0.0-beta.9"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"react": ">=19.0.0",
|