@jbrowse/app-core 3.7.0 → 4.0.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/AppFocus/index.d.ts +3 -3
- package/esm/AppFocus/index.js +1 -1
- package/esm/Assemblies/SessionAssembliesMixin.d.ts +47 -22
- package/esm/Assemblies/SessionAssembliesMixin.js +11 -1
- package/esm/Assemblies/TemporaryAssembliesMixin.d.ts +5 -3
- package/esm/Assemblies/TemporaryAssembliesMixin.js +11 -1
- package/esm/Assemblies/index.d.ts +2 -2
- package/esm/Assemblies/index.js +2 -2
- package/esm/DockviewLayout/index.d.ts +26 -0
- package/esm/DockviewLayout/index.js +107 -0
- package/esm/HistoryManagement/index.d.ts +7 -7
- package/esm/HistoryManagement/index.js +5 -6
- package/esm/JBrowseConfig/RootConfiguration.d.ts +15 -15
- package/esm/JBrowseConfig/index.d.ts +35 -25
- package/esm/JBrowseConfig/index.js +3 -3
- package/esm/JBrowseModel/index.d.ts +52 -34
- package/esm/JBrowseModel/index.js +15 -6
- package/esm/RootMenu/index.d.ts +3 -3
- package/esm/RootMenu/index.js +1 -1
- package/esm/index.d.ts +9 -8
- package/esm/index.js +9 -8
- package/esm/ui/App/App.d.ts +1 -1
- package/esm/ui/App/App.js +8 -8
- package/esm/ui/App/AppFab.js +2 -2
- package/esm/ui/App/AppToolbar.d.ts +1 -1
- package/esm/ui/App/AppToolbar.js +17 -3
- package/esm/ui/App/ClassicViewsContainer.d.ts +12 -0
- package/esm/ui/App/ClassicViewsContainer.js +21 -0
- package/esm/ui/App/DialogQueue.d.ts +2 -2
- package/esm/ui/App/DialogQueue.js +1 -1
- package/esm/ui/App/DockviewContext.d.ts +15 -0
- package/esm/ui/App/DockviewContext.js +21 -0
- package/esm/ui/App/DockviewLeftHeaderActions.d.ts +2 -0
- package/esm/ui/App/DockviewLeftHeaderActions.js +58 -0
- package/esm/ui/App/DockviewRightHeaderActions.d.ts +2 -0
- package/esm/ui/App/DockviewRightHeaderActions.js +100 -0
- package/esm/ui/App/Drawer.js +4 -5
- package/esm/ui/App/DrawerControls.js +1 -1
- package/esm/ui/App/DrawerHeader.js +16 -8
- package/{dist/ui/App/Drawer.d.ts → esm/ui/App/DrawerHeaderHelpButton.d.ts} +3 -3
- package/esm/ui/App/DrawerHeaderHelpButton.js +18 -0
- package/esm/ui/App/DrawerHeaderHelpDialog.d.ts +4 -0
- package/esm/ui/App/DrawerHeaderHelpDialog.js +15 -0
- package/esm/ui/App/DrawerWidget.js +4 -4
- package/esm/ui/App/DrawerWidgetSelector.js +3 -3
- package/esm/ui/App/JBrowseTabMenu.d.ts +5 -0
- package/esm/ui/App/JBrowseTabMenu.js +39 -0
- package/esm/ui/App/JBrowseViewPanel.d.ts +8 -0
- package/esm/ui/App/JBrowseViewPanel.js +49 -0
- package/esm/ui/App/JBrowseViewTab.d.ts +8 -0
- package/esm/ui/App/JBrowseViewTab.js +106 -0
- package/esm/ui/App/ModalWidget.js +3 -3
- package/esm/ui/App/TiledViewsContainer.d.ts +7 -0
- package/esm/ui/App/TiledViewsContainer.js +251 -0
- package/{dist/ui/App/ViewHeader.d.ts → esm/ui/App/ViewButtons.d.ts} +2 -3
- package/esm/ui/App/ViewButtons.js +17 -0
- package/esm/ui/App/ViewContainer.js +7 -8
- package/esm/ui/App/ViewContainerTitle.js +3 -4
- package/esm/ui/App/ViewHeader.js +7 -16
- package/esm/ui/App/ViewLauncher.d.ts +1 -1
- package/esm/ui/App/ViewLauncher.js +3 -4
- package/esm/ui/App/ViewMenu.d.ts +1 -1
- package/esm/ui/App/ViewMenu.js +113 -54
- package/esm/ui/App/ViewWrapper.js +1 -1
- package/esm/ui/App/ViewsContainer.d.ts +2 -2
- package/esm/ui/App/ViewsContainer.js +8 -6
- package/esm/ui/App/copyView.d.ts +1 -0
- package/esm/ui/App/copyView.js +29 -0
- package/esm/ui/App/dockviewUtils.d.ts +44 -0
- package/esm/ui/App/dockviewUtils.js +45 -0
- package/esm/ui/App/index.d.ts +2 -1
- package/esm/ui/App/index.js +2 -1
- package/esm/ui/App/types.d.ts +7 -0
- package/esm/ui/App/types.js +1 -0
- package/esm/ui/index.d.ts +1 -1
- package/esm/ui/index.js +1 -1
- package/package.json +27 -32
- package/dist/AppFocus/index.d.ts +0 -5
- package/dist/AppFocus/index.js +0 -15
- package/dist/Assemblies/SessionAssembliesMixin.d.ts +0 -67
- package/dist/Assemblies/SessionAssembliesMixin.js +0 -46
- package/dist/Assemblies/TemporaryAssembliesMixin.d.ts +0 -8
- package/dist/Assemblies/TemporaryAssembliesMixin.js +0 -30
- package/dist/Assemblies/index.d.ts +0 -2
- package/dist/Assemblies/index.js +0 -18
- package/dist/HistoryManagement/index.d.ts +0 -23
- package/dist/HistoryManagement/index.js +0 -47
- package/dist/JBrowseConfig/RootConfiguration.d.ts +0 -102
- package/dist/JBrowseConfig/RootConfiguration.js +0 -46
- package/dist/JBrowseConfig/index.d.ts +0 -115
- package/dist/JBrowseConfig/index.js +0 -26
- package/dist/JBrowseModel/index.d.ts +0 -142
- package/dist/JBrowseModel/index.js +0 -101
- package/dist/RootMenu/index.d.ts +0 -13
- package/dist/RootMenu/index.js +0 -74
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -24
- package/dist/menus.d.ts +0 -74
- package/dist/menus.js +0 -108
- package/dist/ui/App/App.d.ts +0 -18
- package/dist/ui/App/App.js +0 -82
- package/dist/ui/App/AppFab.d.ts +0 -5
- package/dist/ui/App/AppFab.js +0 -32
- package/dist/ui/App/AppToolbar.d.ts +0 -18
- package/dist/ui/App/AppToolbar.js +0 -44
- package/dist/ui/App/DialogQueue.d.ts +0 -5
- package/dist/ui/App/DialogQueue.js +0 -10
- package/dist/ui/App/Drawer.js +0 -51
- package/dist/ui/App/DrawerControls.d.ts +0 -5
- package/dist/ui/App/DrawerControls.js +0 -30
- package/dist/ui/App/DrawerHeader.d.ts +0 -7
- package/dist/ui/App/DrawerHeader.js +0 -37
- package/dist/ui/App/DrawerWidget.d.ts +0 -5
- package/dist/ui/App/DrawerWidget.js +0 -65
- package/dist/ui/App/DrawerWidgetSelector.d.ts +0 -5
- package/dist/ui/App/DrawerWidgetSelector.js +0 -52
- package/dist/ui/App/ModalWidget.d.ts +0 -6
- package/dist/ui/App/ModalWidget.js +0 -53
- package/dist/ui/App/ViewContainer.d.ts +0 -6
- package/dist/ui/App/ViewContainer.js +0 -55
- package/dist/ui/App/ViewContainerTitle.d.ts +0 -5
- package/dist/ui/App/ViewContainerTitle.js +0 -44
- package/dist/ui/App/ViewHeader.js +0 -60
- package/dist/ui/App/ViewLauncher.d.ts +0 -11
- package/dist/ui/App/ViewLauncher.js +0 -29
- package/dist/ui/App/ViewMenu.d.ts +0 -8
- package/dist/ui/App/ViewMenu.js +0 -74
- package/dist/ui/App/ViewWrapper.d.ts +0 -6
- package/dist/ui/App/ViewWrapper.js +0 -23
- package/dist/ui/App/ViewsContainer.d.ts +0 -12
- package/dist/ui/App/ViewsContainer.js +0 -57
- package/dist/ui/App/index.d.ts +0 -1
- package/dist/ui/App/index.js +0 -17
- package/dist/ui/index.d.ts +0 -1
- package/dist/ui/index.js +0 -17
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { nanoid } from '@jbrowse/core/util/nanoid';
|
|
4
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
5
|
+
import { useTheme } from '@mui/material';
|
|
6
|
+
import { DockviewReact } from 'dockview-react';
|
|
7
|
+
import { autorun } from 'mobx';
|
|
8
|
+
import { observer } from 'mobx-react';
|
|
9
|
+
import { DockviewContext, getPendingMoveAction } from "./DockviewContext.js";
|
|
10
|
+
import DockviewLeftHeaderActions from "./DockviewLeftHeaderActions.js";
|
|
11
|
+
import DockviewRightHeaderActions from "./DockviewRightHeaderActions.js";
|
|
12
|
+
import JBrowseViewPanel from "./JBrowseViewPanel.js";
|
|
13
|
+
import JBrowseViewTab from "./JBrowseViewTab.js";
|
|
14
|
+
import { cleanLayoutForStorage, createPanelConfig, updatePanelParams, } from "./dockviewUtils.js";
|
|
15
|
+
import { isSessionWithDockviewLayout } from "../../DockviewLayout/index.js";
|
|
16
|
+
import 'dockview-react/dist/styles/dockview.css';
|
|
17
|
+
const useStyles = makeStyles()(() => ({
|
|
18
|
+
container: {
|
|
19
|
+
height: '100%',
|
|
20
|
+
width: '100%',
|
|
21
|
+
gridRow: 'components',
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
const components = {
|
|
25
|
+
jbrowseView: JBrowseViewPanel,
|
|
26
|
+
};
|
|
27
|
+
const tabComponents = {
|
|
28
|
+
jbrowseTab: JBrowseViewTab,
|
|
29
|
+
};
|
|
30
|
+
const TiledViewsContainer = observer(function TiledViewsContainer({ session, }) {
|
|
31
|
+
const { classes } = useStyles();
|
|
32
|
+
const theme = useTheme();
|
|
33
|
+
const [api, setApi] = useState(null);
|
|
34
|
+
const trackedViewIdsRef = useRef(new Set());
|
|
35
|
+
const rearrangingRef = useRef(false);
|
|
36
|
+
const sessionRef = useRef(session);
|
|
37
|
+
sessionRef.current = session;
|
|
38
|
+
const rearrangePanels = useCallback((arrange) => {
|
|
39
|
+
if (!api) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
rearrangingRef.current = true;
|
|
43
|
+
try {
|
|
44
|
+
arrange(api);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
rearrangingRef.current = false;
|
|
48
|
+
}
|
|
49
|
+
}, [api]);
|
|
50
|
+
const addEmptyTab = useCallback((targetGroup) => {
|
|
51
|
+
if (!api) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const panelId = `panel-${nanoid()}`;
|
|
55
|
+
const group = targetGroup ?? api.activeGroup;
|
|
56
|
+
api.addPanel({
|
|
57
|
+
...createPanelConfig(panelId, session, 'New Tab'),
|
|
58
|
+
position: group ? { referenceGroup: group } : undefined,
|
|
59
|
+
});
|
|
60
|
+
if (isSessionWithDockviewLayout(session)) {
|
|
61
|
+
session.setActivePanelId(panelId);
|
|
62
|
+
}
|
|
63
|
+
}, [api, session]);
|
|
64
|
+
const moveViewToNewTab = useCallback((viewId) => {
|
|
65
|
+
if (!api || !isSessionWithDockviewLayout(session)) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
session.removeViewFromPanel(viewId);
|
|
69
|
+
const panelId = `panel-${nanoid()}`;
|
|
70
|
+
const group = api.activeGroup;
|
|
71
|
+
api.addPanel({
|
|
72
|
+
...createPanelConfig(panelId, session, 'New Tab'),
|
|
73
|
+
position: group ? { referenceGroup: group } : undefined,
|
|
74
|
+
});
|
|
75
|
+
session.assignViewToPanel(panelId, viewId);
|
|
76
|
+
session.setActivePanelId(panelId);
|
|
77
|
+
}, [api, session]);
|
|
78
|
+
const moveViewToSplitRight = useCallback((viewId) => {
|
|
79
|
+
if (!api || !isSessionWithDockviewLayout(session)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
session.removeViewFromPanel(viewId);
|
|
83
|
+
const panelId = `panel-${nanoid()}`;
|
|
84
|
+
const group = api.activeGroup;
|
|
85
|
+
api.addPanel({
|
|
86
|
+
...createPanelConfig(panelId, session, 'New Tab'),
|
|
87
|
+
position: group
|
|
88
|
+
? { referenceGroup: group, direction: 'right' }
|
|
89
|
+
: undefined,
|
|
90
|
+
});
|
|
91
|
+
session.assignViewToPanel(panelId, viewId);
|
|
92
|
+
session.setActivePanelId(panelId);
|
|
93
|
+
}, [api, session]);
|
|
94
|
+
const contextValue = useMemo(() => ({
|
|
95
|
+
api,
|
|
96
|
+
rearrangePanels,
|
|
97
|
+
addEmptyTab,
|
|
98
|
+
moveViewToNewTab,
|
|
99
|
+
moveViewToSplitRight,
|
|
100
|
+
}), [api, rearrangePanels, addEmptyTab, moveViewToNewTab, moveViewToSplitRight]);
|
|
101
|
+
const createInitialPanel = useCallback((dockviewApi) => {
|
|
102
|
+
const panelId = `panel-${nanoid()}`;
|
|
103
|
+
dockviewApi.addPanel(createPanelConfig(panelId, sessionRef.current));
|
|
104
|
+
if (isSessionWithDockviewLayout(sessionRef.current)) {
|
|
105
|
+
sessionRef.current.setActivePanelId(panelId);
|
|
106
|
+
}
|
|
107
|
+
}, []);
|
|
108
|
+
const onReady = useCallback((event) => {
|
|
109
|
+
setApi(event.api);
|
|
110
|
+
event.api.onDidActivePanelChange(e => {
|
|
111
|
+
if (e?.id && isSessionWithDockviewLayout(sessionRef.current)) {
|
|
112
|
+
sessionRef.current.setActivePanelId(e.id);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
event.api.onDidRemovePanel(e => {
|
|
116
|
+
if (rearrangingRef.current) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (isSessionWithDockviewLayout(sessionRef.current)) {
|
|
120
|
+
const viewIds = sessionRef.current.getViewIdsForPanel(e.id);
|
|
121
|
+
for (const viewId of viewIds) {
|
|
122
|
+
const view = sessionRef.current.views.find(v => v.id === viewId);
|
|
123
|
+
if (view) {
|
|
124
|
+
sessionRef.current.removeView(view);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
sessionRef.current.removePanel(e.id);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
event.api.onDidLayoutChange(() => {
|
|
131
|
+
if (!rearrangingRef.current &&
|
|
132
|
+
isSessionWithDockviewLayout(sessionRef.current)) {
|
|
133
|
+
sessionRef.current.setDockviewLayout(cleanLayoutForStorage(event.api.toJSON()));
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
if (isSessionWithDockviewLayout(sessionRef.current) &&
|
|
137
|
+
sessionRef.current.dockviewLayout) {
|
|
138
|
+
try {
|
|
139
|
+
rearrangingRef.current = true;
|
|
140
|
+
event.api.fromJSON(sessionRef.current.dockviewLayout);
|
|
141
|
+
updatePanelParams(event.api, sessionRef.current);
|
|
142
|
+
for (const viewIds of sessionRef.current.panelViewAssignments.values()) {
|
|
143
|
+
for (const viewId of viewIds) {
|
|
144
|
+
trackedViewIdsRef.current.add(viewId);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
rearrangingRef.current = false;
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
console.error('Failed to restore dockview layout:', e);
|
|
151
|
+
rearrangingRef.current = false;
|
|
152
|
+
createInitialPanel(event.api);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
createInitialPanel(event.api);
|
|
157
|
+
}
|
|
158
|
+
}, [createInitialPanel]);
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
const dispose = autorun(() => {
|
|
161
|
+
if (!api) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const { views } = session;
|
|
165
|
+
const currentViewIds = new Set(views.map(v => v.id));
|
|
166
|
+
const trackedIds = trackedViewIdsRef.current;
|
|
167
|
+
for (const view of views) {
|
|
168
|
+
if (!trackedIds.has(view.id)) {
|
|
169
|
+
trackedIds.add(view.id);
|
|
170
|
+
if (isSessionWithDockviewLayout(session)) {
|
|
171
|
+
let activePanelId = session.activePanelId;
|
|
172
|
+
if (!activePanelId || !api.getPanel(activePanelId)) {
|
|
173
|
+
const firstPanel = api.panels[0];
|
|
174
|
+
if (firstPanel) {
|
|
175
|
+
activePanelId = firstPanel.id;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
activePanelId = `panel-${nanoid()}`;
|
|
179
|
+
api.addPanel(createPanelConfig(activePanelId, session));
|
|
180
|
+
}
|
|
181
|
+
session.setActivePanelId(activePanelId);
|
|
182
|
+
}
|
|
183
|
+
session.assignViewToPanel(activePanelId, view.id);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
for (const id of trackedIds) {
|
|
188
|
+
if (!currentViewIds.has(id)) {
|
|
189
|
+
trackedIds.delete(id);
|
|
190
|
+
if (isSessionWithDockviewLayout(session)) {
|
|
191
|
+
session.removeViewFromPanel(id);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
return dispose;
|
|
197
|
+
}, [session, api]);
|
|
198
|
+
useEffect(() => {
|
|
199
|
+
if (!api) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const pendingAction = getPendingMoveAction();
|
|
203
|
+
if (pendingAction) {
|
|
204
|
+
const { type, viewId } = pendingAction;
|
|
205
|
+
if (type === 'newTab') {
|
|
206
|
+
moveViewToNewTab(viewId);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
moveViewToSplitRight(viewId);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}, [api, moveViewToNewTab, moveViewToSplitRight]);
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
if (!api || !isSessionWithDockviewLayout(session)) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const dispose = autorun(() => {
|
|
218
|
+
const { dockviewLayout } = session;
|
|
219
|
+
if (!dockviewLayout || rearrangingRef.current) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const currentLayout = cleanLayoutForStorage(api.toJSON());
|
|
223
|
+
if (JSON.stringify(currentLayout) === JSON.stringify(dockviewLayout)) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
rearrangingRef.current = true;
|
|
227
|
+
try {
|
|
228
|
+
api.fromJSON(dockviewLayout);
|
|
229
|
+
updatePanelParams(api, sessionRef.current);
|
|
230
|
+
trackedViewIdsRef.current.clear();
|
|
231
|
+
for (const viewIds of session.panelViewAssignments.values()) {
|
|
232
|
+
for (const viewId of viewIds) {
|
|
233
|
+
trackedViewIdsRef.current.add(viewId);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
catch (e) {
|
|
238
|
+
console.error('Failed to restore dockview layout from undo:', e);
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
rearrangingRef.current = false;
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
return dispose;
|
|
245
|
+
}, [session, api]);
|
|
246
|
+
const themeClass = theme.palette.mode === 'dark'
|
|
247
|
+
? 'dockview-theme-dark'
|
|
248
|
+
: 'dockview-theme-light';
|
|
249
|
+
return (_jsx(DockviewContext.Provider, { value: contextValue, children: _jsx("div", { className: `${classes.container} ${themeClass}`, children: _jsx(DockviewReact, { components: components, tabComponents: tabComponents, leftHeaderActionsComponent: DockviewLeftHeaderActions, rightHeaderActionsComponent: DockviewRightHeaderActions, onReady: onReady }) }) }));
|
|
250
|
+
});
|
|
251
|
+
export default TiledViewsContainer;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { IBaseViewModel } from '@jbrowse/core/pluggableElementTypes/models';
|
|
2
|
-
declare const
|
|
2
|
+
declare const ViewButtons: ({ view, onClose, onMinimize, }: {
|
|
3
3
|
view: IBaseViewModel;
|
|
4
4
|
onClose: () => void;
|
|
5
5
|
onMinimize: () => void;
|
|
6
|
-
className?: string;
|
|
7
6
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
-
export default
|
|
7
|
+
export default ViewButtons;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
3
|
+
import AddIcon from '@mui/icons-material/Add';
|
|
4
|
+
import CloseIcon from '@mui/icons-material/Close';
|
|
5
|
+
import MinimizeIcon from '@mui/icons-material/Minimize';
|
|
6
|
+
import { IconButton } from '@mui/material';
|
|
7
|
+
import { observer } from 'mobx-react';
|
|
8
|
+
const useStyles = makeStyles()(theme => ({
|
|
9
|
+
icon: {
|
|
10
|
+
color: theme.palette.secondary.contrastText,
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
const ViewButtons = observer(function ViewButtons({ view, onClose, onMinimize, }) {
|
|
14
|
+
const { classes } = useStyles();
|
|
15
|
+
return (_jsxs(_Fragment, { children: [_jsx(IconButton, { "data-testid": "minimize_view", onClick: onMinimize, children: view.minimized ? (_jsx(AddIcon, { className: classes.icon, fontSize: "small" })) : (_jsx(MinimizeIcon, { className: classes.icon, fontSize: "small" })) }), _jsx(IconButton, { "data-testid": "close_view", onClick: onClose, children: _jsx(CloseIcon, { className: classes.icon, fontSize: "small" }) })] }));
|
|
16
|
+
});
|
|
17
|
+
export default ViewButtons;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect } from 'react';
|
|
3
3
|
import { useWidthSetter } from '@jbrowse/core/util';
|
|
4
|
+
import { cx, makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
5
|
import { Paper, useTheme } from '@mui/material';
|
|
5
6
|
import { observer } from 'mobx-react';
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import ViewWrapper from './ViewWrapper';
|
|
7
|
+
import ViewHeader from "./ViewHeader.js";
|
|
8
|
+
import ViewWrapper from "./ViewWrapper.js";
|
|
9
9
|
const useStyles = makeStyles()(theme => ({
|
|
10
10
|
viewContainer: {
|
|
11
11
|
margin: theme.spacing(0.5),
|
|
@@ -19,14 +19,13 @@ const useStyles = makeStyles()(theme => ({
|
|
|
19
19
|
background: theme.palette.secondary.dark,
|
|
20
20
|
},
|
|
21
21
|
}));
|
|
22
|
-
const ViewContainer = observer(function ({ view, session, }) {
|
|
22
|
+
const ViewContainer = observer(function ViewContainer({ view, session, }) {
|
|
23
23
|
const theme = useTheme();
|
|
24
24
|
const ref = useWidthSetter(view, theme.spacing(1));
|
|
25
|
-
const { classes
|
|
25
|
+
const { classes } = useStyles();
|
|
26
26
|
useEffect(() => {
|
|
27
27
|
function handleSelectView(e) {
|
|
28
|
-
|
|
29
|
-
if (e.target instanceof Element && ((_a = ref.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
|
|
28
|
+
if (e.target instanceof Element && ref.current?.contains(e.target)) {
|
|
30
29
|
session.setFocusedViewId(view.id);
|
|
31
30
|
}
|
|
32
31
|
}
|
|
@@ -41,7 +40,7 @@ const ViewContainer = observer(function ({ view, session, }) {
|
|
|
41
40
|
? classes.focusedView
|
|
42
41
|
: classes.unfocusedView;
|
|
43
42
|
const viewContainerClassName = cx(classes.viewContainer, backgroundColorClassName);
|
|
44
|
-
return (_jsxs(Paper, { ref: ref, elevation: 12, className: viewContainerClassName, children: [_jsx(ViewHeader, { view: view, onClose: () => {
|
|
43
|
+
return (_jsxs(Paper, { ref: ref, elevation: 12, className: viewContainerClassName, "data-testid": `view-container-${view.id}`, children: [_jsx(ViewHeader, { view: view, onClose: () => {
|
|
45
44
|
session.removeView(view);
|
|
46
45
|
}, onMinimize: () => {
|
|
47
46
|
view.setMinimized(!view.minimized);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import EditableTypography from '@jbrowse/core/ui/EditableTypography';
|
|
3
3
|
import { getSession } from '@jbrowse/core/util';
|
|
4
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
5
|
import { Tooltip } from '@mui/material';
|
|
5
6
|
import { observer } from 'mobx-react';
|
|
6
|
-
import { makeStyles } from 'tss-react/mui';
|
|
7
7
|
const useStyles = makeStyles()(theme => ({
|
|
8
8
|
input: {
|
|
9
9
|
paddingBottom: 0,
|
|
@@ -22,12 +22,11 @@ const useStyles = makeStyles()(theme => ({
|
|
|
22
22
|
backgroundColor: theme.palette.secondary.light,
|
|
23
23
|
},
|
|
24
24
|
}));
|
|
25
|
-
const ViewContainerTitle = observer(function ({ view, }) {
|
|
26
|
-
var _a;
|
|
25
|
+
const ViewContainerTitle = observer(function ViewContainerTitle({ view, }) {
|
|
27
26
|
const { classes } = useStyles();
|
|
28
27
|
const { assemblyManager } = getSession(view);
|
|
29
28
|
return (_jsx(Tooltip, { title: "Rename view", arrow: true, children: _jsx(EditableTypography, { value: view.displayName ||
|
|
30
|
-
`${
|
|
29
|
+
`${view.assemblyNames?.map(r => assemblyManager.get(r)?.displayName).join(',') || 'Untitled view'}${view.minimized ? ' (minimized)' : ''}`, setValue: val => {
|
|
31
30
|
view.setDisplayName(val);
|
|
32
31
|
}, variant: "body2", classes: {
|
|
33
32
|
input: classes.input,
|
package/esm/ui/App/ViewHeader.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
3
|
import { VIEW_HEADER_HEIGHT } from '@jbrowse/core/ui';
|
|
4
4
|
import { getSession } from '@jbrowse/core/util';
|
|
5
|
+
import { cx, makeStyles } from '@jbrowse/core/util/tss-react';
|
|
5
6
|
import { isSessionWithMultipleViews } from '@jbrowse/product-core';
|
|
6
|
-
import AddIcon from '@mui/icons-material/Add';
|
|
7
|
-
import CloseIcon from '@mui/icons-material/Close';
|
|
8
7
|
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
|
|
9
|
-
import MinimizeIcon from '@mui/icons-material/Minimize';
|
|
10
|
-
import { IconButton } from '@mui/material';
|
|
11
8
|
import { observer } from 'mobx-react';
|
|
12
|
-
import
|
|
13
|
-
import ViewContainerTitle from
|
|
14
|
-
import ViewMenu from
|
|
9
|
+
import ViewButtons from "./ViewButtons.js";
|
|
10
|
+
import ViewContainerTitle from "./ViewContainerTitle.js";
|
|
11
|
+
import ViewMenu from "./ViewMenu.js";
|
|
15
12
|
const useStyles = makeStyles()(theme => ({
|
|
16
13
|
icon: {
|
|
17
14
|
color: theme.palette.secondary.contrastText,
|
|
@@ -24,19 +21,14 @@ const useStyles = makeStyles()(theme => ({
|
|
|
24
21
|
height: VIEW_HEADER_HEIGHT,
|
|
25
22
|
top: 0,
|
|
26
23
|
zIndex: 900,
|
|
27
|
-
background: theme.palette.secondary.main,
|
|
28
24
|
},
|
|
29
25
|
viewTitle: {
|
|
30
26
|
display: 'flex',
|
|
31
27
|
alignItems: 'center',
|
|
32
28
|
},
|
|
33
29
|
}));
|
|
34
|
-
const
|
|
30
|
+
const ViewHeader = observer(function ViewHeader({ view, onClose, onMinimize, className, }) {
|
|
35
31
|
const { classes } = useStyles();
|
|
36
|
-
return (_jsxs(_Fragment, { children: [_jsx(IconButton, { "data-testid": "minimize_view", onClick: onMinimize, children: view.minimized ? (_jsx(AddIcon, { className: classes.icon, fontSize: "small" })) : (_jsx(MinimizeIcon, { className: classes.icon, fontSize: "small" })) }), _jsx(IconButton, { "data-testid": "close_view", onClick: onClose, children: _jsx(CloseIcon, { className: classes.icon, fontSize: "small" }) })] }));
|
|
37
|
-
});
|
|
38
|
-
const ViewHeader = observer(function ({ view, onClose, onMinimize, className, }) {
|
|
39
|
-
const { classes, cx } = useStyles();
|
|
40
32
|
const scrollRef = useRef(null);
|
|
41
33
|
const session = getSession(view);
|
|
42
34
|
let stickyViewHeaders = false;
|
|
@@ -45,9 +37,8 @@ const ViewHeader = observer(function ({ view, onClose, onMinimize, className, })
|
|
|
45
37
|
({ stickyViewHeaders } = session);
|
|
46
38
|
}
|
|
47
39
|
useEffect(() => {
|
|
48
|
-
var _a;
|
|
49
40
|
if (typeof jest === 'undefined') {
|
|
50
|
-
|
|
41
|
+
scrollRef.current?.scrollIntoView({ block: 'center' });
|
|
51
42
|
}
|
|
52
43
|
}, []);
|
|
53
44
|
return (_jsxs("div", { ref: scrollRef, className: cx(classes.viewHeader, className), style: { position: stickyViewHeaders ? 'sticky' : undefined }, children: [_jsx(ViewMenu, { model: view, IconProps: { className: classes.icon } }), _jsx("div", { className: classes.grow }), _jsxs("div", { className: classes.viewTitle, children: [session.focusedViewId === view.id ? (_jsx(KeyboardArrowRightIcon, { className: classes.icon, fontSize: "small" })) : null, _jsx(ViewContainerTitle, { view: view })] }), _jsx("div", { className: classes.grow }), _jsx(ViewButtons, { onClose: onClose, onMinimize: onMinimize, view: view })] }));
|
|
@@ -5,7 +5,7 @@ type AppSession = SessionWithDrawerWidgets & {
|
|
|
5
5
|
renameCurrentSession: (arg: string) => void;
|
|
6
6
|
popSnackbarMessage: () => unknown;
|
|
7
7
|
};
|
|
8
|
-
declare const ViewLauncher: ({ session }: {
|
|
8
|
+
declare const ViewLauncher: ({ session, }: {
|
|
9
9
|
session: AppSession;
|
|
10
10
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
11
11
|
export default ViewLauncher;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { getEnv } from '@jbrowse/core/util';
|
|
4
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
5
|
import { Button, FormControl, MenuItem, Paper, Select, Typography, } from '@mui/material';
|
|
5
6
|
import { observer } from 'mobx-react';
|
|
6
|
-
import { makeStyles } from 'tss-react/mui';
|
|
7
7
|
const useStyles = makeStyles()(theme => ({
|
|
8
8
|
selectPaper: {
|
|
9
9
|
padding: theme.spacing(4),
|
|
@@ -12,12 +12,11 @@ const useStyles = makeStyles()(theme => ({
|
|
|
12
12
|
margin: 2,
|
|
13
13
|
},
|
|
14
14
|
}));
|
|
15
|
-
const ViewLauncher = observer(({ session })
|
|
16
|
-
var _a;
|
|
15
|
+
const ViewLauncher = observer(function ViewLauncher({ session, }) {
|
|
17
16
|
const { classes } = useStyles();
|
|
18
17
|
const { pluginManager } = getEnv(session);
|
|
19
18
|
const viewTypes = pluginManager.getViewElements();
|
|
20
|
-
const [value, setValue] = useState(
|
|
19
|
+
const [value, setValue] = useState(viewTypes[0]?.name || '');
|
|
21
20
|
return (_jsxs(Paper, { className: classes.selectPaper, children: [_jsx(Typography, { children: "Select a view to launch" }), _jsx(FormControl, { className: classes.m2, children: _jsx(Select, { value: value, onChange: event => {
|
|
22
21
|
setValue(event.target.value);
|
|
23
22
|
}, children: viewTypes
|
package/esm/ui/App/ViewMenu.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IBaseViewModel } from '@jbrowse/core/pluggableElementTypes/models';
|
|
2
2
|
import type { IconButtonProps as IconButtonPropsType, SvgIconProps } from '@mui/material';
|
|
3
|
-
declare const ViewMenu: ({ model,
|
|
3
|
+
declare const ViewMenu: ({ model, IconProps, }: {
|
|
4
4
|
model: IBaseViewModel;
|
|
5
5
|
IconButtonProps?: IconButtonPropsType;
|
|
6
6
|
IconProps: SvgIconProps;
|
package/esm/ui/App/ViewMenu.js
CHANGED
|
@@ -1,69 +1,128 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import
|
|
3
|
-
import { bindPopover, bindTrigger, usePopupState } from '@jbrowse/core/ui/hooks';
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { CascadingMenuButton } from '@jbrowse/core/ui';
|
|
4
3
|
import { getSession } from '@jbrowse/core/util';
|
|
4
|
+
import { getSnapshot } from '@jbrowse/mobx-state-tree';
|
|
5
|
+
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
|
5
6
|
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
|
6
7
|
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
|
7
8
|
import KeyboardDoubleArrowDownIcon from '@mui/icons-material/KeyboardDoubleArrowDown';
|
|
8
9
|
import KeyboardDoubleArrowUpIcon from '@mui/icons-material/KeyboardDoubleArrowUp';
|
|
9
10
|
import MenuIcon from '@mui/icons-material/Menu';
|
|
10
|
-
import
|
|
11
|
+
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
|
12
|
+
import VerticalSplitIcon from '@mui/icons-material/VerticalSplit';
|
|
11
13
|
import { observer } from 'mobx-react';
|
|
12
|
-
|
|
14
|
+
import { useDockview } from "./DockviewContext.js";
|
|
15
|
+
import { renameIds } from "./copyView.js";
|
|
16
|
+
import { isSessionWithDockviewLayout } from "../../DockviewLayout/index.js";
|
|
17
|
+
function getPanelViewCount(session, viewId) {
|
|
18
|
+
if (!isSessionWithDockviewLayout(session)) {
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
for (const viewIds of session.panelViewAssignments.values()) {
|
|
22
|
+
if (viewIds.includes(viewId)) {
|
|
23
|
+
return viewIds.length;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return 0;
|
|
27
|
+
}
|
|
28
|
+
const ViewMenu = observer(function ViewMenu({ model, IconProps, }) {
|
|
13
29
|
const session = getSession(model);
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
30
|
+
const { moveViewToNewTab, moveViewToSplitRight } = useDockview();
|
|
31
|
+
const usePanel = session.useWorkspaces && isSessionWithDockviewLayout(session);
|
|
32
|
+
const viewCount = usePanel
|
|
33
|
+
? getPanelViewCount(session, model.id)
|
|
34
|
+
: session.views.length;
|
|
35
|
+
return (_jsx(CascadingMenuButton, { "data-testid": "view_menu_icon", menuItems: () => [
|
|
36
|
+
{
|
|
37
|
+
label: 'View options',
|
|
38
|
+
type: 'subMenu',
|
|
39
|
+
subMenu: [
|
|
40
|
+
{
|
|
41
|
+
label: 'Copy view',
|
|
42
|
+
icon: ContentCopyIcon,
|
|
43
|
+
onClick: () => {
|
|
44
|
+
session.addView(model.type, renameIds(structuredClone(getSnapshot(model))));
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: 'Move to new tab',
|
|
49
|
+
icon: OpenInNewIcon,
|
|
50
|
+
onClick: () => {
|
|
51
|
+
moveViewToNewTab(model.id);
|
|
52
|
+
session.setUseWorkspaces(true);
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
label: 'Move to split view (right side of screen)',
|
|
57
|
+
icon: VerticalSplitIcon,
|
|
58
|
+
onClick: () => {
|
|
59
|
+
moveViewToSplitRight(model.id);
|
|
60
|
+
session.setUseWorkspaces(true);
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
...(viewCount > 2
|
|
21
64
|
? [
|
|
22
65
|
{
|
|
23
|
-
label: '
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
]
|
|
36
|
-
: []),
|
|
37
|
-
{
|
|
38
|
-
label: 'Move view up',
|
|
39
|
-
icon: KeyboardArrowUpIcon,
|
|
40
|
-
onClick: () => {
|
|
41
|
-
session.moveViewUp(model.id);
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
label: 'Move view down',
|
|
46
|
-
icon: KeyboardArrowDownIcon,
|
|
47
|
-
onClick: () => {
|
|
48
|
-
session.moveViewDown(model.id);
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
...(session.views.length > 2
|
|
52
|
-
? [
|
|
53
|
-
{
|
|
54
|
-
label: 'Move view to bottom',
|
|
55
|
-
icon: KeyboardDoubleArrowDownIcon,
|
|
56
|
-
onClick: () => {
|
|
57
|
-
session.moveViewToBottom(model.id);
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
]
|
|
61
|
-
: []),
|
|
62
|
-
],
|
|
66
|
+
label: 'Move view to top',
|
|
67
|
+
icon: KeyboardDoubleArrowUpIcon,
|
|
68
|
+
onClick: () => {
|
|
69
|
+
if (usePanel) {
|
|
70
|
+
session.moveViewToTopInPanel(model.id);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
session.moveViewToTop(model.id);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
63
76
|
},
|
|
64
77
|
]
|
|
65
78
|
: []),
|
|
66
|
-
...
|
|
67
|
-
|
|
79
|
+
...(viewCount > 1
|
|
80
|
+
? [
|
|
81
|
+
{
|
|
82
|
+
label: 'Move view up',
|
|
83
|
+
icon: KeyboardArrowUpIcon,
|
|
84
|
+
onClick: () => {
|
|
85
|
+
if (usePanel) {
|
|
86
|
+
session.moveViewUpInPanel(model.id);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
session.moveViewUp(model.id);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
label: 'Move view down',
|
|
95
|
+
icon: KeyboardArrowDownIcon,
|
|
96
|
+
onClick: () => {
|
|
97
|
+
if (usePanel) {
|
|
98
|
+
session.moveViewDownInPanel(model.id);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
session.moveViewDown(model.id);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
]
|
|
106
|
+
: []),
|
|
107
|
+
...(viewCount > 2
|
|
108
|
+
? [
|
|
109
|
+
{
|
|
110
|
+
label: 'Move view to bottom',
|
|
111
|
+
icon: KeyboardDoubleArrowDownIcon,
|
|
112
|
+
onClick: () => {
|
|
113
|
+
if (usePanel) {
|
|
114
|
+
session.moveViewToBottomInPanel(model.id);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
session.moveViewToBottom(model.id);
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
]
|
|
122
|
+
: []),
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
...model.menuItems(),
|
|
126
|
+
], children: _jsx(MenuIcon, { ...IconProps, fontSize: "small" }) }));
|
|
68
127
|
});
|
|
69
128
|
export default ViewMenu;
|
|
@@ -3,7 +3,7 @@ import { Suspense } from 'react';
|
|
|
3
3
|
import LoadingEllipses from '@jbrowse/core/ui/LoadingEllipses';
|
|
4
4
|
import { getEnv } from '@jbrowse/core/util';
|
|
5
5
|
import { observer } from 'mobx-react';
|
|
6
|
-
const ViewWrapper = observer(function ({ view, session, }) {
|
|
6
|
+
const ViewWrapper = observer(function ViewWrapper({ view, session, }) {
|
|
7
7
|
const { pluginManager } = getEnv(session);
|
|
8
8
|
const viewType = pluginManager.getViewType(view.type);
|
|
9
9
|
if (!viewType) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { SnackbarMessage } from '@jbrowse/core/ui/SnackbarModel';
|
|
2
|
-
import type { SessionWithFocusedViewAndDrawerWidgets } from '@jbrowse/core/util';
|
|
2
|
+
import type { AbstractViewContainer, SessionWithFocusedViewAndDrawerWidgets } from '@jbrowse/core/util';
|
|
3
3
|
interface Props {
|
|
4
4
|
HeaderButtons?: React.ReactElement;
|
|
5
|
-
session: SessionWithFocusedViewAndDrawerWidgets & {
|
|
5
|
+
session: SessionWithFocusedViewAndDrawerWidgets & AbstractViewContainer & {
|
|
6
6
|
renameCurrentSession: (arg: string) => void;
|
|
7
7
|
snackbarMessages: SnackbarMessage[];
|
|
8
8
|
popSnackbarMessage: () => unknown;
|