@jbrowse/plugin-data-management 4.0.3 → 4.0.4
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/AddTrackWidget/components/ConfirmTrack.js +11 -12
- package/esm/AddTrackWidget/components/PasteConfigWorkflow.js +1 -1
- package/esm/AddTrackWidget/components/TextIndexingConfig.js +18 -26
- package/esm/AddTrackWidget/components/TrackSourceSelect.js +1 -1
- package/esm/AddTrackWidget/components/doSubmit.js +1 -1
- package/esm/AddTrackWidget/model.d.ts +5 -11
- package/esm/AddTrackWidget/model.js +40 -38
- package/esm/UCSCTrackHubConnection/doConnect.js +2 -2
- package/package.json +5 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Suspense, useEffect
|
|
2
|
+
import { Suspense, useEffect } from 'react';
|
|
3
3
|
import { AssemblySelector } from '@jbrowse/core/ui';
|
|
4
4
|
import { getEnv, getSession, isElectron, isSupportedIndexingAdapter, } from '@jbrowse/core/util';
|
|
5
5
|
import { UNKNOWN } from '@jbrowse/core/util/tracks';
|
|
@@ -16,17 +16,21 @@ const useStyles = makeStyles()(theme => ({
|
|
|
16
16
|
spacing: {
|
|
17
17
|
marginBottom: theme.spacing(3),
|
|
18
18
|
},
|
|
19
|
+
selectorsContainer: {
|
|
20
|
+
display: 'flex',
|
|
21
|
+
flexDirection: 'column',
|
|
22
|
+
gap: 10,
|
|
23
|
+
},
|
|
19
24
|
}));
|
|
20
25
|
const ConfirmTrack = observer(function ConfirmTrack({ model, }) {
|
|
21
26
|
const { classes } = useStyles();
|
|
22
|
-
const [check, setCheck] = useState(true);
|
|
23
27
|
const session = getSession(model);
|
|
24
|
-
const { trackName, unsupported, trackAdapter, trackType, warningMessage, adapterHint, } = model;
|
|
28
|
+
const { trackName, unsupported, trackAdapter, trackType, warningMessage, adapterHint, textIndexTrack, } = model;
|
|
25
29
|
useEffect(() => {
|
|
26
30
|
if (adapterHint === '' && trackAdapter) {
|
|
27
31
|
model.setAdapterHint(trackAdapter.type);
|
|
28
32
|
}
|
|
29
|
-
}, [adapterHint, trackAdapter,
|
|
33
|
+
}, [adapterHint, trackAdapter, model]);
|
|
30
34
|
if (unsupported) {
|
|
31
35
|
return _jsx(Unsupported, {});
|
|
32
36
|
}
|
|
@@ -49,20 +53,15 @@ const ConfirmTrack = observer(function ConfirmTrack({ model, }) {
|
|
|
49
53
|
},
|
|
50
54
|
},
|
|
51
55
|
} })), { model });
|
|
52
|
-
return (_jsxs("div", { children: [_jsx(StatusMessage, { trackAdapter: trackAdapter, trackType: trackType }), warningMessage ? (_jsx(Typography, { color: "warning", children: warningMessage })) : null, _jsx(TextField, { className: classes.spacing, label: "
|
|
56
|
+
return (_jsxs("div", { children: [_jsx(StatusMessage, { trackAdapter: trackAdapter, trackType: trackType }), warningMessage ? (_jsx(Typography, { color: "warning", children: warningMessage })) : null, _jsx(TextField, { className: classes.spacing, label: "Track name", helperText: "A name for this track", fullWidth: true, value: trackName, onChange: event => {
|
|
53
57
|
model.setTrackName(event.target.value);
|
|
54
58
|
}, slotProps: {
|
|
55
59
|
htmlInput: {
|
|
56
60
|
'data-testid': 'trackNameInput',
|
|
57
61
|
},
|
|
58
|
-
} }), _jsxs("div", {
|
|
59
|
-
display: 'flex',
|
|
60
|
-
flexDirection: 'column',
|
|
61
|
-
gap: 10,
|
|
62
|
-
}, children: [_jsx(TrackAdapterSelector, { model: model }), _jsx(TrackTypeSelector, { model: model }), _jsx(Suspense, { fallback: null, children: _jsx(Component, { model: model }) })] }), isElectron && supportedForIndexing && (_jsx(FormControl, { children: _jsx(FormControlLabel, { label: "Index track for text searching?", control: _jsx(Checkbox, { checked: check, onChange: e => {
|
|
63
|
-
setCheck(e.target.checked);
|
|
62
|
+
} }), _jsxs("div", { className: classes.selectorsContainer, children: [_jsx(TrackAdapterSelector, { model: model }), _jsx(TrackTypeSelector, { model: model }), _jsx(Suspense, { fallback: null, children: _jsx(Component, { model: model }) })] }), isElectron && supportedForIndexing && (_jsx(FormControl, { children: _jsx(FormControlLabel, { label: "Index track for text searching?", control: _jsx(Checkbox, { checked: textIndexTrack, onChange: e => {
|
|
64
63
|
model.setTextIndexTrack(e.target.checked);
|
|
65
|
-
} }) }) })), isElectron &&
|
|
64
|
+
} }) }) })), isElectron && textIndexTrack && supportedForIndexing ? (_jsx(TextIndexingConfig, { model: model })) : null] }));
|
|
66
65
|
}
|
|
67
66
|
});
|
|
68
67
|
export default ConfirmTrack;
|
|
@@ -17,54 +17,46 @@ const useStyles = makeStyles()(theme => ({
|
|
|
17
17
|
}));
|
|
18
18
|
const TextIndexingConfig = observer(function TextIndexingConfig({ model, }) {
|
|
19
19
|
const { classes } = useStyles();
|
|
20
|
-
const [
|
|
21
|
-
const [
|
|
20
|
+
const [attributeInput, setAttributeInput] = useState('');
|
|
21
|
+
const [excludeInput, setExcludeInput] = useState('');
|
|
22
22
|
const [attributes, setAttributes] = useState(['Name', 'ID']);
|
|
23
23
|
const [exclude, setExclude] = useState(['CDS', 'exon']);
|
|
24
24
|
const sections = [
|
|
25
25
|
{
|
|
26
26
|
label: 'Indexing attributes',
|
|
27
27
|
values: attributes,
|
|
28
|
+
setValues: setAttributes,
|
|
29
|
+
inputValue: attributeInput,
|
|
30
|
+
setInputValue: setAttributeInput,
|
|
28
31
|
},
|
|
29
32
|
{
|
|
30
33
|
label: 'Feature types to exclude',
|
|
31
34
|
values: exclude,
|
|
35
|
+
setValues: setExclude,
|
|
36
|
+
inputValue: excludeInput,
|
|
37
|
+
setInputValue: setExcludeInput,
|
|
32
38
|
},
|
|
33
39
|
];
|
|
34
40
|
useEffect(() => {
|
|
35
41
|
model.setTextIndexingConf({ attributes, exclude });
|
|
36
42
|
}, [model, attributes, exclude]);
|
|
37
|
-
return (_jsxs(Paper, { className: classes.paper, children: [_jsx(InputLabel, { children: "Indexing configuration" }), sections.map(
|
|
43
|
+
return (_jsxs(Paper, { className: classes.paper, children: [_jsx(InputLabel, { children: "Indexing configuration" }), sections.map(section => (_jsx(Card, { raised: true, className: classes.card, children: _jsxs(CardContent, { children: [_jsx(InputLabel, { children: section.label }), _jsxs(List, { disablePadding: true, children: [section.values.map((val, idx) => (_jsx(ListItem, { disableGutters: true, children: _jsx(TextField, { value: val, slotProps: {
|
|
38
44
|
input: {
|
|
39
45
|
endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsx(IconButton, { onClick: () => {
|
|
40
|
-
|
|
41
|
-
if (index === 0) {
|
|
42
|
-
setAttributes(newAttr);
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
setExclude(newAttr);
|
|
46
|
-
}
|
|
46
|
+
section.setValues(section.values.filter((_, i) => i !== idx));
|
|
47
47
|
}, children: _jsx(DeleteIcon, {}) }) })),
|
|
48
48
|
},
|
|
49
|
-
} }) }, `${val}-${idx}`))), _jsx(ListItem, { disableGutters: true, children: _jsx(TextField, { value:
|
|
50
|
-
|
|
51
|
-
setValue1(event.target.value);
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
setValue2(event.target.value);
|
|
55
|
-
}
|
|
49
|
+
} }) }, `${val}-${idx}`))), _jsx(ListItem, { disableGutters: true, children: _jsx(TextField, { value: section.inputValue, placeholder: "add new", onChange: event => {
|
|
50
|
+
section.setInputValue(event.target.value);
|
|
56
51
|
}, slotProps: {
|
|
57
52
|
input: {
|
|
58
53
|
endAdornment: (_jsx(InputAdornment, { position: "end", children: _jsx(IconButton, { onClick: () => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
setValue2('');
|
|
66
|
-
}
|
|
67
|
-
}, disabled: index === 0 ? value1 === '' : value2 === '', "data-testid": "stringArrayAdd-Feat", children: _jsx(AddIcon, {}) }) })),
|
|
54
|
+
section.setValues([
|
|
55
|
+
...section.values,
|
|
56
|
+
section.inputValue,
|
|
57
|
+
]);
|
|
58
|
+
section.setInputValue('');
|
|
59
|
+
}, disabled: section.inputValue === '', "data-testid": "stringArrayAdd-Feat", children: _jsx(AddIcon, {}) }) })),
|
|
68
60
|
},
|
|
69
61
|
} }) })] })] }) }, section.label)))] }));
|
|
70
62
|
});
|
|
@@ -15,6 +15,6 @@ const useStyles = makeStyles()(theme => ({
|
|
|
15
15
|
const TrackSourceSelect = observer(function TrackSourceSelect({ model, }) {
|
|
16
16
|
const { classes } = useStyles();
|
|
17
17
|
const rootModel = getRoot(model);
|
|
18
|
-
return (_jsxs(Paper, { className: classes.paper, children: [_jsx(FileSelector, { name: "Main file", description: "", location: model.trackData, setLocation: model.setTrackData, setName: model.setTrackName, rootModel: rootModel }), _jsx("div", { className: classes.spacer }), _jsx(FileSelector, { name: "Index file", description: "(Optional) The URL of the index file is automatically inferred from the URL of the main file if it is not supplied.", location: model.indexTrackData, setLocation: model.setIndexTrackData,
|
|
18
|
+
return (_jsxs(Paper, { className: classes.paper, children: [_jsx(FileSelector, { name: "Main file", description: "", location: model.trackData, setLocation: model.setTrackData, setName: model.setTrackName, rootModel: rootModel }), _jsx("div", { className: classes.spacer }), _jsx(FileSelector, { name: "Index file", description: "(Optional) The URL of the index file is automatically inferred from the URL of the main file if it is not supplied.", location: model.indexTrackData, setLocation: model.setIndexTrackData, rootModel: rootModel })] }));
|
|
19
19
|
});
|
|
20
20
|
export default TrackSourceSelect;
|
|
@@ -10,7 +10,6 @@ export default function f(pluginManager: PluginManager): import("@jbrowse/mobx-s
|
|
|
10
10
|
type: import("@jbrowse/mobx-state-tree").ISimpleType<"AddTrackWidget">;
|
|
11
11
|
view: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").IReferenceType<import("@jbrowse/mobx-state-tree").IAnyType>>;
|
|
12
12
|
}, {
|
|
13
|
-
trackSource: string;
|
|
14
13
|
trackData: FileLocation | undefined;
|
|
15
14
|
indexTrackData: FileLocation | undefined;
|
|
16
15
|
altAssemblyName: string;
|
|
@@ -19,11 +18,10 @@ export default function f(pluginManager: PluginManager): import("@jbrowse/mobx-s
|
|
|
19
18
|
adapterHint: string;
|
|
20
19
|
textIndexTrack: boolean;
|
|
21
20
|
textIndexingConf: IndexingAttr | undefined;
|
|
22
|
-
mixinData:
|
|
21
|
+
mixinData: Record<string, unknown>;
|
|
23
22
|
} & {
|
|
24
23
|
setMixinData(arg: Record<string, unknown>): void;
|
|
25
24
|
setAdapterHint(obj: string): void;
|
|
26
|
-
setTrackSource(str: string): void;
|
|
27
25
|
setTextIndexingConf(conf: IndexingAttr): void;
|
|
28
26
|
setTextIndexTrack(flag: boolean): void;
|
|
29
27
|
setTrackData(obj: FileLocation): void;
|
|
@@ -39,20 +37,16 @@ export default function f(pluginManager: PluginManager): import("@jbrowse/mobx-s
|
|
|
39
37
|
readonly isRelativeTrackUrl: boolean;
|
|
40
38
|
readonly isRelativeIndexUrl: boolean;
|
|
41
39
|
readonly isRelativeUrl: boolean;
|
|
42
|
-
readonly trackHttp:
|
|
43
|
-
readonly indexHttp:
|
|
44
|
-
readonly wrongProtocol:
|
|
40
|
+
readonly trackHttp: boolean | undefined;
|
|
41
|
+
readonly indexHttp: boolean | undefined;
|
|
42
|
+
readonly wrongProtocol: boolean | undefined;
|
|
45
43
|
readonly unsupported: boolean;
|
|
46
44
|
readonly assembly: any;
|
|
47
45
|
readonly trackAdapterType: string | undefined;
|
|
48
46
|
readonly trackType: string;
|
|
49
47
|
} & {
|
|
50
48
|
getTrackConfig(timestamp: number): {
|
|
51
|
-
|
|
52
|
-
type: string;
|
|
53
|
-
name: string;
|
|
54
|
-
assemblyNames: any[];
|
|
55
|
-
adapter: import("@jbrowse/core/util").AdapterConfig;
|
|
49
|
+
[x: string]: any;
|
|
56
50
|
} | undefined;
|
|
57
51
|
readonly warningMessage: "" | "Warning: JBrowse cannot access files using the ftp protocol" | "Warning: one or more of your files do not provide the protocol e.g.\n https://, please provide an absolute URL unless you are sure a\n relative URL is intended." | "Warning: You entered a http:// resources but we cannot access HTTP\n resources from JBrowse when it is running on https. Please use an\n https URL for your track, or access the JBrowse app from the http\n protocol";
|
|
58
52
|
}, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;
|
|
@@ -1,17 +1,31 @@
|
|
|
1
|
-
import { getSession } from '@jbrowse/core/util';
|
|
1
|
+
import { getSession, isUriLocation } from '@jbrowse/core/util';
|
|
2
2
|
import { UNSUPPORTED, getFileName, guessAdapter, guessTrackType, } from '@jbrowse/core/util/tracks';
|
|
3
3
|
import { ElementId } from '@jbrowse/core/util/types/mst';
|
|
4
4
|
import { types } from '@jbrowse/mobx-state-tree';
|
|
5
5
|
import deepmerge from 'deepmerge';
|
|
6
|
-
function
|
|
6
|
+
function getUri(location) {
|
|
7
|
+
return isUriLocation(location) ? location.uri : undefined;
|
|
8
|
+
}
|
|
9
|
+
function isRelativeUrl(url = '') {
|
|
7
10
|
try {
|
|
8
11
|
new URL(url);
|
|
9
|
-
return
|
|
12
|
+
return false;
|
|
10
13
|
}
|
|
11
|
-
catch
|
|
12
|
-
return url.startsWith('/');
|
|
14
|
+
catch {
|
|
15
|
+
return !url.startsWith('/');
|
|
13
16
|
}
|
|
14
17
|
}
|
|
18
|
+
const defaultVolatileState = {
|
|
19
|
+
trackData: undefined,
|
|
20
|
+
indexTrackData: undefined,
|
|
21
|
+
altAssemblyName: '',
|
|
22
|
+
altTrackName: '',
|
|
23
|
+
altTrackType: '',
|
|
24
|
+
adapterHint: '',
|
|
25
|
+
textIndexTrack: true,
|
|
26
|
+
textIndexingConf: undefined,
|
|
27
|
+
mixinData: {},
|
|
28
|
+
};
|
|
15
29
|
export default function f(pluginManager) {
|
|
16
30
|
return types
|
|
17
31
|
.model('AddTrackModel', {
|
|
@@ -19,18 +33,7 @@ export default function f(pluginManager) {
|
|
|
19
33
|
type: types.literal('AddTrackWidget'),
|
|
20
34
|
view: types.safeReference(pluginManager.pluggableMstType('view', 'stateModel')),
|
|
21
35
|
})
|
|
22
|
-
.volatile(() => ({
|
|
23
|
-
trackSource: 'fromFile',
|
|
24
|
-
trackData: undefined,
|
|
25
|
-
indexTrackData: undefined,
|
|
26
|
-
altAssemblyName: '',
|
|
27
|
-
altTrackName: '',
|
|
28
|
-
altTrackType: '',
|
|
29
|
-
adapterHint: '',
|
|
30
|
-
textIndexTrack: true,
|
|
31
|
-
textIndexingConf: undefined,
|
|
32
|
-
mixinData: {},
|
|
33
|
-
}))
|
|
36
|
+
.volatile(() => ({ ...defaultVolatileState }))
|
|
34
37
|
.actions(self => ({
|
|
35
38
|
setMixinData(arg) {
|
|
36
39
|
self.mixinData = arg;
|
|
@@ -38,9 +41,6 @@ export default function f(pluginManager) {
|
|
|
38
41
|
setAdapterHint(obj) {
|
|
39
42
|
self.adapterHint = obj;
|
|
40
43
|
},
|
|
41
|
-
setTrackSource(str) {
|
|
42
|
-
self.trackSource = str;
|
|
43
|
-
},
|
|
44
44
|
setTextIndexingConf(conf) {
|
|
45
45
|
self.textIndexingConf = conf;
|
|
46
46
|
},
|
|
@@ -53,6 +53,7 @@ export default function f(pluginManager) {
|
|
|
53
53
|
},
|
|
54
54
|
setIndexTrackData(obj) {
|
|
55
55
|
self.indexTrackData = obj;
|
|
56
|
+
self.adapterHint = '';
|
|
56
57
|
},
|
|
57
58
|
setAssembly(str) {
|
|
58
59
|
self.altAssemblyName = str;
|
|
@@ -64,15 +65,15 @@ export default function f(pluginManager) {
|
|
|
64
65
|
self.altTrackType = str;
|
|
65
66
|
},
|
|
66
67
|
clearData() {
|
|
67
|
-
self.
|
|
68
|
-
self.
|
|
69
|
-
self.
|
|
70
|
-
self.
|
|
71
|
-
self.
|
|
72
|
-
self.
|
|
73
|
-
self.
|
|
74
|
-
self.
|
|
75
|
-
self.
|
|
68
|
+
self.altTrackName = defaultVolatileState.altTrackName;
|
|
69
|
+
self.altTrackType = defaultVolatileState.altTrackType;
|
|
70
|
+
self.altAssemblyName = defaultVolatileState.altAssemblyName;
|
|
71
|
+
self.adapterHint = defaultVolatileState.adapterHint;
|
|
72
|
+
self.indexTrackData = defaultVolatileState.indexTrackData;
|
|
73
|
+
self.trackData = defaultVolatileState.trackData;
|
|
74
|
+
self.textIndexingConf = defaultVolatileState.textIndexingConf;
|
|
75
|
+
self.textIndexTrack = defaultVolatileState.textIndexTrack;
|
|
76
|
+
self.mixinData = {};
|
|
76
77
|
},
|
|
77
78
|
}))
|
|
78
79
|
.views(self => ({
|
|
@@ -87,25 +88,26 @@ export default function f(pluginManager) {
|
|
|
87
88
|
(self.trackData ? getFileName(self.trackData) : ''));
|
|
88
89
|
},
|
|
89
90
|
get isFtp() {
|
|
90
|
-
const
|
|
91
|
-
|
|
91
|
+
const trackUri = getUri(self.trackData);
|
|
92
|
+
const indexUri = getUri(self.indexTrackData);
|
|
93
|
+
return !!(indexUri?.startsWith('ftp://') || trackUri?.startsWith('ftp://'));
|
|
92
94
|
},
|
|
93
95
|
get isRelativeTrackUrl() {
|
|
94
|
-
const uri = self.trackData
|
|
95
|
-
return uri ?
|
|
96
|
+
const uri = getUri(self.trackData);
|
|
97
|
+
return uri ? isRelativeUrl(uri) : false;
|
|
96
98
|
},
|
|
97
99
|
get isRelativeIndexUrl() {
|
|
98
|
-
const uri = self.indexTrackData
|
|
99
|
-
return uri ?
|
|
100
|
+
const uri = getUri(self.indexTrackData);
|
|
101
|
+
return uri ? isRelativeUrl(uri) : false;
|
|
100
102
|
},
|
|
101
103
|
get isRelativeUrl() {
|
|
102
104
|
return this.isRelativeIndexUrl || this.isRelativeTrackUrl;
|
|
103
105
|
},
|
|
104
106
|
get trackHttp() {
|
|
105
|
-
return self.trackData?.
|
|
107
|
+
return getUri(self.trackData)?.startsWith('http://');
|
|
106
108
|
},
|
|
107
109
|
get indexHttp() {
|
|
108
|
-
return self.indexTrackData?.
|
|
110
|
+
return getUri(self.indexTrackData)?.startsWith('http://');
|
|
109
111
|
},
|
|
110
112
|
get wrongProtocol() {
|
|
111
113
|
return (window.location.protocol === 'https:' &&
|
|
@@ -115,7 +117,7 @@ export default function f(pluginManager) {
|
|
|
115
117
|
return this.trackAdapter?.type === UNSUPPORTED;
|
|
116
118
|
},
|
|
117
119
|
get assembly() {
|
|
118
|
-
return self.altAssemblyName || self.view
|
|
120
|
+
return self.altAssemblyName || self.view?.assemblyNames?.[0];
|
|
119
121
|
},
|
|
120
122
|
get trackAdapterType() {
|
|
121
123
|
return this.trackAdapter?.type;
|
|
@@ -2,7 +2,7 @@ import { HubFile, SingleFileHub } from '@gmod/ucsc-hub';
|
|
|
2
2
|
import { getConf } from '@jbrowse/core/configuration';
|
|
3
3
|
import { getEnv, getSession } from '@jbrowse/core/util';
|
|
4
4
|
import { openLocation } from '@jbrowse/core/util/io';
|
|
5
|
-
import {
|
|
5
|
+
import { createElementId } from '@jbrowse/core/util/types/mst';
|
|
6
6
|
import { generateTracks } from "./ucscTrackHub.js";
|
|
7
7
|
import { fetchGenomesFile, fetchTrackDbFile, resolve } from "./util.js";
|
|
8
8
|
export async function doConnect(self) {
|
|
@@ -34,7 +34,7 @@ export async function doConnect(self) {
|
|
|
34
34
|
}
|
|
35
35
|
: {}),
|
|
36
36
|
},
|
|
37
|
-
trackId: `${genomeName}-${
|
|
37
|
+
trackId: `${genomeName}-${createElementId()}`,
|
|
38
38
|
adapter: {
|
|
39
39
|
type: 'TwoBitAdapter',
|
|
40
40
|
twoBitLocation: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-data-management",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.4",
|
|
4
4
|
"description": "JBrowse 2 linear genome view",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jbrowse",
|
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
"@jbrowse/mobx-state-tree": "^5.5.0",
|
|
25
25
|
"@mui/icons-material": "^7.3.6",
|
|
26
26
|
"@mui/material": "^7.3.6",
|
|
27
|
-
"@mui/x-data-grid": "^8.
|
|
27
|
+
"@mui/x-data-grid": "^8.25.0",
|
|
28
28
|
"deepmerge": "^4.3.1",
|
|
29
29
|
"mobx": "^6.15.0",
|
|
30
30
|
"mobx-react": "^9.2.1",
|
|
31
|
-
"@jbrowse/
|
|
32
|
-
"@jbrowse/
|
|
33
|
-
"@jbrowse/
|
|
31
|
+
"@jbrowse/product-core": "^4.0.4",
|
|
32
|
+
"@jbrowse/core": "^4.0.4",
|
|
33
|
+
"@jbrowse/plugin-config": "^4.0.4"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"react": ">=18.0.0"
|