diff --git a/ui/src/components/diagram/WorkflowGraph.jsx b/ui/src/components/diagram/WorkflowGraph.jsx index 940919211a..0fb9c0e22c 100644 --- a/ui/src/components/diagram/WorkflowGraph.jsx +++ b/ui/src/components/diagram/WorkflowGraph.jsx @@ -55,6 +55,7 @@ class WorkflowGraph extends React.PureComponent { if (resolvedRef) { inner.select(`g[id='${resolvedRef}']`).classed("selected", true); } + this.zoomHome(); } else if (prevProps.width !== this.props.width) { if (prevProps.width > 0 && this.props.width > 0) { console.log( diff --git a/ui/src/pages/execution/Execution.jsx b/ui/src/pages/execution/Execution.jsx index fbfdc44290..566d9d614e 100644 --- a/ui/src/pages/execution/Execution.jsx +++ b/ui/src/pages/execution/Execution.jsx @@ -1,4 +1,10 @@ -import React, { useState, useMemo } from "react"; +import React, { + useState, + useMemo, + useReducer, + useEffect, + useCallback, +} from "react"; import { useQueryState } from "react-router-use-location-state"; import { Drawer, Divider } from "@material-ui/core"; @@ -22,13 +28,15 @@ import clsx from "clsx"; import ActionModule from "./ActionModule"; import IconButton from "@material-ui/core/IconButton"; import CloseIcon from "@material-ui/icons/Close"; +import SettingsOverscanIcon from "@material-ui/icons/SettingsOverscan"; import RightPanel from "./RightPanel"; import WorkflowDAG from "../../components/diagram/WorkflowDAG"; import StatusBadge from "../../components/StatusBadge"; import { useFetch } from "../../utils/query"; import { Helmet } from "react-helmet"; -const drawerWidth = 650; +const maxWindowWidth = window.innerWidth; +const drawerWidth = maxWindowWidth > 1000 ? maxWindowWidth / 2 : 450; const useStyles = makeStyles({ wrapper: { @@ -38,11 +46,11 @@ const useStyles = makeStyles({ flexDirection: "row", }, drawer: { - width: drawerWidth, + width: (state) => state.drawerWidth, }, drawerPaper: { height: "100vh", - width: drawerWidth, + width: (state) => state.drawerWidth, overflowY: "hidden", }, drawerHeader: { @@ -51,14 +59,14 @@ const useStyles = makeStyles({ padding: 10, // necessary for content to be below app bar //...theme.mixins.toolbar, - justifyContent: "flex-end", + justifyContent: "space-between", }, drawerContent: { overflowY: "scroll", flexGrow: 1, }, content: { - flexGrow: 1, + flexGrow: (state) => (state.isResizing ? 0.8 : 1), paddingBottom: 50, /*transition: theme.transitions.create("margin", { easing: theme.transitions.easing.sharp, @@ -100,7 +108,6 @@ const useStyles = makeStyles({ fr: { display: "flex", position: "relative", - zIndex: 9999, float: "right", marginRight: 50, marginTop: 10, @@ -110,38 +117,147 @@ const useStyles = makeStyles({ alignItems: "center", marginRight: 15, }, + dragger: { + width: "5px", + cursor: "ew-resize", + padding: "4px 0 0", + borderTop: "1px solid #ddd", + position: "absolute", + top: 0, + left: 0, + bottom: 0, + zIndex: "100", + backgroundColor: "#f4f7f9", + }, }); +const actions = { + EXPAND_FULL: 1, + RESET_EXPAND_FULL: 2, + MOUSE_DOWN: 3, + MOUSE_UP: 4, + MOUSE_MOVE: 5, +}; + +const initialState = { + drawerWidth: drawerWidth, + isDrawerFullScreen: false, + isResizing: false, + lastDownX: 0, + newWidth: {}, + drawerVarient: "persistent", +}; + +function reducer(state, action) { + switch (action.type) { + case actions.EXPAND_FULL: + return { ...state, isDrawerFullScreen: true, drawerWidth: "100vw" }; + case actions.RESET_EXPAND_FULL: + return { + ...state, + isDrawerFullScreen: false, + drawerWidth: drawerWidth, + drawerVarient: "persistent", + }; + case actions.MOUSE_DOWN: + return { + ...state, + isResizing: true, + lastDownX: action.clientX, + drawerVarient: "temporary", + }; + case actions.MOUSE_UP: + return { + ...state, + isResizing: false, + drawerVarient: + state.drawerWidth > maxWindowWidth / 2 ? "temporary" : "persistent", + }; + case actions.MOUSE_MOVE: + return { + ...state, + isDrawerFullScreen: false, + drawerWidth: action.offsetRight, + }; + default: + return state; + } +} + export default function Execution() { - const classes = useStyles(); + const [state, dispatch] = useReducer(reducer, initialState); + + const classes = useStyles(state); const match = useRouteMatch(); const url = `/workflow/${match.params.id}`; const { data: execution, isFetching, refetch: refresh } = useFetch(url); const [tabIndex, setTabIndex] = useQueryState("tabIndex", 0); - const [selectedTask, setSelectedTask] = useState(null); - + const [selectedTask, setSelectedTask] = useState(null); + + const handleMousedown = (e) => { + dispatch({ type: actions.MOUSE_DOWN, clientX: e.clientX }); + }; + + const handleMousemove = useCallback( + (e) => { + // we don't want to do anything if we aren't resizing. + if (!state.isResizing) { + return; + } + + const offsetRight = + document.body.offsetWidth - (e.clientX - document.body.offsetLeft); + const minWidth = 0; + const maxWidth = maxWindowWidth-100; + if (offsetRight > minWidth && offsetRight < maxWidth) { + dispatch({ type: actions.MOUSE_MOVE, offsetRight }); + } + }, + [state.isResizing] + ); + + const handleMouseup = (e) => { + dispatch({ type: actions.MOUSE_UP }); + }; + const handleSelectedTask = (task) => { - if(task){ + if (task) { const { taskToDomain } = execution; let domain; - if(taskToDomain['*']){ - domain = taskToDomain['*']; - } - else if(task.taskType){ + if (taskToDomain["*"]) { + domain = taskToDomain["*"]; + } else if (task.taskType) { domain = taskToDomain[task.taskType]; } - + setSelectedTask({ ...task, - domain: domain + domain: domain, }); - } - else { + } else { setSelectedTask(null); } - } + }; + + useEffect(() => { + const mouseMove = (e) => handleMousemove(e); + const mouseUp = (e) => handleMouseup(e); + + document.addEventListener("mousemove", mouseMove); + document.addEventListener("mouseup", mouseUp); + + return () => { + document.removeEventListener("mousemove", mouseMove); + document.removeEventListener("mouseup", mouseUp); + }; + }, [handleMousemove]); + + const handleDrawerMaximize = () => { + if (state.isDrawerFullScreen) dispatch({ type: actions.RESET_EXPAND_FULL }); + else dispatch({ type: actions.EXPAND_FULL }); + }; const dag = useMemo( () => (execution ? new WorkflowDAG(execution) : null), @@ -213,26 +329,37 @@ export default function Execution() { -
- handleSelectedTask(null)}> - - -
- - handleMousedown(event)} + className={classes.dragger} /> + <> +
+ handleDrawerMaximize()}> + + + handleSelectedTask(null)}> + + +
+ + +
);