@jbrowse/plugin-config 4.2.1 → 5.0.0-beta.1
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/AddNewField.d.ts +4 -0
- package/esm/ConfigurationEditorWidget/components/AddNewField.js +28 -0
- package/esm/ConfigurationEditorWidget/components/BooleanEditor.d.ts +4 -2
- package/esm/ConfigurationEditorWidget/components/BooleanEditor.js +19 -4
- package/esm/ConfigurationEditorWidget/components/CallbackEditor.d.ts +4 -2
- package/esm/ConfigurationEditorWidget/components/CallbackEditor.js +24 -42
- package/esm/ConfigurationEditorWidget/components/ColorEditor.d.ts +3 -11
- package/esm/ConfigurationEditorWidget/components/ColorEditor.js +28 -20
- package/esm/ConfigurationEditorWidget/components/ConfigurationEditor.d.ts +2 -3
- package/esm/ConfigurationEditorWidget/components/ConfigurationEditor.js +108 -17
- package/esm/ConfigurationEditorWidget/components/ConfigurationTextField.d.ts +1 -1
- package/esm/ConfigurationEditorWidget/components/ConfigurationTextField.js +10 -2
- package/esm/ConfigurationEditorWidget/components/HeadingComponent.d.ts +1 -1
- package/esm/ConfigurationEditorWidget/components/IntegerEditor.d.ts +10 -0
- package/esm/ConfigurationEditorWidget/components/IntegerEditor.js +9 -0
- package/esm/ConfigurationEditorWidget/components/JsonEditor.d.ts +3 -1
- package/esm/ConfigurationEditorWidget/components/JsonEditor.js +27 -40
- package/esm/ConfigurationEditorWidget/components/MapEntryCard.d.ts +8 -0
- package/esm/ConfigurationEditorWidget/components/MapEntryCard.js +23 -0
- package/esm/ConfigurationEditorWidget/components/MapSlotEditor.d.ts +8 -0
- package/esm/ConfigurationEditorWidget/components/MapSlotEditor.js +18 -0
- package/esm/ConfigurationEditorWidget/components/NumberEditor.d.ts +6 -4
- package/esm/ConfigurationEditorWidget/components/NumberEditor.js +24 -15
- package/esm/ConfigurationEditorWidget/components/NumberMapEditor.d.ts +4 -4
- package/esm/ConfigurationEditorWidget/components/NumberMapEditor.js +7 -31
- package/esm/ConfigurationEditorWidget/components/SlotEditor.d.ts +4 -5
- package/esm/ConfigurationEditorWidget/components/SlotEditor.js +83 -48
- package/esm/ConfigurationEditorWidget/components/StringArrayEditor.d.ts +3 -4
- package/esm/ConfigurationEditorWidget/components/StringArrayEditor.js +11 -24
- package/esm/ConfigurationEditorWidget/components/StringArrayMapEditor.d.ts +3 -6
- package/esm/ConfigurationEditorWidget/components/StringArrayMapEditor.js +14 -39
- package/esm/ConfigurationEditorWidget/components/TypeSelector.d.ts +1 -1
- package/esm/ConfigurationEditorWidget/components/TypeSelector.js +2 -2
- package/esm/ConfigurationEditorWidget/components/singularSlotName.d.ts +13 -0
- package/esm/ConfigurationEditorWidget/components/singularSlotName.js +23 -0
- package/esm/ConfigurationEditorWidget/components/useSlotEditorStyles.d.ts +2 -2
- package/esm/ConfigurationEditorWidget/components/useSlotEditorStyles.js +6 -1
- package/esm/ConfigurationEditorWidget/index.js +3 -3
- package/esm/ConfigurationEditorWidget/model.d.ts +8 -1
- package/esm/ConfigurationEditorWidget/model.js +40 -1
- package/esm/FromConfigAdapter/FromConfigAdapter.d.ts +6 -1
- package/esm/FromConfigAdapter/FromConfigAdapter.js +31 -1
- package/esm/FromConfigAdapter/configSchema.d.ts +36 -3
- package/esm/FromConfigAdapter/configSchema.js +34 -2
- package/esm/FromConfigAdapter/index.js +2 -2
- package/esm/FromConfigRegionsAdapter/FromConfigRegionsAdapter.d.ts +8 -0
- package/esm/FromConfigRegionsAdapter/FromConfigRegionsAdapter.js +10 -25
- package/esm/FromConfigRegionsAdapter/configSchema.d.ts +42 -3
- package/esm/FromConfigRegionsAdapter/configSchema.js +39 -2
- package/esm/FromConfigRegionsAdapter/index.js +2 -2
- package/esm/FromConfigSequenceAdapter/FromConfigSequenceAdapter.d.ts +15 -2
- package/esm/FromConfigSequenceAdapter/FromConfigSequenceAdapter.js +32 -40
- package/esm/FromConfigSequenceAdapter/configSchema.d.ts +41 -3
- package/esm/FromConfigSequenceAdapter/configSchema.js +38 -2
- package/esm/FromConfigSequenceAdapter/index.js +2 -2
- package/esm/NcbiSequenceReportAliasAdapter/NcbiSequenceReportAliasAdapter.d.ts +5 -4
- package/esm/NcbiSequenceReportAliasAdapter/NcbiSequenceReportAliasAdapter.js +28 -24
- package/esm/NcbiSequenceReportAliasAdapter/configSchema.d.ts +77 -9
- package/esm/NcbiSequenceReportAliasAdapter/configSchema.js +79 -12
- package/esm/NcbiSequenceReportAliasAdapter/index.js +3 -2
- package/esm/RefNameAliasAdapter/RefNameAliasAdapter.d.ts +4 -3
- package/esm/RefNameAliasAdapter/RefNameAliasAdapter.js +22 -28
- package/esm/RefNameAliasAdapter/configSchema.d.ts +87 -12
- package/esm/RefNameAliasAdapter/configSchema.js +86 -12
- package/esm/RefNameAliasAdapter/index.js +3 -2
- package/esm/aliasUtils.d.ts +4 -0
- package/esm/aliasUtils.js +26 -0
- package/esm/index.d.ts +2 -2
- package/esm/index.js +7 -7
- package/esm/normalizeUriSnapshot.d.ts +1 -0
- package/esm/normalizeUriSnapshot.js +13 -0
- package/package.json +11 -15
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
3
|
+
import DeleteIcon from '@mui/icons-material/Delete';
|
|
4
|
+
import { Card, CardContent, CardHeader, IconButton } from '@mui/material';
|
|
5
|
+
import AddNewField from './AddNewField.js';
|
|
6
|
+
const useStyles = makeStyles()(theme => ({
|
|
7
|
+
card: {
|
|
8
|
+
marginTop: theme.spacing(1),
|
|
9
|
+
},
|
|
10
|
+
}));
|
|
11
|
+
// shared card for a single key in a map slot editor: a titled card with a
|
|
12
|
+
// delete button wrapping the per-value editor passed as children
|
|
13
|
+
export default function MapEntryCard({ title, onDelete, children, }) {
|
|
14
|
+
const { classes } = useStyles();
|
|
15
|
+
return (_jsxs(Card, { raised: true, className: classes.card, children: [_jsx(CardHeader, { title: title, action: _jsx(IconButton, { onClick: () => {
|
|
16
|
+
onDelete();
|
|
17
|
+
}, children: _jsx(DeleteIcon, {}) }) }), _jsx(CardContent, { children: children })] }));
|
|
18
|
+
}
|
|
19
|
+
// trailing card in a map slot editor holding the "add new key" field
|
|
20
|
+
export function MapAddCard({ onAdd }) {
|
|
21
|
+
const { classes } = useStyles();
|
|
22
|
+
return (_jsx(Card, { raised: true, className: classes.card, children: _jsx(CardHeader, { disableTypography: true, title: _jsx(AddNewField, { onAdd: onAdd }) }) }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export default function MapSlotEditor<V>({ name, description, entries, emptyValue, setMap, renderValue, }: {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
entries: [string, V][];
|
|
5
|
+
emptyValue: V;
|
|
6
|
+
setMap: (val: Record<string, V>) => void;
|
|
7
|
+
renderValue: (val: V, set: (val: V) => void, key: string) => React.ReactNode;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { FormHelperText, InputLabel } from '@mui/material';
|
|
3
|
+
import MapEntryCard, { MapAddCard } from './MapEntryCard.js';
|
|
4
|
+
// shared shell for map slot editors (numberMap, stringArrayMap): a labelled
|
|
5
|
+
// list of per-key cards plus an "add key" card. The owning wrapper stays the
|
|
6
|
+
// observer and passes plain entries in, so this stays a non-observer generic
|
|
7
|
+
// (which keeps JSX type args working at the call sites).
|
|
8
|
+
export default function MapSlotEditor({ name, description, entries, emptyValue, setMap, renderValue, }) {
|
|
9
|
+
const obj = Object.fromEntries(entries);
|
|
10
|
+
return (_jsxs(_Fragment, { children: [_jsx(InputLabel, { children: name }), entries.map(([key, val]) => (_jsx(MapEntryCard, { title: key, onDelete: () => {
|
|
11
|
+
const { [key]: _omit, ...rest } = obj;
|
|
12
|
+
setMap(rest);
|
|
13
|
+
}, children: renderValue(val, newVal => {
|
|
14
|
+
setMap({ ...obj, [key]: newVal });
|
|
15
|
+
}, key) }, key))), _jsx(MapAddCard, { onAdd: key => {
|
|
16
|
+
setMap({ ...obj, [key]: emptyValue });
|
|
17
|
+
} }), _jsx(FormHelperText, { children: description })] }));
|
|
18
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
/** #slotEditor numeric text field */
|
|
2
|
+
declare const NumberEditor: ({ slot, integer, }: {
|
|
2
3
|
slot: {
|
|
3
4
|
name?: string;
|
|
4
|
-
value:
|
|
5
|
+
value: number | undefined;
|
|
5
6
|
description?: string;
|
|
7
|
+
promotedBase?: unknown;
|
|
6
8
|
set: (val: number) => void;
|
|
7
|
-
reset?: () => void;
|
|
8
9
|
};
|
|
9
|
-
|
|
10
|
+
integer?: boolean;
|
|
11
|
+
}) => import("react").JSX.Element;
|
|
10
12
|
export default NumberEditor;
|
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useState } from 'react';
|
|
3
3
|
import { observer } from 'mobx-react';
|
|
4
|
-
import ConfigurationTextField from
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
import ConfigurationTextField from './ConfigurationTextField.js';
|
|
5
|
+
/** #slotEditor numeric text field */
|
|
6
|
+
const NumberEditor = observer(function NumberEditor({ slot, integer = false, }) {
|
|
7
|
+
const [val, setVal] = useState(slot.value === undefined ? '' : String(slot.value));
|
|
8
|
+
// An unset `maybeNumber` is a promotable slot's inherit sentinel, and an
|
|
9
|
+
// empty box is the honest rendering of it — a number field, unlike a
|
|
10
|
+
// checkbox, has a state for "nothing". What it did not say is what the track
|
|
11
|
+
// is therefore drawing at, so `featureHeight` read as a blank with no clue
|
|
12
|
+
// that reads are 7px tall. The placeholder is that, in the affordance meant
|
|
13
|
+
// for it; the label has to float for MUI to draw one over a labelled field.
|
|
14
|
+
const placeholder = slot.value === undefined && slot.promotedBase !== undefined
|
|
15
|
+
? String(slot.promotedBase)
|
|
16
|
+
: undefined;
|
|
17
|
+
return (_jsx(ConfigurationTextField, { label: slot.name, helperText: slot.description, placeholder: placeholder, slotProps: placeholder ? { inputLabel: { shrink: true } } : undefined, value: val, onChange: evt => {
|
|
18
|
+
const v = evt.target.value;
|
|
19
|
+
setVal(v);
|
|
20
|
+
const num = Number(v);
|
|
21
|
+
// commit only valid values; the text buffer preserves in-progress
|
|
22
|
+
// entries like "-" or "1." without writing garbage to the config
|
|
23
|
+
const valid = integer ? Number.isInteger(num) : Number.isFinite(num);
|
|
24
|
+
if (v !== '' && valid) {
|
|
25
|
+
slot.set(num);
|
|
26
|
+
}
|
|
18
27
|
} }));
|
|
19
28
|
});
|
|
20
29
|
export default NumberEditor;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
/** #slotEditor one card per key, each holding that key's numeric field */
|
|
1
2
|
declare const NumberMapEditor: ({ slot, }: {
|
|
2
3
|
slot: {
|
|
3
4
|
name: string;
|
|
4
|
-
value: Map<string,
|
|
5
|
-
|
|
6
|
-
add: (key: string, val: number) => void;
|
|
5
|
+
value: Map<string, number>;
|
|
6
|
+
set: (val: Record<string, number>) => void;
|
|
7
7
|
description: string;
|
|
8
8
|
};
|
|
9
|
-
}) => import("react
|
|
9
|
+
}) => import("react").JSX.Element;
|
|
10
10
|
export default NumberMapEditor;
|
|
@@ -1,35 +1,11 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
|
-
import AddIcon from '@mui/icons-material/Add';
|
|
5
|
-
import DeleteIcon from '@mui/icons-material/Delete';
|
|
6
|
-
import { Card, CardContent, CardHeader, FormHelperText, IconButton, InputAdornment, InputLabel, TextField, } from '@mui/material';
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
2
|
import { observer } from 'mobx-react';
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
marginTop: theme.spacing(1),
|
|
12
|
-
},
|
|
13
|
-
}));
|
|
3
|
+
import MapSlotEditor from './MapSlotEditor.js';
|
|
4
|
+
import NumberEditor from './NumberEditor.js';
|
|
5
|
+
/** #slotEditor one card per key, each holding that key's numeric field */
|
|
14
6
|
const NumberMapEditor = observer(function NumberMapEditor({ slot, }) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
slot.remove(key);
|
|
19
|
-
}, children: _jsx(DeleteIcon, {}) }) }), _jsx(CardContent, { children: _jsx(NumberEditor, { slot: {
|
|
20
|
-
value: val,
|
|
21
|
-
set: (val) => {
|
|
22
|
-
slot.add(key, val);
|
|
23
|
-
},
|
|
24
|
-
} }) })] }, key))), _jsx(Card, { raised: true, className: classes.card, children: _jsx(CardHeader, { disableTypography: true, title: _jsx(TextField, { fullWidth: true, value: value, placeholder: "add new", onChange: event => {
|
|
25
|
-
setValue(event.target.value);
|
|
26
|
-
}, slotProps: {
|
|
27
|
-
input: {
|
|
28
|
-
endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsx(IconButton, { disabled: value === '', onClick: () => {
|
|
29
|
-
slot.add(value, 0);
|
|
30
|
-
setValue('');
|
|
31
|
-
}, children: _jsx(AddIcon, {}) }) })),
|
|
32
|
-
},
|
|
33
|
-
} }) }) }), _jsx(FormHelperText, { children: slot.description })] }));
|
|
7
|
+
return (_jsx(MapSlotEditor, { name: slot.name, description: slot.description, entries: [...slot.value], emptyValue: 0, setMap: val => {
|
|
8
|
+
slot.set(val);
|
|
9
|
+
}, renderValue: (val, set) => _jsx(NumberEditor, { slot: { value: val, set } }) }));
|
|
34
10
|
});
|
|
35
11
|
export default NumberMapEditor;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
declare const SlotEditor: ({ slot,
|
|
3
|
-
slot:
|
|
4
|
-
|
|
5
|
-
}) => import("react/jsx-runtime").JSX.Element;
|
|
1
|
+
import type { SlotFacade } from '@jbrowse/core/configuration';
|
|
2
|
+
declare const SlotEditor: ({ slot, }: {
|
|
3
|
+
slot: SlotFacade;
|
|
4
|
+
}) => import("react").JSX.Element;
|
|
6
5
|
export default SlotEditor;
|
|
@@ -1,57 +1,65 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { isCallbackValue, toCallbackValue, toFixedValue, } from '@jbrowse/core/configuration';
|
|
3
4
|
import { FileSelector } from '@jbrowse/core/ui';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import RadioButtonUncheckedIcon from '@mui/icons-material/RadioButtonUnchecked';
|
|
8
|
-
import { IconButton, MenuItem, Paper, SvgIcon, TextField } from '@mui/material';
|
|
5
|
+
import CodeIcon from '@mui/icons-material/Code';
|
|
6
|
+
import RestartAltIcon from '@mui/icons-material/RestartAlt';
|
|
7
|
+
import { IconButton, MenuItem, Paper, TextField, Tooltip } from '@mui/material';
|
|
9
8
|
import { observer } from 'mobx-react';
|
|
10
|
-
import BooleanEditor from
|
|
11
|
-
import CallbackEditor from
|
|
12
|
-
import ColorEditor from
|
|
13
|
-
import ConfigurationTextField from
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import
|
|
9
|
+
import BooleanEditor from './BooleanEditor.js';
|
|
10
|
+
import CallbackEditor from './CallbackEditor.js';
|
|
11
|
+
import ColorEditor from './ColorEditor.js';
|
|
12
|
+
import ConfigurationTextField from './ConfigurationTextField.js';
|
|
13
|
+
import IntegerEditor from './IntegerEditor.js';
|
|
14
|
+
import JsonEditor from './JsonEditor.js';
|
|
15
|
+
import NumberEditor from './NumberEditor.js';
|
|
16
|
+
import NumberMapEditor from './NumberMapEditor.js';
|
|
17
|
+
import StringArrayEditor from './StringArrayEditor.js';
|
|
18
|
+
import StringArrayMapEditor from './StringArrayMapEditor.js';
|
|
19
|
+
import { useSlotEditorStyles } from './useSlotEditorStyles.js';
|
|
20
|
+
/** #slotEditor single-line text field */
|
|
20
21
|
const StringEditor = observer(function StringEditor({ slot, }) {
|
|
21
22
|
return (_jsx(ConfigurationTextField, { label: slot.name, helperText: slot.description, value: slot.value, onChange: evt => {
|
|
22
23
|
slot.set(evt.target.value);
|
|
23
24
|
} }));
|
|
24
25
|
});
|
|
26
|
+
/** #slotEditor multi-line textarea */
|
|
25
27
|
const TextEditor = observer(function TextEditor({ slot, }) {
|
|
26
28
|
return (_jsx(TextField, { label: slot.name, helperText: slot.description, multiline: true, value: slot.value, onChange: evt => {
|
|
27
29
|
slot.set(evt.target.value);
|
|
28
30
|
} }));
|
|
29
31
|
});
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
slot.set(
|
|
48
|
-
}, children: choices.map(str => (_jsx(MenuItem, { value: str, children: str }, str))) }));
|
|
32
|
+
// Sentinel for "leave the slot unset" in the maybe-enum dropdown. MUI needs a
|
|
33
|
+
// concrete option value, and '' can't collide with an enum member (an empty
|
|
34
|
+
// enumeration member would be unnameable in a menu anyway).
|
|
35
|
+
const UNSET_CHOICE = '';
|
|
36
|
+
/** #slotEditor dropdown of the `model`'s members */
|
|
37
|
+
const StringEnumEditor = observer(function StringEnumEditor({ slot, }) {
|
|
38
|
+
const nullable = slot.type === 'maybeStringEnum';
|
|
39
|
+
// Naming the member unset resolves to, rather than a bare "default": every
|
|
40
|
+
// other choice in the list is a value the user can read off the plot, and
|
|
41
|
+
// this one was the only one that said nothing about what picking it draws.
|
|
42
|
+
// `promotedBase` is the honest half to show — the editor's target is often a
|
|
43
|
+
// detached config node, so it cannot see a value promoted this session.
|
|
44
|
+
const unsetLabel = typeof slot.promotedBase === 'string'
|
|
45
|
+
? `default (${slot.promotedBase})`
|
|
46
|
+
: 'default';
|
|
47
|
+
return (_jsxs(ConfigurationTextField, { value: slot.value ?? UNSET_CHOICE, label: slot.name, select: true, helperText: slot.description, onChange: evt => {
|
|
48
|
+
const { value } = evt.target;
|
|
49
|
+
slot.set(value === UNSET_CHOICE ? undefined : value);
|
|
50
|
+
}, children: [nullable ? (_jsx(MenuItem, { value: UNSET_CHOICE, children: _jsx("em", { children: unsetLabel }) })) : null, (slot.choices ?? []).map(str => (_jsx(MenuItem, { value: str, children: str }, str)))] }));
|
|
49
51
|
});
|
|
52
|
+
/** #slotEditor URL, local file path (desktop) or file blob (browser) */
|
|
50
53
|
const FileSelectorWrapper = observer(function FileSelectorWrapper({ slot, }) {
|
|
51
54
|
return (_jsx(FileSelector, { location: slot.value, setLocation: location => {
|
|
52
55
|
slot.set(location);
|
|
53
|
-
}, name: slot.name, description: slot.description, rootModel:
|
|
56
|
+
}, name: slot.name, description: slot.description, rootModel: slot.pluginManager.rootModel }));
|
|
54
57
|
});
|
|
58
|
+
// dynamic dispatch from a runtime slot `type` string to a typed editor. The
|
|
59
|
+
// `any` is irreducible here: each editor declares a narrow `slot.value` type
|
|
60
|
+
// (number, boolean, FileLocation, string[], ...) but makeSlotFacade can only
|
|
61
|
+
// type value as `unknown`, so no single registry prop type satisfies them all.
|
|
62
|
+
// The editors stay fully typed internally; only this lookup is untyped.
|
|
55
63
|
const valueComponents = {
|
|
56
64
|
string: StringEditor,
|
|
57
65
|
text: TextEditor,
|
|
@@ -60,27 +68,54 @@ const valueComponents = {
|
|
|
60
68
|
stringArrayMap: StringArrayMapEditor,
|
|
61
69
|
numberMap: NumberMapEditor,
|
|
62
70
|
number: NumberEditor,
|
|
71
|
+
maybeNumber: NumberEditor,
|
|
63
72
|
integer: IntegerEditor,
|
|
64
73
|
color: ColorEditor,
|
|
74
|
+
maybeColor: ColorEditor,
|
|
65
75
|
stringEnum: StringEnumEditor,
|
|
76
|
+
maybeStringEnum: StringEnumEditor,
|
|
66
77
|
boolean: BooleanEditor,
|
|
78
|
+
maybeBoolean: BooleanEditor,
|
|
67
79
|
frozen: JsonEditor,
|
|
68
|
-
|
|
80
|
+
maybeFrozen: JsonEditor,
|
|
69
81
|
};
|
|
70
|
-
const SlotEditor = observer(function SlotEditor({ slot,
|
|
82
|
+
const SlotEditor = observer(function SlotEditor({ slot, }) {
|
|
71
83
|
const { classes } = useSlotEditorStyles();
|
|
72
84
|
const { type } = slot;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
// editor mode is UI-only state, derived once from whether the stored value is
|
|
86
|
+
// a jexl callback. Toggling rewrites the value; typing a "jexl:" prefix into a
|
|
87
|
+
// value field does not auto-swap the editor, since this state only changes on
|
|
88
|
+
// the explicit toggle.
|
|
89
|
+
const [callbackMode, setCallbackMode] = useState(() => isCallbackValue(slot.value));
|
|
90
|
+
// bumped on reset to remount the value editor: the buffered editors (number,
|
|
91
|
+
// json, callback) seed internal state from slot.value only on mount, so an
|
|
92
|
+
// external set wouldn't otherwise show in the field
|
|
93
|
+
const [resetNonce, setResetNonce] = useState(0);
|
|
94
|
+
const TypedComponent = valueComponents[type];
|
|
95
|
+
if (!callbackMode && !TypedComponent) {
|
|
78
96
|
console.warn(`no slot editor defined for ${type}, editing as string`);
|
|
79
|
-
ValueComponent = StringEditor;
|
|
80
|
-
}
|
|
81
|
-
if (!(type in valueComponents)) {
|
|
82
|
-
console.warn(`SlotEditor needs to implement ${type}`);
|
|
83
97
|
}
|
|
84
|
-
|
|
98
|
+
const ValueComponent = callbackMode
|
|
99
|
+
? CallbackEditor
|
|
100
|
+
: (TypedComponent ?? StringEditor);
|
|
101
|
+
// a promotable slot's default is its inherit sentinel, so resetting it here
|
|
102
|
+
// doubles as "un-pin / follow the session-wide default"
|
|
103
|
+
const { modified } = slot;
|
|
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: () => {
|
|
105
|
+
slot.set(slot.defaultValue);
|
|
106
|
+
setCallbackMode(isCallbackValue(slot.defaultValue));
|
|
107
|
+
setResetNonce(nonce => nonce + 1);
|
|
108
|
+
}, children: _jsx(RestartAltIcon, {}) }) }) })) : null, slot.contextVariable.length ? (_jsx("div", { className: classes.slotModeSwitch, children: _jsx(Tooltip, { title: callbackMode
|
|
109
|
+
? 'Editing as a callback (Jexl expression). Click to use a fixed value.'
|
|
110
|
+
: 'Editing as a fixed value. Click to use a callback (Jexl expression).', children: _jsx(IconButton, { color: callbackMode ? 'primary' : 'default', onClick: () => {
|
|
111
|
+
if (callbackMode) {
|
|
112
|
+
slot.set(toFixedValue(slot.value, type, slot.defaultValue, slot.pluginManager.jexl));
|
|
113
|
+
setCallbackMode(false);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
slot.set(toCallbackValue(slot.value));
|
|
117
|
+
setCallbackMode(true);
|
|
118
|
+
}
|
|
119
|
+
}, children: _jsx(CodeIcon, {}) }) }) })) : null] }));
|
|
85
120
|
});
|
|
86
121
|
export default SlotEditor;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
+
/** #slotEditor "todolist" of text fields, one per entry, with add and delete */
|
|
1
2
|
declare const StringArrayEditor: ({ slot, }: {
|
|
2
3
|
slot: {
|
|
3
4
|
name: string;
|
|
4
5
|
value: string[];
|
|
5
|
-
|
|
6
|
-
removeAtIndex: (arg: number) => void;
|
|
7
|
-
add: (arg: string) => void;
|
|
6
|
+
set: (arg: string[]) => void;
|
|
8
7
|
description: string;
|
|
9
8
|
};
|
|
10
|
-
}) => import("react
|
|
9
|
+
}) => import("react").JSX.Element;
|
|
11
10
|
export default StringArrayEditor;
|
|
@@ -1,34 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useState } from 'react';
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
2
|
import DeleteIcon from '@mui/icons-material/Delete';
|
|
4
|
-
import {
|
|
3
|
+
import { FormHelperText, IconButton, InputAdornment, InputLabel, List, ListItem, TextField, } from '@mui/material';
|
|
5
4
|
import { observer } from 'mobx-react';
|
|
5
|
+
import AddNewField from './AddNewField.js';
|
|
6
|
+
/** #slotEditor "todolist" of text fields, one per entry, with add and delete */
|
|
6
7
|
const StringArrayEditor = observer(function StringArrayEditor({ slot, }) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
slot.setAtIndex(idx, evt.target.value);
|
|
8
|
+
const value = [...slot.value];
|
|
9
|
+
return (_jsxs(_Fragment, { children: [slot.name ? _jsx(InputLabel, { children: slot.name }) : null, _jsxs(List, { disablePadding: true, children: [value.map((val, idx) => (_jsx(ListItem, { disableGutters: true, children: _jsx(TextField, { fullWidth: true, value: val, onChange: evt => {
|
|
10
|
+
slot.set(value.map((v, i) => (i === idx ? evt.target.value : v)));
|
|
11
11
|
}, slotProps: {
|
|
12
12
|
input: {
|
|
13
13
|
endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsx(IconButton, { onClick: () => {
|
|
14
|
-
slot.
|
|
14
|
+
slot.set(value.filter((_, i) => i !== idx));
|
|
15
15
|
}, children: _jsx(DeleteIcon, {}) }) })),
|
|
16
16
|
},
|
|
17
|
-
} }) },
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
input: {
|
|
21
|
-
endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsxs(_Fragment, { children: [_jsx(Button, { color: "primary", variant: "contained", style: { margin: 2 }, "data-testid": `stringArrayAdd-${slot.name}`, onClick: () => {
|
|
22
|
-
setAddNew(false);
|
|
23
|
-
slot.add(value);
|
|
24
|
-
setValue('');
|
|
25
|
-
}, children: "OK" }), _jsx(Button, { color: "primary", variant: "contained", style: { margin: 2 }, onClick: () => {
|
|
26
|
-
setAddNew(false);
|
|
27
|
-
setValue('');
|
|
28
|
-
}, children: "Cancel" })] }) })),
|
|
29
|
-
},
|
|
30
|
-
} }) })) : null, _jsx(Button, { color: "primary", variant: "contained", style: { margin: 4 }, disabled: addNew, onClick: () => {
|
|
31
|
-
setAddNew(true);
|
|
32
|
-
}, children: "Add item" })] }), _jsx(FormHelperText, { children: slot.description })] }));
|
|
17
|
+
} }) }, idx))), _jsx(ListItem, { disableGutters: true, children: _jsx(AddNewField, { testid: `stringArrayAdd-${slot.name}`, onAdd: val => {
|
|
18
|
+
slot.set([...value, val]);
|
|
19
|
+
} }) })] }), _jsx(FormHelperText, { children: slot.description })] }));
|
|
33
20
|
});
|
|
34
21
|
export default StringArrayEditor;
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
+
/** #slotEditor one card per key, each holding that key's "todolist" of strings */
|
|
1
2
|
declare const StringArrayMapEditor: ({ slot, }: {
|
|
2
3
|
slot: {
|
|
3
4
|
name: string;
|
|
4
5
|
value: Map<string, string[]>;
|
|
5
|
-
|
|
6
|
-
add: (key: string, val: string[]) => void;
|
|
6
|
+
set: (val: Record<string, string[]>) => void;
|
|
7
7
|
description: string;
|
|
8
|
-
setAtKeyIndex: (key: string, idx: number, val: string) => void;
|
|
9
|
-
removeAtKeyIndex: (key: string, idx: number) => void;
|
|
10
|
-
addToKey: (key: string, val: string) => void;
|
|
11
8
|
};
|
|
12
|
-
}) => import("react
|
|
9
|
+
}) => import("react").JSX.Element;
|
|
13
10
|
export default StringArrayMapEditor;
|
|
@@ -1,43 +1,18 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
|
-
import AddIcon from '@mui/icons-material/Add';
|
|
5
|
-
import DeleteIcon from '@mui/icons-material/Delete';
|
|
6
|
-
import { Card, CardContent, CardHeader, FormHelperText, IconButton, InputAdornment, InputLabel, TextField, } from '@mui/material';
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
2
|
import { observer } from 'mobx-react';
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
marginTop: theme.spacing(1),
|
|
12
|
-
},
|
|
13
|
-
}));
|
|
3
|
+
import MapSlotEditor from './MapSlotEditor.js';
|
|
4
|
+
import StringArrayEditor from './StringArrayEditor.js';
|
|
5
|
+
/** #slotEditor one card per key, each holding that key's "todolist" of strings */
|
|
14
6
|
const StringArrayMapEditor = observer(function StringArrayMapEditor({ slot, }) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
},
|
|
26
|
-
removeAtIndex: (idx) => {
|
|
27
|
-
slot.removeAtKeyIndex(key, idx);
|
|
28
|
-
},
|
|
29
|
-
add: (val) => {
|
|
30
|
-
slot.addToKey(key, val);
|
|
31
|
-
},
|
|
32
|
-
} }) })] }, key))), _jsx(Card, { raised: true, className: classes.card, children: _jsx(CardHeader, { disableTypography: true, title: _jsx(TextField, { fullWidth: true, value: value, placeholder: "add new", onChange: event => {
|
|
33
|
-
setValue(event.target.value);
|
|
34
|
-
}, slotProps: {
|
|
35
|
-
input: {
|
|
36
|
-
endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsx(IconButton, { disabled: value === '', onClick: () => {
|
|
37
|
-
slot.add(value, []);
|
|
38
|
-
setValue('');
|
|
39
|
-
}, children: _jsx(AddIcon, {}) }) })),
|
|
40
|
-
},
|
|
41
|
-
} }) }) }), _jsx(FormHelperText, { children: slot.description })] }));
|
|
7
|
+
return (_jsx(MapSlotEditor, { name: slot.name, description: slot.description,
|
|
8
|
+
// deep copy so per-entry edits never reuse the live MST array nodes
|
|
9
|
+
entries: [...slot.value].map(([k, v]) => [k, [...v]]), emptyValue: [], setMap: val => {
|
|
10
|
+
slot.set(val);
|
|
11
|
+
}, renderValue: (val, set, key) => (_jsx(StringArrayEditor, { slot: {
|
|
12
|
+
name: '',
|
|
13
|
+
value: val,
|
|
14
|
+
description: `Values associated with entry ${key}`,
|
|
15
|
+
set,
|
|
16
|
+
} })) }));
|
|
42
17
|
});
|
|
43
18
|
export default StringArrayMapEditor;
|
|
@@ -4,5 +4,5 @@ declare const TypeSelector: ({ typeNameChoices, slot, slotName, onChange, }: {
|
|
|
4
4
|
slot: AnyConfigurationModel;
|
|
5
5
|
slotName: string;
|
|
6
6
|
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
7
|
-
}) => import("react
|
|
7
|
+
}) => import("react").JSX.Element;
|
|
8
8
|
export default TypeSelector;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { MenuItem, Paper, TextField } from '@mui/material';
|
|
3
3
|
import { observer } from 'mobx-react';
|
|
4
|
-
import { useSlotEditorStyles } from
|
|
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.type, label: "Type", select: true, helperText: `Type of ${slotName} to use`, fullWidth: true, onChange: onChange, children: typeNameChoices.map(str => (_jsx(MenuItem, { value: str, children: str }, str))) }) }) }));
|
|
7
|
+
return (_jsx(Paper, { className: classes.paper, children: _jsx("div", { className: classes.paperContent, children: _jsx(TextField, { value: slot.type, label: "Type", select: true, variant: "outlined", helperText: `Type of ${slotName} to use`, fullWidth: true, onChange: onChange, children: typeNameChoices.map(str => (_jsx(MenuItem, { value: str, children: str }, str))) }) }) }));
|
|
8
8
|
});
|
|
9
9
|
export default TypeSelector;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Singularize a config schema slot name for the per-entry label an array-typed
|
|
3
|
+
* sub-schema renders ("displays" -> "display LinearBasicDisplay").
|
|
4
|
+
*
|
|
5
|
+
* Slot names are developer-authored camelCase identifiers — `displays`,
|
|
6
|
+
* `tracks`, `connections` are the ones in tree — so the regular English endings
|
|
7
|
+
* are the whole domain. This replaced the `pluralize` dependency, whose value
|
|
8
|
+
* is its irregular-noun table, and nothing here is ever an irregular noun.
|
|
9
|
+
*
|
|
10
|
+
* The counterpart for label text, where the noun comes from a display rather
|
|
11
|
+
* than a schema key, is core's own `pluralize(count, noun)` in `stringUtils`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function singularSlotName(name: string): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Singularize a config schema slot name for the per-entry label an array-typed
|
|
3
|
+
* sub-schema renders ("displays" -> "display LinearBasicDisplay").
|
|
4
|
+
*
|
|
5
|
+
* Slot names are developer-authored camelCase identifiers — `displays`,
|
|
6
|
+
* `tracks`, `connections` are the ones in tree — so the regular English endings
|
|
7
|
+
* are the whole domain. This replaced the `pluralize` dependency, whose value
|
|
8
|
+
* is its irregular-noun table, and nothing here is ever an irregular noun.
|
|
9
|
+
*
|
|
10
|
+
* The counterpart for label text, where the noun comes from a display rather
|
|
11
|
+
* than a schema key, is core's own `pluralize(count, noun)` in `stringUtils`.
|
|
12
|
+
*/
|
|
13
|
+
export function singularSlotName(name) {
|
|
14
|
+
return /[^aeiou]ies$/.test(name)
|
|
15
|
+
? `${name.slice(0, -3)}y`
|
|
16
|
+
: /(?:s|sh|ch|x|z)es$/.test(name)
|
|
17
|
+
? name.slice(0, -2)
|
|
18
|
+
: // a trailing `s` after `a`/`i`/`u`/`s` belongs to a word that is already
|
|
19
|
+
// singular — `bias`, `alias`, `axis`, `status`, `class` — not to a plural
|
|
20
|
+
/[^saiu]s$/.test(name)
|
|
21
|
+
? name.slice(0, -1)
|
|
22
|
+
: name;
|
|
23
|
+
}
|
|
@@ -3,8 +3,8 @@ export declare const useSlotEditorStyles: (_params?: unknown, muiStyleOverridesP
|
|
|
3
3
|
classes?: Record<string, string>;
|
|
4
4
|
};
|
|
5
5
|
} | undefined) => {
|
|
6
|
-
classes: Record<"paper" | "paperContent" | "slotModeSwitch", string>;
|
|
7
|
-
theme: import("@
|
|
6
|
+
classes: Record<"paper" | "paperContent" | "resetButton" | "slotModeSwitch", string>;
|
|
7
|
+
theme: import("@jbrowse/core/ui/styleTheme").JBrowseStyleTheme;
|
|
8
8
|
css: import("@jbrowse/core/util/tss-react/types").Css;
|
|
9
9
|
cx: import("@jbrowse/core/util/tss-react/types").Cx;
|
|
10
10
|
};
|
|
@@ -6,7 +6,8 @@ export const useSlotEditorStyles = makeStyles()(theme => ({
|
|
|
6
6
|
position: 'relative',
|
|
7
7
|
},
|
|
8
8
|
paperContent: {
|
|
9
|
-
|
|
9
|
+
flex: 1,
|
|
10
|
+
minWidth: 0,
|
|
10
11
|
},
|
|
11
12
|
slotModeSwitch: {
|
|
12
13
|
width: 24,
|
|
@@ -15,4 +16,8 @@ export const useSlotEditorStyles = makeStyles()(theme => ({
|
|
|
15
16
|
justifyContent: 'center',
|
|
16
17
|
alignItems: 'center',
|
|
17
18
|
},
|
|
19
|
+
resetButton: {
|
|
20
|
+
display: 'flex',
|
|
21
|
+
alignItems: 'center',
|
|
22
|
+
},
|
|
18
23
|
}));
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { lazy } from 'react';
|
|
2
2
|
import { ConfigurationSchema } from '@jbrowse/core/configuration';
|
|
3
3
|
import { WidgetType } from '@jbrowse/core/pluggableElementTypes';
|
|
4
|
-
import HeadingComponent from
|
|
5
|
-
import stateModelFactory from
|
|
4
|
+
import HeadingComponent from './components/HeadingComponent.js';
|
|
5
|
+
import stateModelFactory from './model.js';
|
|
6
6
|
const configSchema = ConfigurationSchema('ConfigurationEditorWidget', {});
|
|
7
|
-
const LazyConfigurationEditorComponent = lazy(() => import(
|
|
7
|
+
const LazyConfigurationEditorComponent = lazy(() => import('./components/ConfigurationEditor.js'));
|
|
8
8
|
export default function registerConfigurationEditorWidget(pluginManager) {
|
|
9
9
|
pluginManager.addWidgetType(() => {
|
|
10
10
|
return new WidgetType({
|