import { Dispatch } from 'redux';
import { navigateTo } from 'store/navigation/navigation-action';
+export const sidePanelActions = {
+ TOGGLE_COLLAPSE: 'TOGGLE_COLLAPSE'
+}
+
export const navigateFromSidePanel = (id: string) =>
(dispatch: Dispatch) => {
dispatch<any>(navigateTo(id));
};
+
+export const toggleSidePanel = (collapsedState: boolean) => {
+ return (dispatch) => {
+ dispatch({type: sidePanelActions.TOGGLE_COLLAPSE, payload: !collapsedState})
+ }
+}
\ No newline at end of file
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { sidePanelActions } from "./side-panel-action"
+
+interface SidePanelState {
+ collapsedState: boolean
+}
+
+const sidePanelInitialState = {
+ collapsedState: false
+}
+
+export const sidePanelReducer = (state: SidePanelState = sidePanelInitialState, action)=>{
+ if(action.type === sidePanelActions.TOGGLE_COLLAPSE) return {...state, collapsedState: action.payload}
+ return state
+}
\ No newline at end of file
import { Config } from 'common/config';
import { pluginConfig } from 'plugins';
import { MiddlewareListReducer } from 'common/plugintypes';
+import { sidePanelReducer } from './side-panel/side-panel-reducer'
declare global {
interface Window {
virtualMachines: virtualMachinesReducer,
repositories: repositoriesReducer,
keepServices: keepServicesReducer,
- linkAccountPanel: linkAccountPanelReducer
+ linkAccountPanel: linkAccountPanelReducer,
+ sidePanel: sidePanelReducer
});
import { ReactNode } from "react";
import { AdminMenu } from "views-components/main-app-bar/admin-menu";
import { pluginConfig } from 'plugins';
+import { CollapseLeftPanelTrigger } from 'views-components/side-panel/side-panel'
type CssRules = 'toolbar' | 'link';
color: 'inherit'
},
toolbar: {
- height: '56px'
+ height: '56px',
+ paddingLeft: '0'
}
});
children?: ReactNode;
uuidPrefix: string;
siteBanner: string;
+ sidePanelIsCollapsed: boolean;
+ toggleSidePanel: (collapsedState:boolean) => void
}
export type MainAppBarProps = MainAppBarDataProps & WithStyles<CssRules>;
(props: MainAppBarProps) => {
return <AppBar position="absolute">
<Toolbar className={props.classes.toolbar}>
+ <CollapseLeftPanelTrigger sidePanelIsCollapsed={props.sidePanelIsCollapsed}
+ toggleSidePanel={props.toggleSidePanel}
+ />
<Grid container justify="space-between">
{pluginConfig.appBarLeft || <Grid container item xs={3} direction="column" justify="center">
<Typography variant='h6' color="inherit" noWrap>
<span dangerouslySetInnerHTML={{ __html: props.siteBanner }} /> ({props.uuidPrefix})
</Link>
</Typography>
- <Typography variant="caption" color="inherit">{props.buildInfo}</Typography>
+ <Typography variant="caption" color="inherit">
+
+ {props.buildInfo}</Typography>
</Grid>}
<Grid
item
import { Dispatch } from 'redux';
import { connect } from 'react-redux';
import { navigateFromSidePanel } from 'store/side-panel/side-panel-action';
-import { Grid } from '@material-ui/core';
+import { Grid, Tooltip, IconButton } from '@material-ui/core';
import { SidePanelButton } from 'views-components/side-panel-button/side-panel-button';
import { RootState } from 'store/store';
+import MenuIcon from "@material-ui/icons/Menu";
-const DRAWER_WITDH = 240;
+const DRAWER_WIDTH = 240;
type CssRules = 'root';
borderRight: `1px solid ${theme.palette.divider}`,
height: '100%',
overflowX: 'auto',
- width: DRAWER_WITDH,
+ width: DRAWER_WIDTH,
}
});
<SidePanelTree {...props} />
</Grid>
));
+
+type collapseButtonIconProps = {
+ sidePanelIsCollapsed: boolean;
+ toggleSidePanel: (collapsedState: boolean) => void
+}
+
+const collapseButtonIconStyles = {
+ menuIcon: {
+ height: '4rem',
+ width: '4rem',
+ paddingBottom: '0.25rem'
+ }
+}
+
+export const CollapseLeftPanelTrigger = (props: collapseButtonIconProps) =>{
+ return <Tooltip disableFocusListener title="Toggle Side Panel">
+ <IconButton onClick={()=>{props.toggleSidePanel(props.sidePanelIsCollapsed)}}>
+ <MenuIcon style={collapseButtonIconStyles.menuIcon} aria-label="Toggle Side Panel" />
+ </IconButton>
+ </Tooltip>
+ };
\ No newline at end of file
isLinkingPath: boolean;
siteBanner: string;
sessionIdleTimeout: number;
+ sidePanelIsCollapsed: boolean;
}
-type MainPanelRootProps = MainPanelRootDataProps & WithStyles<CssRules>;
+interface MainPanelRootDispatchProps {
+ toggleSidePanel: () => void
+}
+
+type MainPanelRootProps = MainPanelRootDataProps & MainPanelRootDispatchProps & WithStyles<CssRules>;
export const MainPanelRoot = withStyles(styles)(
({ classes, loading, working, user, buildInfo, uuidPrefix,
- isNotLinking, isLinkingPath, siteBanner, sessionIdleTimeout }: MainPanelRootProps) =>
- loading
+ isNotLinking, isLinkingPath, siteBanner, sessionIdleTimeout,
+ sidePanelIsCollapsed, toggleSidePanel }: MainPanelRootProps) =>{
+ return loading
? <WorkbenchLoadingScreen />
: <>
- {isNotLinking && <MainAppBar
- user={user}
- buildInfo={buildInfo}
- uuidPrefix={uuidPrefix}
- siteBanner={siteBanner}>
- {working
- ? <LinearProgress color="secondary" data-cy="linear-progress" />
- : null}
- </MainAppBar>}
- <Grid container direction="column" className={classes.root}>
- {user
- ? (user.isActive || (!user.isActive && isLinkingPath)
- ? <WorkbenchPanel isNotLinking={isNotLinking} isUserActive={user.isActive} sessionIdleTimeout={sessionIdleTimeout} />
- : <InactivePanel />)
- : <LoginPanel />}
- </Grid>
- </>
+ {isNotLinking && <MainAppBar
+ user={user}
+ buildInfo={buildInfo}
+ uuidPrefix={uuidPrefix}
+ siteBanner={siteBanner}
+ sidePanelIsCollapsed={sidePanelIsCollapsed}
+ toggleSidePanel={toggleSidePanel}
+ >
+ {working
+ ? <LinearProgress color="secondary" data-cy="linear-progress" />
+ : null}
+ </MainAppBar>}
+ <Grid container direction="column" className={classes.root}>
+ {user
+ ? (user.isActive || (!user.isActive && isLinkingPath)
+ ? <WorkbenchPanel isNotLinking={isNotLinking} isUserActive={user.isActive} sessionIdleTimeout={sessionIdleTimeout} sidePanelIsCollapsed={sidePanelIsCollapsed}/>
+ : <InactivePanel />)
+ : <LoginPanel />}
+ </Grid>
+ </>
+ }
);
import { isWorkbenchLoading } from 'store/workbench/workbench-actions';
import { LinkAccountPanelStatus } from 'store/link-account-panel/link-account-panel-reducer';
import { matchLinkAccountRoute } from 'routes/routes';
+import { toggleSidePanel } from "store/side-panel/side-panel-action";
const mapStateToProps = (state: RootState): MainPanelRootDataProps => {
return {
isNotLinking: state.linkAccountPanel.status === LinkAccountPanelStatus.NONE || state.linkAccountPanel.status === LinkAccountPanelStatus.INITIAL,
isLinkingPath: state.router.location ? matchLinkAccountRoute(state.router.location.pathname) !== null : false,
siteBanner: state.auth.config.clusterConfig.Workbench.SiteName,
- sessionIdleTimeout: parse(state.auth.config.clusterConfig.Workbench.IdleTimeout, 's') || 0
+ sessionIdleTimeout: parse(state.auth.config.clusterConfig.Workbench.IdleTimeout, 's') || 0,
+ sidePanelIsCollapsed: state.sidePanel.collapsedState,
};
};
-const mapDispatchToProps = null;
+const mapDispatchToProps = (dispatch) => {
+ return {
+ toggleSidePanel: (collapsedState)=>{
+ return dispatch(toggleSidePanel(collapsedState))
+ }
+ }
+};
export const MainPanel = connect(mapStateToProps, mapDispatchToProps)(MainPanelRoot);
isUserActive: boolean;
isNotLinking: boolean;
sessionIdleTimeout: number;
+ sidePanelIsCollapsed: boolean;
}
type WorkbenchPanelProps = WithStyles<CssRules> & WorkbenchDataProps;
routes = React.createElement(React.Fragment, null, pluginConfig.centerPanelList.reduce(reduceRoutesFn, React.Children.toArray(routes.props.children)));
+const applyCollapsedState = (isCollapsed) => {
+ const rightPanel: Element = document.getElementsByClassName('layout-pane')[1]
+ if(rightPanel) {
+ rightPanel.setAttribute('style', `width: ${isCollapsed ? 100 : getSplitterInitialSize()}%`)
+ }
+}
+
export const WorkbenchPanel =
- withStyles(styles)((props: WorkbenchPanelProps) =>
- <Grid container item xs className={props.classes.root}>
+ withStyles(styles)((props: WorkbenchPanelProps) =>{
+ applyCollapsedState(props.sidePanelIsCollapsed)
+ return <Grid container item xs className={props.classes.root}>
{props.sessionIdleTimeout > 0 && <AutoLogout />}
<Grid container item xs className={props.classes.container}>
<SplitterLayout customClassName={props.classes.splitter} percentage={true}
<FedLogin />
<WebDavS3InfoDialog />
{React.createElement(React.Fragment, null, pluginConfig.dialogs)}
- </Grid>
+ </Grid>}
);