diff --git a/package.json b/package.json
index bf3f20b8..76fab6bc 100644
--- a/package.json
+++ b/package.json
@@ -42,6 +42,7 @@
"react-flot": "^1.3.0",
"react-query": "^3.39.2",
"react-redux": "^7.2.6",
+ "react-resizable": "^3.0.4",
"react-responsive": "^9.0.0-beta.6",
"react-router-dom": "^6.2.1",
"react-table": "7.7.0",
@@ -102,6 +103,7 @@
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@types/react-redux": "^7.1.24",
+ "@types/react-resizable": "^3.0.3",
"babel-loader": "^8.2.3",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-class-properties": "^6.24.1",
diff --git a/src/components/LabelBrowser/components/QueryBar.js b/src/components/LabelBrowser/components/QueryBar.js
index 0ded88ee..b76de58a 100644
--- a/src/components/LabelBrowser/components/QueryBar.js
+++ b/src/components/LabelBrowser/components/QueryBar.js
@@ -1,5 +1,5 @@
/**React */
-import { useState, useEffect, useMemo } from "react";
+import { useState, useEffect, useRef, useMemo } from "react";
import { useLocation } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
/**npm */
@@ -73,14 +73,17 @@ export const QueryBar = (props) => {
queryHistory,
start,
stop,
+ isSplit
} = useSelector((store) => store);
const panelQuery = useSelector((store) => store[name]);
- const isTabletOrMobile = useMediaQuery({ query: "(max-width: 914px)" });
+ const isTabletOrMobile = useMediaQuery({ query: "(max-width: 864px)" });
const [queryInput, setQueryInput] = useState(data.expr);
const [queryValid, setQueryValid] = useState(false);
const [queryValue, setQueryValue] = useState(queryInit(data.expr));
const [open, setOpen] = useState(false);
-
+ const wrapperRef = useRef(null)
+ const labelsButtonRef = useRef(null)
+ const buttonsContainerRef = useRef(null)
useEffect(() => {});
const saveUrl = localUrl();
const expr = useMemo(() => {
@@ -203,6 +206,15 @@ export const QueryBar = (props) => {
function onClose() {
showQuerySettings();
}
+ const getMaxWidth = () => {
+ const labelButtonWidth = !isNaN(labelsButtonRef?.current?.clientWidth) ? labelsButtonRef?.current?.clientWidth : 0;
+ const buttonsContainerWidth = !isNaN(buttonsContainerRef?.current?.clientWidth) ? buttonsContainerRef?.current?.clientWidth : 0;
+ if (isSplit || isTabletOrMobile) {
+ return 0;
+ } else {
+ return ( labelButtonWidth + buttonsContainerWidth + 5)
+ }
+ }
return (
!isEmbed && (
{
`}
>
-
+
{
/>
-
-
-
-
-
+ {!isSplit && }
+
+
+
+ {!isSplit && (
+
+
-
+
+
+ )}
- {!isTabletOrMobile &&
}
- {isTabletOrMobile && (
+ {!isTabletOrMobile && !isSplit && (
+
+ )}
+ {(isTabletOrMobile || isSplit) && (
props.theme.textColor};
+ color: ${(props) => props.theme.textColor};
background: ${(props) => props.theme.buttonDefault};
- border:1px solid ${(props)=>props.theme.buttonBorder};
- height:28px;
+ border: 1px solid ${(props) => props.theme.buttonBorder};
+ height: 28px;
span {
margin-left: 5px;
}
@@ -26,10 +26,10 @@ export const HistoryButtonStyled = styled(BtnSmall)`
export const ShowLabelsBtn = styled(BtnSmall)`
background: ${({ theme }) => theme.buttonDefault};
- border:1px solid ${(props)=>props.theme.buttonBorder};
+ border: 1px solid ${(props) => props.theme.buttonBorder};
text-overflow: ellipsis;
transition: 0.25s all;
- padding-left:6px;
+ padding-left: 6px;
justify-content: flex-start;
color: ${({ theme }) => theme.textColor};
height: 28px;
@@ -46,7 +46,7 @@ export const ShowLabelsBtn = styled(BtnSmall)`
export const QueryBarContainer = styled.div`
display: flex;
padding: 6px;
- margin-top:5px;
+ margin-top: 5px;
margin-left: 0px;
background: ${({ theme }) => theme.widgetContainer};
flex-wrap: wrap;
@@ -54,21 +54,21 @@ export const QueryBarContainer = styled.div`
`;
export const ShowLogsBtn = styled(BtnSmall)`
background: ${(props) => props.theme.primaryDark};
- border:1px solid ${(props)=>props.theme.buttonBorder};
+ border: 1px solid ${(props) => props.theme.buttonBorder};
color: ${(props) => props.theme.buttonText};
margin-left: 5px;
transition: 0.25s all;
justify-content: center;
padding: 3px 12px;
- height:28px;
+ height: 28px;
&:hover {
background: ${(props) => props.theme.primaryLight};
}
&:disabled {
background: ${(props) => props.theme.buttonDefault};
- border:1px solid ${(props)=>props.theme.buttonBorder};
+ border: 1px solid ${(props) => props.theme.buttonBorder};
cursor: not-allowed;
- color: ${props => props.theme.textColor};
+ color: ${(props) => props.theme.textColor};
}
@media screen and (max-width: 864px) {
display: ${(props) => (props.isMobile ? "flex" : "none")};
@@ -80,35 +80,33 @@ export const ShowLogsBtn = styled(BtnSmall)`
export const ShowSettingsBtn = styled(BtnSmall)`
background: none;
margin-left: 5px;
- color: ${(props)=> props.theme.textColor};
+ color: ${(props) => props.theme.textColor};
background: ${(props) => props.theme.buttonDefault};
- border:1px solid ${(props)=>props.theme.buttonBorder};
- height:28px;
+ border: 1px solid ${(props) => props.theme.buttonBorder};
+ height: 28px;
span {
margin-left: 5px;
}
- @media screen and (max-width: 864px) {
- display: ${(props) => (props.isMobile ? "flex" : "none")};
- }
+ display: ${(props) => (props.isMobile || props.isSplit ? "flex" : "none")};
`;
export const MobileTopQueryMenu = styled.div`
- display: none;
+ display: ${(props) =>
+ props.isSplit || props.dataSourceType === "flux" ? "flex" : "none"};
+
@media screen and (max-width: 864px) {
display: flex;
- justify-content: space-between
+ justify-content: space-between;
}
`;
-
-
export const InputGroup = styled.div`
display: flex;
- flex:1;
- align-items:center;
- justify-content:space-between;
+ flex: 1;
+ align-items: center;
+ justify-content: space-between;
margin-bottom: 20px;
- margin-right:10px;
+ margin-right: 10px;
`;
export const InlineGroup = styled.div`
display: flex;
@@ -120,7 +118,6 @@ export const SettingCont = styled.div`
flex: 1;
flex-direction: column;
-
background: ${({ theme }) => theme.widgetContainer};
`;
@@ -129,9 +126,9 @@ export const SettingsInputContainer = styled.div`
display: flex;
flex-direction: column;
flex: 1;
- .options-input{
- margin:10px;
- display:flex;
+ .options-input {
+ margin: 10px;
+ display: flex;
}
`;
diff --git a/src/components/Panel/Panel.js b/src/components/Panel/Panel.js
index d45c0063..830d038d 100644
--- a/src/components/Panel/Panel.js
+++ b/src/components/Panel/Panel.js
@@ -11,7 +11,7 @@ const PanelCont = styled.div`
display: flex;
flex-direction: column;
flex: 1;
- width: ${(props) => (props.isSplit ? "50%" : "100%")};
+ width: 100%;
`;
// Panel should have injected data
export default function Panel(props) {
diff --git a/src/plugins/ResizableBox/ResiableBox.tsx b/src/plugins/ResizableBox/ResiableBox.tsx
new file mode 100644
index 00000000..008e4b4c
--- /dev/null
+++ b/src/plugins/ResizableBox/ResiableBox.tsx
@@ -0,0 +1,111 @@
+import { ResizableBox as ReactResizableBox } from "react-resizable";
+import { css, cx } from "@emotion/css";
+import { useSelector } from "react-redux";
+
+interface ResizableBoxProps {
+ height: number;
+ minHeight: number;
+ maxHeight: number;
+ width: number;
+ minWidth: number;
+ maxWidth: number;
+ children: any;
+ handle: any;
+ axis: Axis;
+ resizeHandles?: Array
+ onResize: any;
+ className: string;
+}
+type Axis = 'both' | 'x' | 'y' | undefined
+type ResizeHandleAxis = 's' | 'w' | 'e' | 'n' | 'sw' | 'nw' | 'se' | 'ne';
+
+const getStyles = (theme: string): any => {
+ return {
+ "react-resizable": css`
+ position: relative;
+ `,
+ "react-resizable-handle": css`
+ position: absolute;
+ width: 20px;
+ height: 20px;
+ background-repeat: no-repeat;
+ background-origin: content-box;
+ box-sizing: border-box;
+ filter: ${theme !== 'dark' ? 'none' : 'invert(100%)'};
+ background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+");
+ background-position: bottom right;
+ padding: 0 3px 3px 0;
+ `,
+ "react-resizable-handle-sw": css`
+ bottom: 0;
+ left: 0;
+ cursor: sw-resize;
+ transform: rotate(90deg);
+ `,
+ "react-resizable-handle-se": css`
+ bottom: 0;
+ right: 0;
+ cursor: se-resize;
+ `,
+ "react-resizable-handle-nw": css`
+ top: 0;
+ left: 0;
+ cursor: nw-resize;
+ transform: rotate(180deg);
+ `,
+ "react-resizable-handle-ne": css`
+ top: 0;
+ right: 0;
+ cursor: ne-resize;
+ transform: rotate(270deg);
+ `,
+ "react-resizable-handle-w": css`
+ top: 50%;
+ margin-top: -10px;
+ cursor: ew-resize;
+ left: 0;
+ transform: rotate(135deg);
+ `,
+ "react-resizable-handle-e": css`
+ top: 50%;
+ margin-top: -10px;
+ cursor: ew-resize;
+ right: 0;
+ transform: rotate(315deg);
+ `,
+ "react-resizable-handle-n": css`
+ left: 50%;
+ margin-left: -10px;
+ cursor: ns-resize;
+ top: 0;
+ transform: rotate(225deg);
+ `,
+ "react-resizable-handle-s": css`
+ left: 50%;
+ margin-left: -10px;
+ cursor: ns-resize;
+ bottom: 0;
+ transform: rotate(45deg);
+ `
+}};
+export function ResizableBox(props: ResizableBoxProps) {
+
+ const storeTheme = useSelector(({ theme }: any) => theme);
+ const { height, width, children, minWidth, maxWidth, minHeight, maxHeight, resizeHandles, onResize, axis, className } = props;
+ const styles = getStyles(storeTheme);
+ const handleFn = (axis: ResizeHandleAxis, ref: any) => {
+ return ;
+ };
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/plugins/queryeditor/index.js b/src/plugins/queryeditor/index.js
index 1fbb00ce..f37470f0 100644
--- a/src/plugins/queryeditor/index.js
+++ b/src/plugins/queryeditor/index.js
@@ -1,6 +1,6 @@
import styled from "@emotion/styled";
import { css } from "@emotion/css";
-import React, { useCallback, useState, useMemo, useEffect } from "react";
+import React, { useCallback, useState, useMemo, useEffect, useRef } from "react";
import { createEditor, Text } from "slate";
import { Slate, Editable, withReact } from "slate-react";
@@ -11,9 +11,11 @@ import "prismjs/components/prism-sql";
import { themes } from "../../theme/themes";
import { ThemeProvider } from "@emotion/react";
import { useSelector } from "react-redux";
-
+import { ResizableBox } from "../ResizableBox/ResiableBox";
+import { useMediaQuery } from "react-responsive";
const CustomEditor = styled(Editable)`
flex: 1;
+ height: 100%;
background: ${(props) => props.theme.inputBg};
border: 1px solid ${(props) => props.theme.buttonBorder};
color: ${(props) => props.theme.textColor};
@@ -21,11 +23,16 @@ const CustomEditor = styled(Editable)`
font-size: 1em;
font-family: monospace;
margin: 0px 5px;
+ margin-bottom: 20px;
border-radius: 3px;
line-height: 1.5;
line-break: anywhere;
+ overflow-y: scroll;
+`;
+const Resizable = css`
+ margin-bottom: 10px;
+ width: 100%;
`;
-
const QueryBar = styled.div`
display: flex;
align-items: center;
@@ -106,12 +113,28 @@ export function getTokenLength(token) {
return token.content.reduce((l, t) => l + getTokenLength(t), 0);
}
-export default function QueryEditor({ onQueryChange, value, onKeyDown, defaultValue }) {
+export default function QueryEditor({
+ onQueryChange,
+ value,
+ onKeyDown,
+ defaultValue,
+ isSplit,
+ wrapperRef
+}) {
const theme = useSelector((store) => store.theme);
const renderLeaf = useCallback((props) => , []);
-
+ const [height, setHeight] = useState(0);
+ const [width, setWidth] = useState(0);
const editor = useMemo(() => withHistory(withReact(createEditor())), []);
+ const ref = useRef(null);
+
+ useEffect(()=> {
+ setHeight(30)
+ },[setHeight])
+ useEffect(()=> {
+ setWidth(wrapperRef)
+ },[width, setWidth, isSplit, wrapperRef])
// Keep track of state for the value of the editor.
const [language] = useState("sql");
@@ -152,26 +175,46 @@ export default function QueryEditor({ onQueryChange, value, onKeyDown, defaultVa
setEditorValue(value);
editor.children = value;
}, [value, setEditorValue]);
-
+ const onResize = (e, {size}) => {
+ console.log(size)
+ setHeight(size.height)
+ };
return (
-
+
{/* */}
+
+
+ */}
-
+ {" "}
+
+
+
diff --git a/src/views/Main.js b/src/views/Main.js
index fc6f7863..d265c902 100644
--- a/src/views/Main.js
+++ b/src/views/Main.js
@@ -10,8 +10,8 @@ import StatusBar from "../components/StatusBar";
import QueryHistory from "../plugins/queryhistory";
import { useMediaQuery } from "react-responsive";
import MainTabs from "./MainTabs.js";
-import { useMemo } from "react";
-
+import { useMemo, useState, useEffect, useRef } from "react";
+import { ResizableBox } from "../plugins/ResizableBox/ResiableBox";
export const MainContainer = styled.div`
position: absolute;
display: flex;
@@ -23,7 +23,7 @@ export const MainContainer = styled.div`
background-color: ${(props) => props.theme.mainBgColor} !important;
&::-webkit-scrollbar-corner {
background: transparent;
- }
+ }
&::-webkit-scrollbar-thumb {
border-radius: 5px;
background: ${(props) => props.theme.scrollbarThumb} !important;
@@ -62,13 +62,118 @@ export function MobileView({ theme, isEmbed, settingsDialogOpen }) {
* @returns Desktop View
*/
export function DesktopView({ theme, isEmbed, isSplit, settingsDialogOpen }) {
+ const [height, setHeight] = useState(0);
+ const [widthTotal, setWidthTotal] = useState(0);
+ const [widthLeft, setWidthLeft] = useState(0);
+ const [widthRight, setWidthRight] = useState(0);
+ const [widthLeftPercent, setWidthLeftPercent] = useState(0);
+ const [widthRightPercent, setWidthRightercent] = useState(0);
+ const [minWidth, setMinWidth] = useState(0);
+ const [maxWidth, setMaxWidth] = useState(0);
+ const refTotal = useRef(null);
+ useEffect(() => {
+ const widthTotal = refTotal.current.clientWidth
+ setHeight(refTotal.current.clientHeight);
+ setWidthTotal(refTotal.current.clientWidth);
+ setWidthLeft(widthTotal / (isSplit ? 2 : 1));
+ if (isSplit) {
+ setWidthRight(widthTotal / 2);
+ }
+ const realMinWidth = !isSplit ? widthTotal : widthTotal / 4 > 370 ? widthTotal / 4 : 370;
+ setMinWidth(realMinWidth);
+ const realMaxWidth = !isSplit ? widthTotal : widthTotal - realMinWidth
+ setMaxWidth(realMaxWidth);
+ }, [
+ setWidthLeft,
+ setWidthRight,
+ setWidthTotal,
+ setHeight,
+ setMinWidth,
+ setMaxWidth,
+ minWidth,
+ isSplit,
+ ]);
+ useEffect(() => {
+ const widthTotal = refTotal.current.clientWidth
+ setWidthLeftPercent(widthLeft / widthTotal);
+ if (isSplit) {
+ setWidthRightercent(widthRight / widthTotal);
+ }
+ }, [widthLeft, widthRight]);
+ useEffect(() => {
+ const onWindowResize = () => {
+ const widthTotal = refTotal.current.clientWidth
+ setWidthTotal(widthTotal);
+ setWidthLeft(widthTotal * widthLeftPercent);
+ if (isSplit) {
+ setWidthRight(widthTotal * widthRightPercent);
+ }
+ };
+ window.addEventListener("resize", onWindowResize);
+ return () => {
+ window.removeEventListener("resize", onWindowResize);
+ };
+ }, [
+ widthTotal,
+ widthLeft,
+ widthRight,
+ widthLeftPercent,
+ widthRightPercent,
+ isSplit,
+ ]);
+ const onSplitResize = (event, { element, size, handle }) => {
+ if (handle === "e") {
+ setWidthRight(widthTotal - size.width);
+ setWidthLeft(size.width);
+ } else {
+ setWidthLeft(widthTotal - size.width);
+ setWidthRight(size.width);
+ }
+ setWidthLeftPercent(widthLeft / widthTotal);
+ setWidthRightercent(widthRight / widthTotal);
+ };
+
return (
{!isEmbed && }
-
-
- {isSplit &&
}
+
+
+
+
+ {isSplit && (
+
+
+
+ )}