diff --git a/packages/web/src/App.js b/packages/web/src/App.js
index f8383bf80..0af43db1d 100644
--- a/packages/web/src/App.js
+++ b/packages/web/src/App.js
@@ -7,6 +7,7 @@ import {
OpenedTabsProvider,
SavedSqlFilesProvider,
OpenedConnectionsProvider,
+ LeftPanelWidthProvider,
} from './utility/globalState';
import { SocketProvider } from './utility/SocketProvider';
import ConnectionsPinger from './utility/ConnectionsPinger';
@@ -19,9 +20,11 @@ function App() {
-
-
-
+
+
+
+
+
diff --git a/packages/web/src/Screen.js b/packages/web/src/Screen.js
index 8dbf1ed85..33f17598d 100644
--- a/packages/web/src/Screen.js
+++ b/packages/web/src/Screen.js
@@ -6,15 +6,16 @@ import styled from 'styled-components';
import TabsPanel from './TabsPanel';
import TabContent from './TabContent';
import WidgetIconPanel from './widgets/WidgetIconPanel';
-import { useCurrentWidget } from './utility/globalState';
+import { useCurrentWidget, useLeftPanelWidth, useSetLeftPanelWidth } from './utility/globalState';
import WidgetContainer from './widgets/WidgetContainer';
import ToolBar from './widgets/Toolbar';
import StatusBar from './widgets/StatusBar';
+import { useSplitterDrag, HorizontalSplitHandle } from './widgets/Splitter';
const BodyDiv = styled.div`
position: fixed;
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;
right: 0;
background-color: ${theme.mainArea.background};
@@ -43,7 +44,6 @@ const LeftPanel = styled.div`
top: ${theme.toolBar.height}px;
left: ${theme.widgetMenu.iconSize}px;
bottom: ${theme.statusBar.height}px;
- width: ${theme.leftPanel.width}px;
background-color: ${theme.leftPanel.background};
display: flex;
`;
@@ -52,11 +52,11 @@ const TabsPanelContainer = styled.div`
display: flex;
position: fixed;
top: ${theme.toolBar.height}px;
- left: ${props => theme.widgetMenu.iconSize + props.leftPanelWidth}px;
+ left: ${(props) => props.contentLeft}px;
height: ${theme.tabsPanel.height}px;
right: 0;
background-color: ${theme.tabsPanel.background};
- border-top: 1px solid #CCC;
+ border-top: 1px solid #ccc;
`;
const StausBarContainer = styled.div`
@@ -68,10 +68,21 @@ const StausBarContainer = styled.div`
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() {
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 onSplitDown = useSplitterDrag('clientX', (diff) => setLeftPanelWidth((v) => v + diff));
return (
<>
@@ -85,10 +96,16 @@ export default function Screen() {
)}
-
+ {!!currentWidget && (
+
+ )}
+
-
+
diff --git a/packages/web/src/theme.js b/packages/web/src/theme.js
index cab10aa5d..29350d09e 100644
--- a/packages/web/src/theme.js
+++ b/packages/web/src/theme.js
@@ -1,30 +1,33 @@
export default {
widgetMenu: {
iconSize: 60,
- background: "#222",
- iconFontSize: "23pt",
- iconFontColor: "#eee",
- backgroundHover: "#555",
- backgroundSelected: "#4CAF50",
+ background: '#222',
+ iconFontSize: '23pt',
+ iconFontColor: '#eee',
+ backgroundHover: '#555',
+ backgroundSelected: '#4CAF50',
},
leftPanel: {
- width: 300,
- background: "#ccc"
+ // width: 300,
+ background: '#ccc',
},
tabsPanel: {
height: 31,
- background: "#ddd",
- hoverFont: "#338"
+ background: '#ddd',
+ hoverFont: '#338',
},
statusBar: {
height: 20,
- background: "#00c"
+ background: '#00c',
},
toolBar: {
height: 30,
- background: "#eee",
+ background: '#eee',
},
mainArea: {
- background: "#eee"
- }
+ background: '#eee',
+ },
+ splitter: {
+ thickness: 3,
+ },
};
diff --git a/packages/web/src/utility/globalState.js b/packages/web/src/utility/globalState.js
index 39b3dc972..e2fd1136c 100644
--- a/packages/web/src/utility/globalState.js
+++ b/packages/web/src/utility/globalState.js
@@ -104,3 +104,7 @@ export { SavedSqlFilesProvider, useSavedSqlFiles, useSetSavedSqlFiles };
const [OpenedConnectionsProvider, useOpenedConnections, useSetOpenedConnections] = createGlobalState([]);
export { OpenedConnectionsProvider, useOpenedConnections, useSetOpenedConnections };
+
+const [LeftPanelWidthProvider, useLeftPanelWidth, useSetLeftPanelWidth] = createGlobalState(300);
+
+export { LeftPanelWidthProvider, useLeftPanelWidth, useSetLeftPanelWidth };
diff --git a/packages/web/src/widgets/Splitter.js b/packages/web/src/widgets/Splitter.js
index cf9a39e1a..d3d666e5a 100644
--- a/packages/web/src/widgets/Splitter.js
+++ b/packages/web/src/widgets/Splitter.js
@@ -2,6 +2,7 @@ import _ from 'lodash';
import React from 'react';
import styled from 'styled-components';
import useDimensions from '../utility/useDimensions';
+import theme from '../theme';
const MainContainer = styled.div`
flex: 1;
@@ -9,13 +10,20 @@ const MainContainer = styled.div`
flex-direction: column;
`;
-const VerticalSplitHandle = styled.div`
+export const VerticalSplitHandle = styled.div`
background-color: #ccc;
- height: 4px;
+ height: ${theme.splitter.thickness}px;
cursor: row-resize;
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`
// flex: 0 0 50%;
// flex-basis: 100px;
@@ -33,27 +41,16 @@ const ChildContainer2 = styled.div`
position: relative;
`;
-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]);
-
+export function useSplitterDrag(axes, onResize) {
const [resizeStart, setResizeStart] = React.useState(null);
React.useEffect(() => {
if (resizeStart != null) {
const handleResizeMove = (e) => {
e.preventDefault();
- let diff = e.clientY - resizeStart;
- setResizeStart(e.clientY);
- setHeight1((v) => v + diff);
+ let diff = e[axes] - resizeStart;
+ setResizeStart(e[axes]);
+ onResize(diff);
};
const handleResizeEnd = (e) => {
e.preventDefault();
@@ -71,9 +68,26 @@ export function VerticalSplitter({ children }) {
}, [resizeStart]);
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];
return (
diff --git a/packages/web/src/widgets/WidgetStyles.js b/packages/web/src/widgets/WidgetStyles.js
index 913bab8d2..5ea454d36 100644
--- a/packages/web/src/widgets/WidgetStyles.js
+++ b/packages/web/src/widgets/WidgetStyles.js
@@ -1,5 +1,8 @@
+// @ts-nocheck
+import React from 'react';
import styled from 'styled-components';
import theme from '../theme';
+import { useLeftPanelWidth } from '../utility/globalState';
export const SearchBoxWrapper = styled.div`
display: flex;
@@ -15,21 +18,31 @@ export const WidgetsMainContainer = styled.div`
user-select: none;
`;
-export const WidgetsOuterContainer = styled.div`
+const StyledWidgetsOuterContainer = styled.div`
flex: 1 1 0;
overflow: hidden;
- width: ${theme.leftPanel.width}px;
+ width: ${(props) => props.leftPanelWidth}px;
position: relative;
flex-direction: column;
display: flex;
`;
-export const WidgetsInnerContainer = styled.div`
+export function WidgetsOuterContainer({ children }) {
+ const leftPanelWidth = useLeftPanelWidth();
+ return {children};
+}
+
+export const StyledWidgetsInnerContainer = styled.div`
flex: 1 1;
overflow: scroll;
- width: ${theme.leftPanel.width}px;
+ width: ${(props) => props.leftPanelWidth}px;
`;
+export function WidgetsInnerContainer({ children }) {
+ const leftPanelWidth = useLeftPanelWidth();
+ return {children};
+}
+
export const Input = styled.input`
flex: 1;
min-width: 90px;