mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
resizable widget container
This commit is contained in:
parent
dc87aeeb43
commit
836db096a9
@ -7,6 +7,7 @@ import {
|
|||||||
OpenedTabsProvider,
|
OpenedTabsProvider,
|
||||||
SavedSqlFilesProvider,
|
SavedSqlFilesProvider,
|
||||||
OpenedConnectionsProvider,
|
OpenedConnectionsProvider,
|
||||||
|
LeftPanelWidthProvider,
|
||||||
} from './utility/globalState';
|
} from './utility/globalState';
|
||||||
import { SocketProvider } from './utility/SocketProvider';
|
import { SocketProvider } from './utility/SocketProvider';
|
||||||
import ConnectionsPinger from './utility/ConnectionsPinger';
|
import ConnectionsPinger from './utility/ConnectionsPinger';
|
||||||
@ -19,9 +20,11 @@ function App() {
|
|||||||
<OpenedTabsProvider>
|
<OpenedTabsProvider>
|
||||||
<SavedSqlFilesProvider>
|
<SavedSqlFilesProvider>
|
||||||
<OpenedConnectionsProvider>
|
<OpenedConnectionsProvider>
|
||||||
|
<LeftPanelWidthProvider>
|
||||||
<ConnectionsPinger>
|
<ConnectionsPinger>
|
||||||
<Screen />
|
<Screen />
|
||||||
</ConnectionsPinger>
|
</ConnectionsPinger>
|
||||||
|
</LeftPanelWidthProvider>
|
||||||
</OpenedConnectionsProvider>
|
</OpenedConnectionsProvider>
|
||||||
</SavedSqlFilesProvider>
|
</SavedSqlFilesProvider>
|
||||||
</OpenedTabsProvider>
|
</OpenedTabsProvider>
|
||||||
|
@ -6,15 +6,16 @@ import styled from 'styled-components';
|
|||||||
import TabsPanel from './TabsPanel';
|
import TabsPanel from './TabsPanel';
|
||||||
import TabContent from './TabContent';
|
import TabContent from './TabContent';
|
||||||
import WidgetIconPanel from './widgets/WidgetIconPanel';
|
import WidgetIconPanel from './widgets/WidgetIconPanel';
|
||||||
import { useCurrentWidget } from './utility/globalState';
|
import { useCurrentWidget, useLeftPanelWidth, useSetLeftPanelWidth } from './utility/globalState';
|
||||||
import WidgetContainer from './widgets/WidgetContainer';
|
import WidgetContainer from './widgets/WidgetContainer';
|
||||||
import ToolBar from './widgets/Toolbar';
|
import ToolBar from './widgets/Toolbar';
|
||||||
import StatusBar from './widgets/StatusBar';
|
import StatusBar from './widgets/StatusBar';
|
||||||
|
import { useSplitterDrag, HorizontalSplitHandle } from './widgets/Splitter';
|
||||||
|
|
||||||
const BodyDiv = styled.div`
|
const BodyDiv = styled.div`
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: ${theme.tabsPanel.height + theme.toolBar.height}px;
|
top: ${theme.tabsPanel.height + theme.toolBar.height}px;
|
||||||
left: ${props => theme.widgetMenu.iconSize + props.leftPanelWidth}px;
|
left: ${(props) => props.contentLeft}px;
|
||||||
bottom: ${theme.statusBar.height}px;
|
bottom: ${theme.statusBar.height}px;
|
||||||
right: 0;
|
right: 0;
|
||||||
background-color: ${theme.mainArea.background};
|
background-color: ${theme.mainArea.background};
|
||||||
@ -43,7 +44,6 @@ const LeftPanel = styled.div`
|
|||||||
top: ${theme.toolBar.height}px;
|
top: ${theme.toolBar.height}px;
|
||||||
left: ${theme.widgetMenu.iconSize}px;
|
left: ${theme.widgetMenu.iconSize}px;
|
||||||
bottom: ${theme.statusBar.height}px;
|
bottom: ${theme.statusBar.height}px;
|
||||||
width: ${theme.leftPanel.width}px;
|
|
||||||
background-color: ${theme.leftPanel.background};
|
background-color: ${theme.leftPanel.background};
|
||||||
display: flex;
|
display: flex;
|
||||||
`;
|
`;
|
||||||
@ -52,11 +52,11 @@ const TabsPanelContainer = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: ${theme.toolBar.height}px;
|
top: ${theme.toolBar.height}px;
|
||||||
left: ${props => theme.widgetMenu.iconSize + props.leftPanelWidth}px;
|
left: ${(props) => props.contentLeft}px;
|
||||||
height: ${theme.tabsPanel.height}px;
|
height: ${theme.tabsPanel.height}px;
|
||||||
right: 0;
|
right: 0;
|
||||||
background-color: ${theme.tabsPanel.background};
|
background-color: ${theme.tabsPanel.background};
|
||||||
border-top: 1px solid #CCC;
|
border-top: 1px solid #ccc;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StausBarContainer = styled.div`
|
const StausBarContainer = styled.div`
|
||||||
@ -68,10 +68,21 @@ const StausBarContainer = styled.div`
|
|||||||
background-color: ${theme.statusBar.background};
|
background-color: ${theme.statusBar.background};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const ScreenHorizontalSplitHandle = styled(HorizontalSplitHandle)`
|
||||||
|
position: absolute;
|
||||||
|
top: ${theme.toolBar.height}px;
|
||||||
|
bottom: ${theme.statusBar.height}px;
|
||||||
|
`;
|
||||||
|
|
||||||
export default function Screen() {
|
export default function Screen() {
|
||||||
const currentWidget = useCurrentWidget();
|
const currentWidget = useCurrentWidget();
|
||||||
const leftPanelWidth = currentWidget ? theme.leftPanel.width : 0;
|
const leftPanelWidth = useLeftPanelWidth();
|
||||||
|
const setLeftPanelWidth = useSetLeftPanelWidth();
|
||||||
|
const contentLeft = currentWidget
|
||||||
|
? theme.widgetMenu.iconSize + leftPanelWidth + theme.splitter.thickness
|
||||||
|
: theme.widgetMenu.iconSize;
|
||||||
const toolbarPortalRef = React.useRef();
|
const toolbarPortalRef = React.useRef();
|
||||||
|
const onSplitDown = useSplitterDrag('clientX', (diff) => setLeftPanelWidth((v) => v + diff));
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ToolBarDiv>
|
<ToolBarDiv>
|
||||||
@ -85,10 +96,16 @@ export default function Screen() {
|
|||||||
<WidgetContainer />
|
<WidgetContainer />
|
||||||
</LeftPanel>
|
</LeftPanel>
|
||||||
)}
|
)}
|
||||||
<TabsPanelContainer leftPanelWidth={leftPanelWidth}>
|
{!!currentWidget && (
|
||||||
|
<ScreenHorizontalSplitHandle
|
||||||
|
onMouseDown={onSplitDown}
|
||||||
|
style={{ left: leftPanelWidth + theme.widgetMenu.iconSize }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<TabsPanelContainer contentLeft={contentLeft}>
|
||||||
<TabsPanel></TabsPanel>
|
<TabsPanel></TabsPanel>
|
||||||
</TabsPanelContainer>
|
</TabsPanelContainer>
|
||||||
<BodyDiv leftPanelWidth={leftPanelWidth}>
|
<BodyDiv contentLeft={contentLeft}>
|
||||||
<TabContent toolbarPortalRef={toolbarPortalRef} />
|
<TabContent toolbarPortalRef={toolbarPortalRef} />
|
||||||
</BodyDiv>
|
</BodyDiv>
|
||||||
<StausBarContainer>
|
<StausBarContainer>
|
||||||
|
@ -1,30 +1,33 @@
|
|||||||
export default {
|
export default {
|
||||||
widgetMenu: {
|
widgetMenu: {
|
||||||
iconSize: 60,
|
iconSize: 60,
|
||||||
background: "#222",
|
background: '#222',
|
||||||
iconFontSize: "23pt",
|
iconFontSize: '23pt',
|
||||||
iconFontColor: "#eee",
|
iconFontColor: '#eee',
|
||||||
backgroundHover: "#555",
|
backgroundHover: '#555',
|
||||||
backgroundSelected: "#4CAF50",
|
backgroundSelected: '#4CAF50',
|
||||||
},
|
},
|
||||||
leftPanel: {
|
leftPanel: {
|
||||||
width: 300,
|
// width: 300,
|
||||||
background: "#ccc"
|
background: '#ccc',
|
||||||
},
|
},
|
||||||
tabsPanel: {
|
tabsPanel: {
|
||||||
height: 31,
|
height: 31,
|
||||||
background: "#ddd",
|
background: '#ddd',
|
||||||
hoverFont: "#338"
|
hoverFont: '#338',
|
||||||
},
|
},
|
||||||
statusBar: {
|
statusBar: {
|
||||||
height: 20,
|
height: 20,
|
||||||
background: "#00c"
|
background: '#00c',
|
||||||
},
|
},
|
||||||
toolBar: {
|
toolBar: {
|
||||||
height: 30,
|
height: 30,
|
||||||
background: "#eee",
|
background: '#eee',
|
||||||
},
|
},
|
||||||
mainArea: {
|
mainArea: {
|
||||||
background: "#eee"
|
background: '#eee',
|
||||||
}
|
},
|
||||||
|
splitter: {
|
||||||
|
thickness: 3,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -104,3 +104,7 @@ export { SavedSqlFilesProvider, useSavedSqlFiles, useSetSavedSqlFiles };
|
|||||||
const [OpenedConnectionsProvider, useOpenedConnections, useSetOpenedConnections] = createGlobalState([]);
|
const [OpenedConnectionsProvider, useOpenedConnections, useSetOpenedConnections] = createGlobalState([]);
|
||||||
|
|
||||||
export { OpenedConnectionsProvider, useOpenedConnections, useSetOpenedConnections };
|
export { OpenedConnectionsProvider, useOpenedConnections, useSetOpenedConnections };
|
||||||
|
|
||||||
|
const [LeftPanelWidthProvider, useLeftPanelWidth, useSetLeftPanelWidth] = createGlobalState(300);
|
||||||
|
|
||||||
|
export { LeftPanelWidthProvider, useLeftPanelWidth, useSetLeftPanelWidth };
|
||||||
|
@ -2,6 +2,7 @@ import _ from 'lodash';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import useDimensions from '../utility/useDimensions';
|
import useDimensions from '../utility/useDimensions';
|
||||||
|
import theme from '../theme';
|
||||||
|
|
||||||
const MainContainer = styled.div`
|
const MainContainer = styled.div`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
@ -9,13 +10,20 @@ const MainContainer = styled.div`
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const VerticalSplitHandle = styled.div`
|
export const VerticalSplitHandle = styled.div`
|
||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
height: 4px;
|
height: ${theme.splitter.thickness}px;
|
||||||
cursor: row-resize;
|
cursor: row-resize;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const HorizontalSplitHandle = styled.div`
|
||||||
|
background-color: #ccc;
|
||||||
|
width: ${theme.splitter.thickness}px;
|
||||||
|
cursor: col-resize;
|
||||||
|
z-index: 1;
|
||||||
|
`;
|
||||||
|
|
||||||
const ChildContainer1 = styled.div`
|
const ChildContainer1 = styled.div`
|
||||||
// flex: 0 0 50%;
|
// flex: 0 0 50%;
|
||||||
// flex-basis: 100px;
|
// flex-basis: 100px;
|
||||||
@ -33,27 +41,16 @@ const ChildContainer2 = styled.div`
|
|||||||
position: relative;
|
position: relative;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function VerticalSplitter({ children }) {
|
export function useSplitterDrag(axes, onResize) {
|
||||||
const childrenArray = _.isArray(children) ? children : [children];
|
|
||||||
if (childrenArray.length !== 1 && childrenArray.length != 2) {
|
|
||||||
throw new Error('Splitter must have 1 or 2 children');
|
|
||||||
}
|
|
||||||
const [refNode, dimensions] = useDimensions();
|
|
||||||
const [height1, setHeight1] = React.useState(0);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
setHeight1(dimensions.height / 2);
|
|
||||||
}, [dimensions]);
|
|
||||||
|
|
||||||
const [resizeStart, setResizeStart] = React.useState(null);
|
const [resizeStart, setResizeStart] = React.useState(null);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (resizeStart != null) {
|
if (resizeStart != null) {
|
||||||
const handleResizeMove = (e) => {
|
const handleResizeMove = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let diff = e.clientY - resizeStart;
|
let diff = e[axes] - resizeStart;
|
||||||
setResizeStart(e.clientY);
|
setResizeStart(e[axes]);
|
||||||
setHeight1((v) => v + diff);
|
onResize(diff);
|
||||||
};
|
};
|
||||||
const handleResizeEnd = (e) => {
|
const handleResizeEnd = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -71,9 +68,26 @@ export function VerticalSplitter({ children }) {
|
|||||||
}, [resizeStart]);
|
}, [resizeStart]);
|
||||||
|
|
||||||
const handleResizeDown = (e) => {
|
const handleResizeDown = (e) => {
|
||||||
setResizeStart(e.clientY);
|
setResizeStart(e[axes]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return handleResizeDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function VerticalSplitter({ children }) {
|
||||||
|
const childrenArray = _.isArray(children) ? children : [children];
|
||||||
|
if (childrenArray.length !== 1 && childrenArray.length != 2) {
|
||||||
|
throw new Error('Splitter must have 1 or 2 children');
|
||||||
|
}
|
||||||
|
const [refNode, dimensions] = useDimensions();
|
||||||
|
const [height1, setHeight1] = React.useState(0);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setHeight1(dimensions.height / 2);
|
||||||
|
}, [dimensions]);
|
||||||
|
|
||||||
|
const handleResizeDown = useSplitterDrag('clientY', (diff) => setHeight1((v) => v + diff));
|
||||||
|
|
||||||
const isSplitter = !!childrenArray[1];
|
const isSplitter = !!childrenArray[1];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import theme from '../theme';
|
import theme from '../theme';
|
||||||
|
import { useLeftPanelWidth } from '../utility/globalState';
|
||||||
|
|
||||||
export const SearchBoxWrapper = styled.div`
|
export const SearchBoxWrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -15,21 +18,31 @@ export const WidgetsMainContainer = styled.div`
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const WidgetsOuterContainer = styled.div`
|
const StyledWidgetsOuterContainer = styled.div`
|
||||||
flex: 1 1 0;
|
flex: 1 1 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: ${theme.leftPanel.width}px;
|
width: ${(props) => props.leftPanelWidth}px;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
display: flex;
|
display: flex;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const WidgetsInnerContainer = styled.div`
|
export function WidgetsOuterContainer({ children }) {
|
||||||
|
const leftPanelWidth = useLeftPanelWidth();
|
||||||
|
return <StyledWidgetsOuterContainer leftPanelWidth={leftPanelWidth}>{children}</StyledWidgetsOuterContainer>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StyledWidgetsInnerContainer = styled.div`
|
||||||
flex: 1 1;
|
flex: 1 1;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
width: ${theme.leftPanel.width}px;
|
width: ${(props) => props.leftPanelWidth}px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export function WidgetsInnerContainer({ children }) {
|
||||||
|
const leftPanelWidth = useLeftPanelWidth();
|
||||||
|
return <StyledWidgetsInnerContainer leftPanelWidth={leftPanelWidth}>{children}</StyledWidgetsInnerContainer>;
|
||||||
|
}
|
||||||
|
|
||||||
export const Input = styled.input`
|
export const Input = styled.input`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 90px;
|
min-width: 90px;
|
||||||
|
Loading…
Reference in New Issue
Block a user