@jbrowse/plugin-config 5.0.0-beta.10 → 5.0.0-beta.13
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/ConfigurationEditor.js +4 -4
- package/esm/ConfigurationEditorWidget/components/SlotEditor.js +4 -10
- package/esm/ConfigurationEditorWidget/index.js +1 -0
- package/esm/ConfigurationEditorWidget/model.d.ts +24 -1
- package/esm/ConfigurationEditorWidget/model.js +80 -47
- package/package.json +4 -4
|
@@ -71,8 +71,8 @@ function schemaMatches(schema, query) {
|
|
|
71
71
|
const { properties } = getMembers(schema);
|
|
72
72
|
return Object.entries(properties).some(([slotName, slotSchema]) => memberMatches(schema, slotName, slotSchema, query));
|
|
73
73
|
}
|
|
74
|
-
//
|
|
75
|
-
function
|
|
74
|
+
// narrower than the About dialog's Advanced card, which also takes callbacks
|
|
75
|
+
function hidesBehindAdvancedToggle(schema, slotName) {
|
|
76
76
|
return (isConfigurationSlot(schema, slotName) &&
|
|
77
77
|
!!getSlotDefinition(schema, slotName).advanced);
|
|
78
78
|
}
|
|
@@ -176,8 +176,8 @@ const Schema = observer(function Schema({ schema, path = [], filter = '', expand
|
|
|
176
176
|
const query = filter.toLowerCase();
|
|
177
177
|
const properties = getMembers(schema).properties;
|
|
178
178
|
const visible = Object.entries(properties).filter(([slotName, slotSchema]) => !query || memberMatches(schema, slotName, slotSchema, query));
|
|
179
|
-
const normal = visible.filter(([slotName]) => !
|
|
180
|
-
const advanced = visible.filter(([slotName]) =>
|
|
179
|
+
const normal = visible.filter(([slotName]) => !hidesBehindAdvancedToggle(schema, slotName));
|
|
180
|
+
const advanced = visible.filter(([slotName]) => hidesBehindAdvancedToggle(schema, slotName));
|
|
181
181
|
const renderMember = ([slotName, slotSchema]) => (_jsx(Member, { slotName: slotName, slotSchema: slotSchema, path: path, schema: schema, filter: filter, expandedDisplayId: expandedDisplayId }, slotName));
|
|
182
182
|
return (_jsxs(_Fragment, { children: [normal.map(renderMember), advanced.length && !query ? (_jsxs(Button, { className: classes.advancedToggle, size: "small", startIcon: showAdvanced ? _jsx(ExpandLessIcon, {}) : _jsx(ExpandMoreIcon, {}), onClick: () => {
|
|
183
183
|
setShowAdvanced(!showAdvanced);
|
|
@@ -52,11 +52,9 @@ const FileSelectorWrapper = observer(function FileSelectorWrapper({ slot, }) {
|
|
|
52
52
|
slot.set(location);
|
|
53
53
|
}, name: slot.name, description: slot.description, rootModel: slot.pluginManager.rootModel }));
|
|
54
54
|
});
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
// type value as `unknown`, so no single registry prop type satisfies them all.
|
|
59
|
-
// The editors stay fully typed internally; only this lookup is untyped.
|
|
55
|
+
// The `any` is irreducible: each editor declares a narrow `slot.value` type, but
|
|
56
|
+
// makeSlotFacade can only type value as `unknown`. The keys are checked: a slot
|
|
57
|
+
// type with no editor is a compile error.
|
|
60
58
|
const valueComponents = {
|
|
61
59
|
string: StringEditor,
|
|
62
60
|
maybeString: StringEditor,
|
|
@@ -97,13 +95,9 @@ const SlotEditor = observer(function SlotEditor({ slot, }) {
|
|
|
97
95
|
// json, callback) seed internal state from slot.value only on mount, so an
|
|
98
96
|
// external set wouldn't otherwise show in the field
|
|
99
97
|
const [resetNonce, setResetNonce] = useState(0);
|
|
100
|
-
const TypedComponent = valueComponents[type];
|
|
101
|
-
if (!callbackMode && !TypedComponent) {
|
|
102
|
-
console.warn(`no slot editor defined for ${type}, editing as string`);
|
|
103
|
-
}
|
|
104
98
|
const ValueComponent = callbackMode
|
|
105
99
|
? CallbackEditor
|
|
106
|
-
:
|
|
100
|
+
: valueComponents[type];
|
|
107
101
|
const { modified } = slot;
|
|
108
102
|
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: () => {
|
|
109
103
|
slot.set(slot.defaultValue);
|
|
@@ -9,6 +9,7 @@ export default function registerConfigurationEditorWidget(pluginManager) {
|
|
|
9
9
|
pluginManager.addWidgetType(() => {
|
|
10
10
|
return new WidgetType({
|
|
11
11
|
name: 'ConfigurationEditorWidget',
|
|
12
|
+
discardOnClose: true,
|
|
12
13
|
HeadingComponent,
|
|
13
14
|
configSchema,
|
|
14
15
|
stateModel: stateModelFactory(pluginManager),
|
|
@@ -8,11 +8,34 @@ import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
|
8
8
|
export default function stateModelFactory(_pluginManager: PluginManager): import("@jbrowse/mobx-state-tree").IModelType<{
|
|
9
9
|
id: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
10
10
|
type: import("@jbrowse/mobx-state-tree").ILiteralType<"ConfigurationEditorWidget">;
|
|
11
|
+
/**
|
|
12
|
+
* #property
|
|
13
|
+
* the track whose working copy this edits, re-resolved on every read so
|
|
14
|
+
* an undo that replaces the copy moves the editor with it
|
|
15
|
+
*/
|
|
16
|
+
trackId: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<string>>;
|
|
11
17
|
}, {
|
|
12
|
-
|
|
18
|
+
inlineTarget: AnyConfigurationModel | undefined;
|
|
13
19
|
expandedDisplayId: string | undefined;
|
|
14
20
|
} & {
|
|
21
|
+
/**
|
|
22
|
+
* #getter
|
|
23
|
+
*/
|
|
24
|
+
readonly target: AnyConfigurationModel | undefined;
|
|
25
|
+
} & {
|
|
26
|
+
/**
|
|
27
|
+
* #action
|
|
28
|
+
* edit the working copy of `trackId`, saving any edit still pending
|
|
29
|
+
* on the previous target first
|
|
30
|
+
*/
|
|
31
|
+
setTrackId(trackId: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* #action
|
|
34
|
+
* edit a config node directly, saving any edit still pending on the
|
|
35
|
+
* previous target first
|
|
36
|
+
*/
|
|
15
37
|
setTarget(newTarget: AnyConfigurationModel | undefined): void;
|
|
16
38
|
setExpandedDisplayId(displayId: string | undefined): void;
|
|
17
39
|
afterCreate(): void;
|
|
40
|
+
beforeDetach(): void;
|
|
18
41
|
}, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getSession } from '@jbrowse/core/util';
|
|
2
|
+
import { getEditableTrackConfigById } from '@jbrowse/core/util/types';
|
|
2
3
|
import { ElementId } from '@jbrowse/core/util/types/mst';
|
|
3
4
|
import { addDisposer, getSnapshot, types } from '@jbrowse/mobx-state-tree';
|
|
4
5
|
import { autorun } from 'mobx';
|
|
@@ -12,60 +13,92 @@ export default function stateModelFactory(_pluginManager) {
|
|
|
12
13
|
.model('ConfigurationEditorWidget', {
|
|
13
14
|
id: ElementId,
|
|
14
15
|
type: types.literal('ConfigurationEditorWidget'),
|
|
16
|
+
/**
|
|
17
|
+
* #property
|
|
18
|
+
* the track whose working copy this edits, re-resolved on every read so
|
|
19
|
+
* an undo that replaces the copy moves the editor with it
|
|
20
|
+
*/
|
|
21
|
+
trackId: types.maybe(types.string),
|
|
15
22
|
})
|
|
16
23
|
.volatile(() => ({
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
target: undefined,
|
|
24
|
+
// a config the session cannot resolve by trackId: an assembly, a
|
|
25
|
+
// connection, or a track a view holds inline
|
|
26
|
+
inlineTarget: undefined,
|
|
21
27
|
// displayId of the display active in the view this editor was opened
|
|
22
28
|
// from; its config accordion expands by default while the track's other
|
|
23
29
|
// (incompatible/inactive) displays start collapsed
|
|
24
30
|
expandedDisplayId: undefined,
|
|
25
31
|
}))
|
|
26
|
-
.
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
.views(self => ({
|
|
33
|
+
/**
|
|
34
|
+
* #getter
|
|
35
|
+
*/
|
|
36
|
+
get target() {
|
|
37
|
+
return self.trackId === undefined
|
|
38
|
+
? self.inlineTarget
|
|
39
|
+
: getEditableTrackConfigById(getSession(self), self.trackId);
|
|
29
40
|
},
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
41
|
+
}))
|
|
42
|
+
.actions(self => {
|
|
43
|
+
let timeout;
|
|
44
|
+
let unsaved;
|
|
45
|
+
function save() {
|
|
46
|
+
clearTimeout(timeout);
|
|
47
|
+
if (unsaved) {
|
|
48
|
+
const session = getSession(self);
|
|
49
|
+
session.updateTrackConfiguration(unsaved);
|
|
50
|
+
unsaved = undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
/**
|
|
55
|
+
* #action
|
|
56
|
+
* edit the working copy of `trackId`, saving any edit still pending
|
|
57
|
+
* on the previous target first
|
|
58
|
+
*/
|
|
59
|
+
setTrackId(trackId) {
|
|
60
|
+
save();
|
|
61
|
+
self.inlineTarget = undefined;
|
|
62
|
+
self.trackId = trackId;
|
|
63
|
+
},
|
|
64
|
+
/**
|
|
65
|
+
* #action
|
|
66
|
+
* edit a config node directly, saving any edit still pending on the
|
|
67
|
+
* previous target first
|
|
68
|
+
*/
|
|
69
|
+
setTarget(newTarget) {
|
|
70
|
+
save();
|
|
71
|
+
self.trackId = undefined;
|
|
72
|
+
self.inlineTarget = newTarget;
|
|
73
|
+
},
|
|
74
|
+
setExpandedDisplayId(displayId) {
|
|
75
|
+
self.expandedDisplayId = displayId;
|
|
76
|
+
},
|
|
77
|
+
afterCreate() {
|
|
78
|
+
// Saves through updateTrackConfiguration as a per-track delta. A
|
|
79
|
+
// config with no trackId (assembly/connection) finds no home there,
|
|
80
|
+
// so the edit stays on the live MST node for this session.
|
|
81
|
+
//
|
|
82
|
+
// BaseTrackModel runs a sibling saver on a shown track's working
|
|
83
|
+
// copy, which this widget edits too; this one covers a track that is
|
|
84
|
+
// not shown. When both fire they compute an identical delta, deduped
|
|
85
|
+
// in updateTrackConfiguration.
|
|
86
|
+
addDisposer(self, autorun(() => {
|
|
87
|
+
if (self.target) {
|
|
88
|
+
unsaved = getSnapshot(self.target);
|
|
89
|
+
clearTimeout(timeout);
|
|
90
|
+
timeout = setTimeout(save, 400);
|
|
91
|
+
}
|
|
92
|
+
}));
|
|
93
|
+
addDisposer(self, () => {
|
|
57
94
|
clearTimeout(timeout);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
clearTimeout(timeout);
|
|
68
|
-
});
|
|
69
|
-
},
|
|
70
|
-
}));
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
// closing the editor detaches it from the session, so an edit still
|
|
98
|
+
// inside the debounce saves now, while getSession(self) resolves
|
|
99
|
+
beforeDetach() {
|
|
100
|
+
save();
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
});
|
|
71
104
|
}
|
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.13",
|
|
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.
|
|
24
|
+
"@jbrowse/mobx-state-tree": "^6.7.0",
|
|
25
25
|
"@mui/icons-material": "^9.4.0",
|
|
26
26
|
"@mui/material": "^9.4.0",
|
|
27
|
-
"mobx": "^7.0.
|
|
27
|
+
"mobx": "^7.0.6",
|
|
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.13"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"react": ">=19.0.0",
|