@jbrowse/app-core 4.3.0 → 5.0.0-beta.2
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/AppSession/AppRootModel.d.ts +30 -0
- package/esm/AppSession/AppRootModel.js +1 -0
- package/esm/AppSession/AppSessionMixin.d.ts +1598 -0
- package/esm/AppSession/AppSessionMixin.js +62 -0
- package/esm/AppSession/index.d.ts +2 -0
- package/esm/AppSession/index.js +1 -0
- package/esm/Assemblies/AssembliesMixin.d.ts +225 -0
- package/esm/Assemblies/AssembliesMixin.js +149 -0
- package/esm/Assemblies/index.d.ts +1 -2
- package/esm/Assemblies/index.js +1 -2
- package/esm/HistoryManagement/index.d.ts +19 -0
- package/esm/HistoryManagement/index.js +42 -5
- package/esm/JBrowseConfig/RootConfiguration.d.ts +171 -78
- package/esm/JBrowseConfig/RootConfiguration.js +85 -7
- package/esm/JBrowseConfig/index.d.ts +173 -84
- package/esm/JBrowseConfig/index.js +62 -2
- package/esm/JBrowseModel/index.d.ts +180 -95
- package/esm/JBrowseModel/index.js +122 -21
- package/esm/JbApi/index.d.ts +2 -0
- package/esm/JbApi/index.js +1 -0
- package/esm/JbApi/jbApi.d.ts +325 -0
- package/esm/JbApi/jbApi.js +748 -0
- package/esm/RootMenu/index.d.ts +69 -3
- package/esm/RootMenu/index.js +96 -61
- package/esm/SessionSpec/index.d.ts +6 -0
- package/esm/SessionSpec/index.js +3 -0
- package/esm/SessionSpec/lgvUrlInit.d.ts +79 -0
- package/esm/SessionSpec/lgvUrlInit.js +158 -0
- package/esm/SessionSpec/loadSessionSpec.d.ts +21 -0
- package/esm/SessionSpec/loadSessionSpec.js +392 -0
- package/esm/SessionSpec/parseSessionSpecUrl.d.ts +13 -0
- package/esm/SessionSpec/parseSessionSpecUrl.js +196 -0
- package/esm/SessionSpec/types.d.ts +31 -0
- package/esm/SessionSpec/types.js +1 -0
- package/esm/WorkspaceLayout/LayoutRenderer.d.ts +24 -0
- package/esm/WorkspaceLayout/LayoutRenderer.js +185 -0
- package/esm/WorkspaceLayout/PanelView.d.ts +13 -0
- package/esm/WorkspaceLayout/PanelView.js +99 -0
- package/esm/WorkspaceLayout/TabStrip.d.ts +14 -0
- package/esm/WorkspaceLayout/TabStrip.js +226 -0
- package/esm/WorkspaceLayout/WorkspaceContainer.d.ts +16 -0
- package/esm/WorkspaceLayout/WorkspaceContainer.js +105 -0
- package/esm/WorkspaceLayout/WorkspacePanelActions.d.ts +24 -0
- package/esm/WorkspaceLayout/WorkspacePanelActions.js +120 -0
- package/esm/WorkspaceLayout/WorkspaceTab.d.ts +27 -0
- package/esm/WorkspaceLayout/WorkspaceTab.js +109 -0
- package/esm/WorkspaceLayout/dockviewTheme.d.ts +69 -0
- package/esm/WorkspaceLayout/dockviewTheme.js +82 -0
- package/esm/WorkspaceLayout/dropZone.d.ts +105 -0
- package/esm/WorkspaceLayout/dropZone.js +113 -0
- package/esm/WorkspaceLayout/index.d.ts +8 -0
- package/esm/WorkspaceLayout/index.js +4 -0
- package/esm/WorkspaceLayout/model.d.ts +248 -0
- package/esm/WorkspaceLayout/model.js +562 -0
- package/esm/WorkspaceLayout/panelChrome.d.ts +19 -0
- package/esm/WorkspaceLayout/panelChrome.js +4 -0
- package/esm/WorkspaceLayout/spec.d.ts +88 -0
- package/esm/WorkspaceLayout/spec.js +226 -0
- package/esm/WorkspaceLayout/splitter.d.ts +25 -0
- package/esm/WorkspaceLayout/splitter.js +37 -0
- package/esm/WorkspaceLayout/tree.d.ts +158 -0
- package/esm/WorkspaceLayout/tree.js +379 -0
- package/esm/WorkspaceLayout/useLayoutDrag.d.ts +34 -0
- package/esm/WorkspaceLayout/useLayoutDrag.js +232 -0
- package/esm/index.d.ts +18 -9
- package/esm/index.js +16 -9
- package/esm/menus.d.ts +55 -68
- package/esm/menus.js +117 -86
- package/esm/queryParams.d.ts +9 -0
- package/esm/queryParams.js +92 -0
- package/esm/sessionModelFactory.d.ts +18 -0
- package/esm/sessionModelFactory.js +1 -0
- package/esm/ui/App/App.d.ts +3 -14
- package/esm/ui/App/App.js +30 -17
- package/esm/ui/App/AppFab.d.ts +12 -3
- package/esm/ui/App/AppFab.js +15 -8
- package/esm/ui/App/AppReadyMarker.d.ts +43 -0
- package/esm/ui/App/AppReadyMarker.js +56 -0
- package/esm/ui/App/AppToolbar.d.ts +2 -14
- package/esm/ui/App/AppToolbar.js +23 -24
- package/esm/ui/App/ClassicViewsContainer.d.ts +3 -9
- package/esm/ui/App/ClassicViewsContainer.js +9 -8
- package/esm/ui/App/DialogQueue.d.ts +17 -1
- package/esm/ui/App/DialogQueue.js +30 -1
- package/esm/ui/App/Drawer.d.ts +1 -1
- package/esm/ui/App/Drawer.js +10 -39
- package/esm/ui/App/DrawerControls.d.ts +1 -5
- package/esm/ui/App/DrawerControls.js +1 -25
- package/esm/ui/App/DrawerHeader.d.ts +2 -3
- package/esm/ui/App/DrawerHeader.js +24 -10
- package/esm/ui/App/DrawerHeaderHelpButton.d.ts +1 -1
- package/esm/ui/App/DrawerHeaderHelpButton.js +3 -3
- package/esm/ui/App/DrawerHeaderHelpDialog.d.ts +2 -2
- package/esm/ui/App/DrawerHeaderHelpDialog.js +3 -4
- package/esm/ui/App/DrawerWidget.d.ts +1 -1
- package/esm/ui/App/DrawerWidget.js +9 -21
- package/esm/ui/App/JBrowseTabMenu.d.ts +2 -3
- package/esm/ui/App/JBrowseTabMenu.js +24 -5
- package/esm/ui/App/ViewButtons.d.ts +2 -4
- package/esm/ui/App/ViewButtons.js +15 -3
- package/esm/ui/App/ViewContainer.d.ts +3 -2
- package/esm/ui/App/ViewContainer.js +144 -24
- package/esm/ui/App/ViewContainerTitle.d.ts +1 -1
- package/esm/ui/App/ViewContainerTitle.js +9 -2
- package/esm/ui/App/ViewErrorFallback.d.ts +18 -0
- package/esm/ui/App/ViewErrorFallback.js +42 -0
- package/esm/ui/App/ViewHeader.d.ts +3 -4
- package/esm/ui/App/ViewHeader.js +28 -17
- package/esm/ui/App/ViewLauncher.d.ts +2 -8
- package/esm/ui/App/ViewLauncher.js +7 -5
- package/esm/ui/App/ViewMenu.d.ts +3 -5
- package/esm/ui/App/ViewMenu.js +87 -92
- package/esm/ui/App/ViewStack.d.ts +7 -0
- package/esm/ui/App/ViewStack.js +42 -0
- package/esm/ui/App/ViewWrapper.d.ts +15 -1
- package/esm/ui/App/ViewWrapper.js +26 -10
- package/esm/ui/App/ViewsContainer.d.ts +3 -8
- package/esm/ui/App/ViewsContainer.js +9 -6
- package/esm/ui/App/copyView.js +1 -1
- package/esm/ui/App/index.d.ts +2 -1
- package/esm/ui/App/index.js +3 -2
- package/esm/ui/App/lazyChunk.d.ts +13 -0
- package/esm/ui/App/lazyChunk.js +91 -0
- package/esm/ui/App/types.d.ts +12 -3
- package/esm/ui/App/types.js +1 -1
- package/esm/ui/App/useViewVisibility.d.ts +34 -0
- package/esm/ui/App/useViewVisibility.js +87 -0
- package/esm/ui/App/viewTitle.d.ts +5 -0
- package/esm/ui/App/viewTitle.js +9 -0
- package/esm/ui/index.d.ts +1 -1
- package/esm/ui/index.js +1 -1
- package/package.json +12 -17
- package/esm/AppFocus/index.d.ts +0 -5
- package/esm/AppFocus/index.js +0 -12
- package/esm/Assemblies/SessionAssembliesMixin.d.ts +0 -92
- package/esm/Assemblies/SessionAssembliesMixin.js +0 -53
- package/esm/Assemblies/TemporaryAssembliesMixin.d.ts +0 -10
- package/esm/Assemblies/TemporaryAssembliesMixin.js +0 -37
- package/esm/DockviewLayout/index.d.ts +0 -35
- package/esm/DockviewLayout/index.js +0 -111
- package/esm/ui/App/DockviewContext.d.ts +0 -22
- package/esm/ui/App/DockviewContext.js +0 -29
- package/esm/ui/App/DockviewLeftHeaderActions.d.ts +0 -2
- package/esm/ui/App/DockviewLeftHeaderActions.js +0 -58
- package/esm/ui/App/DockviewRightHeaderActions.d.ts +0 -2
- package/esm/ui/App/DockviewRightHeaderActions.js +0 -100
- package/esm/ui/App/DrawerWidgetSelector.d.ts +0 -5
- package/esm/ui/App/DrawerWidgetSelector.js +0 -47
- package/esm/ui/App/JBrowseViewPanel.d.ts +0 -8
- package/esm/ui/App/JBrowseViewPanel.js +0 -40
- package/esm/ui/App/JBrowseViewTab.d.ts +0 -8
- package/esm/ui/App/JBrowseViewTab.js +0 -97
- package/esm/ui/App/ModalWidget.d.ts +0 -6
- package/esm/ui/App/ModalWidget.js +0 -48
- package/esm/ui/App/TiledViewsContainer.d.ts +0 -6
- package/esm/ui/App/TiledViewsContainer.js +0 -366
- package/esm/ui/App/dockviewUtils.d.ts +0 -46
- package/esm/ui/App/dockviewUtils.js +0 -55
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { WorkspaceSessionType } from '../ui/App/types.ts';
|
|
2
|
+
import type { WorkspaceLayout } from './model.ts';
|
|
3
|
+
import type { PanelNode } from './tree.ts';
|
|
4
|
+
/**
|
|
5
|
+
* The per-cell buttons: new tab, split, close.
|
|
6
|
+
*
|
|
7
|
+
* Every one of these is a single MST action on the layout, where the dockview
|
|
8
|
+
* versions were an api call plus a session write that had to be kept consistent
|
|
9
|
+
* with it — `handleSplit` was `containerApi.addGroup(...)` followed by
|
|
10
|
+
* `addEmptyTab(newGroup)`, and the gap between those two was where the
|
|
11
|
+
* "resource is already disposed" crash lived.
|
|
12
|
+
*/
|
|
13
|
+
export declare const WorkspacePanelActions: ({ panel, session, onClose, }: {
|
|
14
|
+
panel: PanelNode;
|
|
15
|
+
session: WorkspaceSessionType & WorkspaceLayout;
|
|
16
|
+
/**
|
|
17
|
+
* Closing a cell closes the views its tabs held, and the layout does not own
|
|
18
|
+
* views — so the pair is one function at the call site, the same way
|
|
19
|
+
* `WorkspaceTab` takes its `onClose`. Spelled here as well, this file would
|
|
20
|
+
* be the third place stating "and also remove the views", which is one more
|
|
21
|
+
* than the number that can be kept in step.
|
|
22
|
+
*/
|
|
23
|
+
onClose: () => void;
|
|
24
|
+
}) => import("react").JSX.Element;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
|
|
3
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
|
+
import AddIcon from '@mui/icons-material/Add';
|
|
5
|
+
import CloseIcon from '@mui/icons-material/Close';
|
|
6
|
+
import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen';
|
|
7
|
+
import DynamicFeedIcon from '@mui/icons-material/DynamicFeed';
|
|
8
|
+
import HorizontalSplitIcon from '@mui/icons-material/HorizontalSplit';
|
|
9
|
+
import OpenInFullIcon from '@mui/icons-material/OpenInFull';
|
|
10
|
+
import TabIcon from '@mui/icons-material/Tab';
|
|
11
|
+
import TableRowsIcon from '@mui/icons-material/TableRows';
|
|
12
|
+
import VerticalSplitIcon from '@mui/icons-material/VerticalSplit';
|
|
13
|
+
import ViewColumnIcon from '@mui/icons-material/ViewColumn';
|
|
14
|
+
import ViewModuleIcon from '@mui/icons-material/ViewModule';
|
|
15
|
+
import { IconButton, Tooltip } from '@mui/material';
|
|
16
|
+
import { observer } from 'mobx-react';
|
|
17
|
+
import { dv } from './dockviewTheme.js';
|
|
18
|
+
const useStyles = makeStyles()({
|
|
19
|
+
actions: { display: 'flex', alignItems: 'center', flex: '0 0 auto' },
|
|
20
|
+
// dockview's `.dv-default-tab-action`: inherits the strip's colour, and takes
|
|
21
|
+
// a small rounded chip on hover rather than MUI's circular ripple surface
|
|
22
|
+
button: {
|
|
23
|
+
padding: 4,
|
|
24
|
+
borderRadius: 2,
|
|
25
|
+
color: dv.activeGroupHiddenTabColor,
|
|
26
|
+
'&:hover': { background: dv.iconHoverBackground, borderRadius: 2 },
|
|
27
|
+
},
|
|
28
|
+
icon: { fontSize: 16 },
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* The per-cell buttons: new tab, split, close.
|
|
32
|
+
*
|
|
33
|
+
* Every one of these is a single MST action on the layout, where the dockview
|
|
34
|
+
* versions were an api call plus a session write that had to be kept consistent
|
|
35
|
+
* with it — `handleSplit` was `containerApi.addGroup(...)` followed by
|
|
36
|
+
* `addEmptyTab(newGroup)`, and the gap between those two was where the
|
|
37
|
+
* "resource is already disposed" crash lived.
|
|
38
|
+
*/
|
|
39
|
+
export const WorkspacePanelActions = observer(function WorkspacePanelActions({ panel, session, onClose, }) {
|
|
40
|
+
const { classes } = useStyles();
|
|
41
|
+
const canClose = session.panels.length > 1;
|
|
42
|
+
const maximized = session.maximizedPanelId === panel.id;
|
|
43
|
+
const tile = (mode) => {
|
|
44
|
+
session.tileViews(mode, session.views.map(v => v.id));
|
|
45
|
+
};
|
|
46
|
+
return (_jsxs("div", { className: classes.actions, children: [_jsx(CascadingMenuButton, { menuItems: [
|
|
47
|
+
{
|
|
48
|
+
label: 'New empty tab',
|
|
49
|
+
icon: TabIcon,
|
|
50
|
+
onClick: () => {
|
|
51
|
+
session.addTab(panel.id);
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: 'New empty split horizontal',
|
|
56
|
+
icon: VerticalSplitIcon,
|
|
57
|
+
onClick: () => {
|
|
58
|
+
session.splitPanel(panel.id, 'row');
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
label: 'New empty split vertical',
|
|
63
|
+
icon: HorizontalSplitIcon,
|
|
64
|
+
onClick: () => {
|
|
65
|
+
session.splitPanel(panel.id, 'column');
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
// The strip's double-click is the gesture; this is how anyone finds
|
|
69
|
+
// it, and the only way to reach it from the keyboard. Gated on there
|
|
70
|
+
// being another cell to hide, since maximizing the only one is a
|
|
71
|
+
// no-op with a label promising otherwise.
|
|
72
|
+
...(session.panels.length > 1 || maximized
|
|
73
|
+
? [
|
|
74
|
+
{
|
|
75
|
+
label: maximized ? 'Restore panel' : 'Maximize panel',
|
|
76
|
+
icon: maximized ? CloseFullscreenIcon : OpenInFullIcon,
|
|
77
|
+
onClick: () => {
|
|
78
|
+
session.toggleMaximizedPanel(panel.id);
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
]
|
|
82
|
+
: []),
|
|
83
|
+
// The whole-workspace commands, kept behind the same "Global:" prefix
|
|
84
|
+
// and the same >1-view gate they had on the dockview header, so a
|
|
85
|
+
// returning user finds them where they were and the label still says
|
|
86
|
+
// that the button in THIS cell rearranges every cell.
|
|
87
|
+
...(session.views.length > 1
|
|
88
|
+
? [
|
|
89
|
+
{
|
|
90
|
+
label: 'Global: change layout into set of tabs',
|
|
91
|
+
icon: DynamicFeedIcon,
|
|
92
|
+
onClick: () => {
|
|
93
|
+
tile('tabs');
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
label: 'Global: tile horizontally',
|
|
98
|
+
icon: ViewColumnIcon,
|
|
99
|
+
onClick: () => {
|
|
100
|
+
tile('horizontal');
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
label: 'Global: tile vertically',
|
|
105
|
+
icon: TableRowsIcon,
|
|
106
|
+
onClick: () => {
|
|
107
|
+
tile('vertical');
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
label: 'Global: tile grid',
|
|
112
|
+
icon: ViewModuleIcon,
|
|
113
|
+
onClick: () => {
|
|
114
|
+
tile('grid');
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
]
|
|
118
|
+
: []),
|
|
119
|
+
], size: "small", className: classes.button, children: _jsx(AddIcon, { className: classes.icon }) }), canClose ? (_jsx(Tooltip, { title: "Close panel", children: _jsx(IconButton, { size: "small", className: classes.button, onClick: onClose, children: _jsx(CloseIcon, { className: classes.icon }) }) })) : null] }));
|
|
120
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { WorkspaceSessionType } from '../ui/App/types.ts';
|
|
2
|
+
import type { WorkspaceLayout } from './model.ts';
|
|
3
|
+
import type { TabNode } from './tree.ts';
|
|
4
|
+
import type { AbstractViewModel } from '@jbrowse/core/util';
|
|
5
|
+
/**
|
|
6
|
+
* A tab's name is **derived** from the views it holds, unless the user set one.
|
|
7
|
+
*
|
|
8
|
+
* `title === undefined` is the sentinel for "not renamed", which is a plain
|
|
9
|
+
* `maybe` on the tab rather than the comparison the dockview version needed:
|
|
10
|
+
* dockview restores an unset title as the panel id, so telling a real title
|
|
11
|
+
* from a restored one meant testing `title !== panelId` and hoping nobody named
|
|
12
|
+
* a tab after a panel.
|
|
13
|
+
*/
|
|
14
|
+
export declare function tabDisplayName(tab: TabNode, views: AbstractViewModel[], session: WorkspaceSessionType): string;
|
|
15
|
+
export declare const WorkspaceTab: ({ tab, views, session, layout, onClose, }: {
|
|
16
|
+
tab: TabNode;
|
|
17
|
+
views: AbstractViewModel[];
|
|
18
|
+
session: WorkspaceSessionType;
|
|
19
|
+
layout: WorkspaceLayout;
|
|
20
|
+
/**
|
|
21
|
+
* Closing a tab closes the views it held, and the layout does not own views —
|
|
22
|
+
* so the pair is one function at the call site rather than spelled out here.
|
|
23
|
+
* The strip's middle-click needs the same pair, and two spellings of "and
|
|
24
|
+
* also remove the views" is how one of them comes to be missing it.
|
|
25
|
+
*/
|
|
26
|
+
onClose: () => void;
|
|
27
|
+
}) => import("react").JSX.Element;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
|
+
import { InputBase, Typography } from '@mui/material';
|
|
5
|
+
import { observer } from 'mobx-react';
|
|
6
|
+
import JBrowseTabMenu from '../ui/App/JBrowseTabMenu.js';
|
|
7
|
+
const useStyles = makeStyles()(theme => ({
|
|
8
|
+
// Only the label and its menu. The background, the selected state and the
|
|
9
|
+
// hover live on the strip's own `[role=tab]` wrapper, which is where the
|
|
10
|
+
// selection is known — two places styling one tab is how they drift.
|
|
11
|
+
tab: {
|
|
12
|
+
display: 'flex',
|
|
13
|
+
alignItems: 'center',
|
|
14
|
+
gap: 4,
|
|
15
|
+
minWidth: 0,
|
|
16
|
+
},
|
|
17
|
+
// dockview's `.dv-default-tab-content`: takes the space, truncates with an
|
|
18
|
+
// ellipsis, and takes its colour from the tab, which is what encodes
|
|
19
|
+
// active/inactive
|
|
20
|
+
title: {
|
|
21
|
+
flexGrow: 1,
|
|
22
|
+
marginRight: 4,
|
|
23
|
+
overflow: 'hidden',
|
|
24
|
+
textOverflow: 'ellipsis',
|
|
25
|
+
whiteSpace: 'nowrap',
|
|
26
|
+
fontSize: 'inherit',
|
|
27
|
+
color: 'inherit',
|
|
28
|
+
},
|
|
29
|
+
editInput: {
|
|
30
|
+
fontSize: 'inherit',
|
|
31
|
+
padding: '2px 4px',
|
|
32
|
+
color: theme.palette.text.primary,
|
|
33
|
+
backgroundColor: theme.palette.background.paper,
|
|
34
|
+
borderRadius: theme.shape.borderRadius,
|
|
35
|
+
flex: 1,
|
|
36
|
+
},
|
|
37
|
+
}));
|
|
38
|
+
/**
|
|
39
|
+
* A tab's name is **derived** from the views it holds, unless the user set one.
|
|
40
|
+
*
|
|
41
|
+
* `title === undefined` is the sentinel for "not renamed", which is a plain
|
|
42
|
+
* `maybe` on the tab rather than the comparison the dockview version needed:
|
|
43
|
+
* dockview restores an unset title as the panel id, so telling a real title
|
|
44
|
+
* from a restored one meant testing `title !== panelId` and hoping nobody named
|
|
45
|
+
* a tab after a panel.
|
|
46
|
+
*/
|
|
47
|
+
export function tabDisplayName(tab, views, session) {
|
|
48
|
+
if (tab.title) {
|
|
49
|
+
return tab.title;
|
|
50
|
+
}
|
|
51
|
+
if (views.length === 0) {
|
|
52
|
+
return 'Empty';
|
|
53
|
+
}
|
|
54
|
+
if (views.length === 1) {
|
|
55
|
+
const view = views[0];
|
|
56
|
+
return (view.displayName ||
|
|
57
|
+
view.assemblyNames
|
|
58
|
+
?.map(r => session.assemblyManager.getDisplayName(r))
|
|
59
|
+
.join(',') ||
|
|
60
|
+
'View');
|
|
61
|
+
}
|
|
62
|
+
return `${views.length} views`;
|
|
63
|
+
}
|
|
64
|
+
export const WorkspaceTab = observer(function WorkspaceTab({ tab, views, session, layout, onClose, }) {
|
|
65
|
+
const { classes } = useStyles();
|
|
66
|
+
const [editing, setEditing] = useState(false);
|
|
67
|
+
const [draft, setDraft] = useState('');
|
|
68
|
+
const title = tabDisplayName(tab, views, session);
|
|
69
|
+
const save = () => {
|
|
70
|
+
// An empty box means "go back to the automatic name". `title === undefined`
|
|
71
|
+
// is the sentinel for "not renamed" and `renameTab` takes it, but nothing
|
|
72
|
+
// in the UI passed it: clearing the box just discarded the edit, so a
|
|
73
|
+
// rename could be made and never unmade.
|
|
74
|
+
layout.renameTab(tab.id, draft.trim() || undefined);
|
|
75
|
+
setEditing(false);
|
|
76
|
+
};
|
|
77
|
+
return (_jsx("div", { className: classes.tab, children: editing ? (_jsx(InputBase, { autoFocus: true, className: classes.editInput, value: draft, onChange: e => {
|
|
78
|
+
setDraft(e.target.value);
|
|
79
|
+
}, onFocus: e => {
|
|
80
|
+
e.target.select();
|
|
81
|
+
}, onBlur: save, onKeyDown: e => {
|
|
82
|
+
// The box sits INSIDE the `role="tab"` that owns the strip's roving
|
|
83
|
+
// tabindex, and every key it handles — the arrows, Home, End, Enter
|
|
84
|
+
// and Space — it also preventDefault()s. All of them reach that
|
|
85
|
+
// handler by bubbling out of here, so while the box is open the
|
|
86
|
+
// arrows jumped to the next tab instead of moving the caret and a
|
|
87
|
+
// space never reached the input at all: a tab could not be given a
|
|
88
|
+
// name with a space in it.
|
|
89
|
+
e.stopPropagation();
|
|
90
|
+
if (e.key === 'Enter') {
|
|
91
|
+
save();
|
|
92
|
+
}
|
|
93
|
+
else if (e.key === 'Escape') {
|
|
94
|
+
setEditing(false);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
// the tab strip starts a drag on pointerdown; renaming must not
|
|
98
|
+
onPointerDown: e => {
|
|
99
|
+
e.stopPropagation();
|
|
100
|
+
}, onClick: e => {
|
|
101
|
+
e.stopPropagation();
|
|
102
|
+
} })) : (_jsxs(_Fragment, { children: [_jsx(Typography, { className: classes.title, variant: "body2", onDoubleClick: () => {
|
|
103
|
+
setDraft(title);
|
|
104
|
+
setEditing(true);
|
|
105
|
+
}, children: title }), _jsx(JBrowseTabMenu, { onRename: () => {
|
|
106
|
+
setDraft(title);
|
|
107
|
+
setEditing(true);
|
|
108
|
+
}, onClose: onClose })] })) }));
|
|
109
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dockview's `dockview-theme-dark`, transcribed.
|
|
3
|
+
*
|
|
4
|
+
* We render the workspace ourselves now, but the look is deliberately not a new
|
|
5
|
+
* design — it is the theme we already shipped, so nothing about the chrome
|
|
6
|
+
* changed for users when the engine underneath it did. Every value below is
|
|
7
|
+
* copied from `dockview/dist/styles/dockview.css`'s `.dockview-theme-dark`
|
|
8
|
+
* block (v8.0.0); the names are its CSS custom properties with the `--dv-`
|
|
9
|
+
* prefix dropped.
|
|
10
|
+
*
|
|
11
|
+
* **Fixed, not theme-derived.** These apply in a light JBrowse theme too, the
|
|
12
|
+
* same way the app bar is dark in both — the chrome frames the content rather
|
|
13
|
+
* than matching it. Don't wire them to `theme.palette`: a light strip reads as
|
|
14
|
+
* content, which is exactly what it looked like before this file existed.
|
|
15
|
+
*
|
|
16
|
+
* **This is the chrome only, and the panel BODY is not chrome.** dockview also
|
|
17
|
+
* has a `--dv-group-view-background-color` (`#1e1e1e`) for the surface a tab's
|
|
18
|
+
* content sits on, and transcribing it is what made a light JBrowse theme come
|
|
19
|
+
* up dark below the views — the frame swallowing the thing it frames. The body
|
|
20
|
+
* follows `theme.palette.background.default` in `PanelView` instead, so the
|
|
21
|
+
* cell matches every other surface the app draws content on. It is deliberately
|
|
22
|
+
* absent here rather than present and unused, since an unused colour named
|
|
23
|
+
* after the panel reads as a value someone forgot to wire up.
|
|
24
|
+
*
|
|
25
|
+
* A consequence worth knowing before "fixing" it: dockview's selected tab is
|
|
26
|
+
* `#1e1e1e` **because** the body was, so the tab merged into its content. Ours
|
|
27
|
+
* cannot merge into a light body, and the four values below are kept anyway —
|
|
28
|
+
* darker-than-the-strip still reads as selected, and it is the one cue that
|
|
29
|
+
* survives being the only dark band on screen.
|
|
30
|
+
*
|
|
31
|
+
* Kept in one module because four components draw parts of the same chrome, and
|
|
32
|
+
* the four-way tab colouring below is a rule about the whole, not about any one
|
|
33
|
+
* of them.
|
|
34
|
+
*/
|
|
35
|
+
export declare const dv: {
|
|
36
|
+
/** the tab strip itself */
|
|
37
|
+
readonly tabsBackground: '#252526';
|
|
38
|
+
readonly activeGroupVisibleTabBackground: '#1e1e1e';
|
|
39
|
+
readonly activeGroupHiddenTabBackground: '#2d2d2d';
|
|
40
|
+
readonly inactiveGroupVisibleTabBackground: '#1e1e1e';
|
|
41
|
+
readonly inactiveGroupHiddenTabBackground: '#2d2d2d';
|
|
42
|
+
readonly activeGroupVisibleTabColor: 'white';
|
|
43
|
+
readonly activeGroupHiddenTabColor: '#969696';
|
|
44
|
+
readonly inactiveGroupVisibleTabColor: '#8f8f8f';
|
|
45
|
+
readonly inactiveGroupHiddenTabColor: '#626262';
|
|
46
|
+
readonly tabDividerColor: '#1e1e1e';
|
|
47
|
+
readonly separatorBorder: 'rgb(68, 68, 68)';
|
|
48
|
+
readonly iconHoverBackground: 'rgba(90, 93, 94, 0.31)';
|
|
49
|
+
readonly dragOverBackground: 'rgba(83, 89, 93, 0.5)';
|
|
50
|
+
readonly edgeDockIndicatorColor: 'rgba(56, 139, 253, 0.9)';
|
|
51
|
+
readonly tabsHeight: 35;
|
|
52
|
+
readonly tabsFontSize: 13;
|
|
53
|
+
/** the sash is 4px of grab area with a 1px line drawn inside it */
|
|
54
|
+
readonly sashSize: 4;
|
|
55
|
+
};
|
|
56
|
+
/** The background and text a tab takes, given where it sits. */
|
|
57
|
+
export declare function tabColors(groupActive: boolean, visible: boolean): {
|
|
58
|
+
background: "#1e1e1e";
|
|
59
|
+
color: "white";
|
|
60
|
+
} | {
|
|
61
|
+
background: "#2d2d2d";
|
|
62
|
+
color: "#969696";
|
|
63
|
+
} | {
|
|
64
|
+
background: "#1e1e1e";
|
|
65
|
+
color: "#8f8f8f";
|
|
66
|
+
} | {
|
|
67
|
+
background: "#2d2d2d";
|
|
68
|
+
color: "#626262";
|
|
69
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dockview's `dockview-theme-dark`, transcribed.
|
|
3
|
+
*
|
|
4
|
+
* We render the workspace ourselves now, but the look is deliberately not a new
|
|
5
|
+
* design — it is the theme we already shipped, so nothing about the chrome
|
|
6
|
+
* changed for users when the engine underneath it did. Every value below is
|
|
7
|
+
* copied from `dockview/dist/styles/dockview.css`'s `.dockview-theme-dark`
|
|
8
|
+
* block (v8.0.0); the names are its CSS custom properties with the `--dv-`
|
|
9
|
+
* prefix dropped.
|
|
10
|
+
*
|
|
11
|
+
* **Fixed, not theme-derived.** These apply in a light JBrowse theme too, the
|
|
12
|
+
* same way the app bar is dark in both — the chrome frames the content rather
|
|
13
|
+
* than matching it. Don't wire them to `theme.palette`: a light strip reads as
|
|
14
|
+
* content, which is exactly what it looked like before this file existed.
|
|
15
|
+
*
|
|
16
|
+
* **This is the chrome only, and the panel BODY is not chrome.** dockview also
|
|
17
|
+
* has a `--dv-group-view-background-color` (`#1e1e1e`) for the surface a tab's
|
|
18
|
+
* content sits on, and transcribing it is what made a light JBrowse theme come
|
|
19
|
+
* up dark below the views — the frame swallowing the thing it frames. The body
|
|
20
|
+
* follows `theme.palette.background.default` in `PanelView` instead, so the
|
|
21
|
+
* cell matches every other surface the app draws content on. It is deliberately
|
|
22
|
+
* absent here rather than present and unused, since an unused colour named
|
|
23
|
+
* after the panel reads as a value someone forgot to wire up.
|
|
24
|
+
*
|
|
25
|
+
* A consequence worth knowing before "fixing" it: dockview's selected tab is
|
|
26
|
+
* `#1e1e1e` **because** the body was, so the tab merged into its content. Ours
|
|
27
|
+
* cannot merge into a light body, and the four values below are kept anyway —
|
|
28
|
+
* darker-than-the-strip still reads as selected, and it is the one cue that
|
|
29
|
+
* survives being the only dark band on screen.
|
|
30
|
+
*
|
|
31
|
+
* Kept in one module because four components draw parts of the same chrome, and
|
|
32
|
+
* the four-way tab colouring below is a rule about the whole, not about any one
|
|
33
|
+
* of them.
|
|
34
|
+
*/
|
|
35
|
+
export const dv = {
|
|
36
|
+
/** the tab strip itself */
|
|
37
|
+
tabsBackground: '#252526',
|
|
38
|
+
// A tab's colours depend on TWO things: whether its panel is the active one,
|
|
39
|
+
// and whether it is the tab that panel is showing. dockview enumerates all
|
|
40
|
+
// four rather than dimming one axis, so the focused panel's selected tab is
|
|
41
|
+
// the only fully-white label on screen.
|
|
42
|
+
activeGroupVisibleTabBackground: '#1e1e1e',
|
|
43
|
+
activeGroupHiddenTabBackground: '#2d2d2d',
|
|
44
|
+
inactiveGroupVisibleTabBackground: '#1e1e1e',
|
|
45
|
+
inactiveGroupHiddenTabBackground: '#2d2d2d',
|
|
46
|
+
activeGroupVisibleTabColor: 'white',
|
|
47
|
+
activeGroupHiddenTabColor: '#969696',
|
|
48
|
+
inactiveGroupVisibleTabColor: '#8f8f8f',
|
|
49
|
+
inactiveGroupHiddenTabColor: '#626262',
|
|
50
|
+
tabDividerColor: '#1e1e1e',
|
|
51
|
+
separatorBorder: 'rgb(68, 68, 68)',
|
|
52
|
+
iconHoverBackground: 'rgba(90, 93, 94, 0.31)',
|
|
53
|
+
dragOverBackground: 'rgba(83, 89, 93, 0.5)',
|
|
54
|
+
edgeDockIndicatorColor: 'rgba(56, 139, 253, 0.9)',
|
|
55
|
+
tabsHeight: 35,
|
|
56
|
+
tabsFontSize: 13,
|
|
57
|
+
/** the sash is 4px of grab area with a 1px line drawn inside it */
|
|
58
|
+
sashSize: 4,
|
|
59
|
+
};
|
|
60
|
+
/** The background and text a tab takes, given where it sits. */
|
|
61
|
+
export function tabColors(groupActive, visible) {
|
|
62
|
+
if (groupActive) {
|
|
63
|
+
return visible
|
|
64
|
+
? {
|
|
65
|
+
background: dv.activeGroupVisibleTabBackground,
|
|
66
|
+
color: dv.activeGroupVisibleTabColor,
|
|
67
|
+
}
|
|
68
|
+
: {
|
|
69
|
+
background: dv.activeGroupHiddenTabBackground,
|
|
70
|
+
color: dv.activeGroupHiddenTabColor,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return visible
|
|
74
|
+
? {
|
|
75
|
+
background: dv.inactiveGroupVisibleTabBackground,
|
|
76
|
+
color: dv.inactiveGroupVisibleTabColor,
|
|
77
|
+
}
|
|
78
|
+
: {
|
|
79
|
+
background: dv.inactiveGroupHiddenTabBackground,
|
|
80
|
+
color: dv.inactiveGroupHiddenTabColor,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a pointer sitting over a panel would drop a view.
|
|
3
|
+
*
|
|
4
|
+
* Kept as geometry over a plain rect rather than reading the DOM, because this
|
|
5
|
+
* is the part of drag-and-drop with actual decisions in it — the edge band, the
|
|
6
|
+
* tie-break between two edges, what happens in the middle — and all of it is
|
|
7
|
+
* checkable without rendering anything or synthesising a pointer.
|
|
8
|
+
*/
|
|
9
|
+
export type DropZone = 'center' | 'left' | 'right' | 'top' | 'bottom';
|
|
10
|
+
export interface Rect {
|
|
11
|
+
left: number;
|
|
12
|
+
top: number;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Where a drop would land. `zone` alone says which half of a cell; `strip` is
|
|
18
|
+
* set when the pointer is on the tab strip itself, which is a finer answer than
|
|
19
|
+
* `center` — that one appends, this one says where in the order.
|
|
20
|
+
*/
|
|
21
|
+
export interface DropTarget {
|
|
22
|
+
zone: DropZone;
|
|
23
|
+
strip?: StripDrop;
|
|
24
|
+
}
|
|
25
|
+
/** A drop on the strip: the position in the tab order, and where to draw it. */
|
|
26
|
+
export interface StripDrop {
|
|
27
|
+
/** insertion index into the target panel's tabs, as they are ordered NOW */
|
|
28
|
+
index: number;
|
|
29
|
+
/** x of that gap, in the same space as the rects it was computed from */
|
|
30
|
+
left: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* `center` means "add as a tab in this panel"; an edge means "split this panel
|
|
34
|
+
* and put the view in the new half".
|
|
35
|
+
*
|
|
36
|
+
* The bands are proportional rather than a fixed pixel depth so a narrow panel
|
|
37
|
+
* stays droppable — at a fixed 40px a 100px-wide panel would be all edge and
|
|
38
|
+
* have no centre to drop a tab into. `edgeFraction` is capped below 0.5 for the
|
|
39
|
+
* same reason: at 0.5 the centre vanishes entirely.
|
|
40
|
+
*
|
|
41
|
+
* Corners go to whichever edge the pointer is proportionally deeper into, so
|
|
42
|
+
* the diagonal is the tie-break and neither axis silently wins. **This is a
|
|
43
|
+
* deliberate divergence from dockview**, whose `calculateQuadrantAsPercentage`
|
|
44
|
+
* tests left, right, top, bottom in that order and returns the first hit — so
|
|
45
|
+
* in its top-left corner `left` always wins, however far into the top band the
|
|
46
|
+
* pointer is. Its band is 20% where this defaults to 25%.
|
|
47
|
+
*/
|
|
48
|
+
export declare function dropZoneAt(rect: Rect, x: number, y: number, edgeFraction?: number): DropZone;
|
|
49
|
+
/**
|
|
50
|
+
* Which gap between tabs a pointer at `x` is nearest — the insertion index a
|
|
51
|
+
* drop there would use, and the x to draw the caret at.
|
|
52
|
+
*
|
|
53
|
+
* The test is each tab's MIDPOINT rather than its edges, so every x belongs to
|
|
54
|
+
* exactly one gap and there is no dead band between tabs where a drop would
|
|
55
|
+
* have to fall back to appending. `rects` are in strip order and `x` is in
|
|
56
|
+
* their coordinate space, whatever that is — this function does not know
|
|
57
|
+
* whether it was handed viewport or panel-relative numbers.
|
|
58
|
+
*
|
|
59
|
+
* An empty strip is index 0, which is the only sensible place for a first tab.
|
|
60
|
+
*/
|
|
61
|
+
export declare function stripDropAt(rects: Rect[], x: number): StripDrop;
|
|
62
|
+
/**
|
|
63
|
+
* The split a drop on `zone` asks for: which way the panel divides, and whether
|
|
64
|
+
* the new half goes first. `center` is not a split.
|
|
65
|
+
*/
|
|
66
|
+
export declare function splitForZone(zone: DropZone): {
|
|
67
|
+
readonly direction: 'row';
|
|
68
|
+
readonly before: true;
|
|
69
|
+
} | {
|
|
70
|
+
readonly direction: 'row';
|
|
71
|
+
readonly before: false;
|
|
72
|
+
} | {
|
|
73
|
+
readonly direction: 'column';
|
|
74
|
+
readonly before: true;
|
|
75
|
+
} | {
|
|
76
|
+
readonly direction: 'column';
|
|
77
|
+
readonly before: false;
|
|
78
|
+
} | undefined;
|
|
79
|
+
/** Where the drop indicator goes, as CSS percentages of the panel. */
|
|
80
|
+
export declare function indicatorRect(zone: DropZone): {
|
|
81
|
+
readonly left: '0%';
|
|
82
|
+
readonly top: '0%';
|
|
83
|
+
readonly width: '50%';
|
|
84
|
+
readonly height: '100%';
|
|
85
|
+
} | {
|
|
86
|
+
readonly left: '50%';
|
|
87
|
+
readonly top: '0%';
|
|
88
|
+
readonly width: '50%';
|
|
89
|
+
readonly height: '100%';
|
|
90
|
+
} | {
|
|
91
|
+
readonly left: '0%';
|
|
92
|
+
readonly top: '0%';
|
|
93
|
+
readonly width: '100%';
|
|
94
|
+
readonly height: '50%';
|
|
95
|
+
} | {
|
|
96
|
+
readonly left: '0%';
|
|
97
|
+
readonly top: '50%';
|
|
98
|
+
readonly width: '100%';
|
|
99
|
+
readonly height: '50%';
|
|
100
|
+
} | {
|
|
101
|
+
readonly left: '0%';
|
|
102
|
+
readonly top: '0%';
|
|
103
|
+
readonly width: '100%';
|
|
104
|
+
readonly height: '100%';
|
|
105
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a pointer sitting over a panel would drop a view.
|
|
3
|
+
*
|
|
4
|
+
* Kept as geometry over a plain rect rather than reading the DOM, because this
|
|
5
|
+
* is the part of drag-and-drop with actual decisions in it — the edge band, the
|
|
6
|
+
* tie-break between two edges, what happens in the middle — and all of it is
|
|
7
|
+
* checkable without rendering anything or synthesising a pointer.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* `center` means "add as a tab in this panel"; an edge means "split this panel
|
|
11
|
+
* and put the view in the new half".
|
|
12
|
+
*
|
|
13
|
+
* The bands are proportional rather than a fixed pixel depth so a narrow panel
|
|
14
|
+
* stays droppable — at a fixed 40px a 100px-wide panel would be all edge and
|
|
15
|
+
* have no centre to drop a tab into. `edgeFraction` is capped below 0.5 for the
|
|
16
|
+
* same reason: at 0.5 the centre vanishes entirely.
|
|
17
|
+
*
|
|
18
|
+
* Corners go to whichever edge the pointer is proportionally deeper into, so
|
|
19
|
+
* the diagonal is the tie-break and neither axis silently wins. **This is a
|
|
20
|
+
* deliberate divergence from dockview**, whose `calculateQuadrantAsPercentage`
|
|
21
|
+
* tests left, right, top, bottom in that order and returns the first hit — so
|
|
22
|
+
* in its top-left corner `left` always wins, however far into the top band the
|
|
23
|
+
* pointer is. Its band is 20% where this defaults to 25%.
|
|
24
|
+
*/
|
|
25
|
+
export function dropZoneAt(rect, x, y, edgeFraction = 0.25) {
|
|
26
|
+
const fraction = Math.min(Math.max(edgeFraction, 0), 0.49);
|
|
27
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
28
|
+
return 'center';
|
|
29
|
+
}
|
|
30
|
+
// 0 at the left/top edge, 1 at the right/bottom
|
|
31
|
+
const px = (x - rect.left) / rect.width;
|
|
32
|
+
const py = (y - rect.top) / rect.height;
|
|
33
|
+
// how far into an edge band the pointer is, as a share of that band
|
|
34
|
+
const depths = [
|
|
35
|
+
{ zone: 'left', depth: (fraction - px) / fraction },
|
|
36
|
+
{ zone: 'right', depth: (px - (1 - fraction)) / fraction },
|
|
37
|
+
{ zone: 'top', depth: (fraction - py) / fraction },
|
|
38
|
+
{ zone: 'bottom', depth: (py - (1 - fraction)) / fraction },
|
|
39
|
+
].filter(candidate => candidate.depth > 0);
|
|
40
|
+
if (depths.length === 0) {
|
|
41
|
+
return 'center';
|
|
42
|
+
}
|
|
43
|
+
return depths.reduce((best, candidate) => candidate.depth > best.depth ? candidate : best).zone;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Which gap between tabs a pointer at `x` is nearest — the insertion index a
|
|
47
|
+
* drop there would use, and the x to draw the caret at.
|
|
48
|
+
*
|
|
49
|
+
* The test is each tab's MIDPOINT rather than its edges, so every x belongs to
|
|
50
|
+
* exactly one gap and there is no dead band between tabs where a drop would
|
|
51
|
+
* have to fall back to appending. `rects` are in strip order and `x` is in
|
|
52
|
+
* their coordinate space, whatever that is — this function does not know
|
|
53
|
+
* whether it was handed viewport or panel-relative numbers.
|
|
54
|
+
*
|
|
55
|
+
* An empty strip is index 0, which is the only sensible place for a first tab.
|
|
56
|
+
*/
|
|
57
|
+
export function stripDropAt(rects, x) {
|
|
58
|
+
for (const [i, rect] of rects.entries()) {
|
|
59
|
+
if (x < rect.left + rect.width / 2) {
|
|
60
|
+
return { index: i, left: rect.left };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const last = rects.at(-1);
|
|
64
|
+
return {
|
|
65
|
+
index: rects.length,
|
|
66
|
+
left: last ? last.left + last.width : 0,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* What each zone means, in one table.
|
|
71
|
+
*
|
|
72
|
+
* `split` is the split a drop there asks for — which way the panel divides and
|
|
73
|
+
* whether the new half goes first; `center` is not a split. `rect` is where the
|
|
74
|
+
* indicator is drawn, as CSS percentages of the panel.
|
|
75
|
+
*
|
|
76
|
+
* One table rather than two switches because the pair has to agree: the shaded
|
|
77
|
+
* half IS the half the drop lands in, and that is the whole content of the
|
|
78
|
+
* indicator. Written apart, `right` could shade the left half and nothing would
|
|
79
|
+
* be wrong with either function on its own.
|
|
80
|
+
*/
|
|
81
|
+
const ZONES = {
|
|
82
|
+
left: {
|
|
83
|
+
split: { direction: 'row', before: true },
|
|
84
|
+
rect: { left: '0%', top: '0%', width: '50%', height: '100%' },
|
|
85
|
+
},
|
|
86
|
+
right: {
|
|
87
|
+
split: { direction: 'row', before: false },
|
|
88
|
+
rect: { left: '50%', top: '0%', width: '50%', height: '100%' },
|
|
89
|
+
},
|
|
90
|
+
top: {
|
|
91
|
+
split: { direction: 'column', before: true },
|
|
92
|
+
rect: { left: '0%', top: '0%', width: '100%', height: '50%' },
|
|
93
|
+
},
|
|
94
|
+
bottom: {
|
|
95
|
+
split: { direction: 'column', before: false },
|
|
96
|
+
rect: { left: '0%', top: '50%', width: '100%', height: '50%' },
|
|
97
|
+
},
|
|
98
|
+
center: {
|
|
99
|
+
split: undefined,
|
|
100
|
+
rect: { left: '0%', top: '0%', width: '100%', height: '100%' },
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* The split a drop on `zone` asks for: which way the panel divides, and whether
|
|
105
|
+
* the new half goes first. `center` is not a split.
|
|
106
|
+
*/
|
|
107
|
+
export function splitForZone(zone) {
|
|
108
|
+
return ZONES[zone].split;
|
|
109
|
+
}
|
|
110
|
+
/** Where the drop indicator goes, as CSS percentages of the panel. */
|
|
111
|
+
export function indicatorRect(zone) {
|
|
112
|
+
return ZONES[zone].rect;
|
|
113
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { WorkspaceLayoutMixin, isSessionWithWorkspaceLayout } from './model.ts';
|
|
2
|
+
export { WorkspaceContainer } from './WorkspaceContainer.tsx';
|
|
3
|
+
export { LayoutRenderer } from './LayoutRenderer.tsx';
|
|
4
|
+
export { useLayoutDrag } from './useLayoutDrag.ts';
|
|
5
|
+
export type { WorkspaceLayoutMixinType, WorkspaceLayout } from './model.ts';
|
|
6
|
+
export type { LayoutTree, PanelNode, TabNode, BranchNode, TabHome, } from './tree.ts';
|
|
7
|
+
export type { LayoutSpecNode, PendingMove, TileMode } from './spec.ts';
|
|
8
|
+
export type { DropZone } from './dropZone.ts';
|