import { ArvadosTheme } from "common/custom-theme";
import { createTree } from 'models/tree';
import { DataTableFilters } from 'components/data-table-filters/data-table-filters-tree';
-import { CloseIcon, IconType, MaximizeIcon, MoreOptionsIcon } from 'components/icon/icon';
+import {
+ CloseIcon,
+ IconType,
+ MaximizeIcon,
+ UnMaximizeIcon,
+ MoreOptionsIcon
+} from 'components/icon/icon';
import { PaperProps } from '@material-ui/core/Paper';
import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
searchBox: {
- paddingBottom: theme.spacing.unit * 2
+ paddingBottom: 0,
},
toolbar: {
- paddingTop: theme.spacing.unit,
- paddingRight: theme.spacing.unit * 2,
+ paddingTop: 0,
+ paddingRight: theme.spacing.unit,
},
footer: {
overflow: 'auto'
},
title: {
display: 'inline-block',
- paddingLeft: theme.spacing.unit * 3,
- paddingTop: theme.spacing.unit * 3,
+ paddingLeft: theme.spacing.unit * 2,
+ paddingTop: theme.spacing.unit * 2,
fontSize: '18px'
},
dataTable: {
},
headerMenu: {
float: 'right',
- display: 'inline-block'
+ display: 'inline-block',
}
});
items, itemsAvailable, onRowClick, onRowDoubleClick, classes,
defaultViewIcon, defaultViewMessages, hideColumnSelector, actions, paperProps, hideSearchInput,
paperKey, fetchMode, currentItemUuid, title,
- doHidePanel, doMaximizePanel, panelName, panelMaximized, elementPath
+ doHidePanel, doMaximizePanel, doUnMaximizePanel, panelName, panelMaximized, elementPath
} = this.props;
return <Paper className={classes.root} {...paperProps} key={paperKey} data-cy={this.props["data-cy"]}>
(!hideColumnSelector || !hideSearchInput || !!actions) &&
<Grid className={classes.headerMenu} item xs>
<Toolbar className={classes.toolbar}>
- <Grid container justify="space-between" wrap="nowrap" alignItems="center">
- {!hideSearchInput && <div className={classes.searchBox}>
- {!hideSearchInput && <SearchInput
- label={searchLabel}
- value={searchValue}
- selfClearProp={currentItemUuid}
- onSearch={onSearch} />}
- </div>}
- {actions}
- {!hideColumnSelector && <ColumnSelector
- columns={columns}
- onColumnToggle={onColumnToggle} />}
- </Grid>
+ {!hideSearchInput && <div className={classes.searchBox}>
+ {!hideSearchInput && <SearchInput
+ label={searchLabel}
+ value={searchValue}
+ selfClearProp={currentItemUuid}
+ onSearch={onSearch} />}
+ </div>}
+ {actions}
+ {!hideColumnSelector && <ColumnSelector
+ columns={columns}
+ onColumnToggle={onColumnToggle} />}
+ { doUnMaximizePanel && panelMaximized &&
+ <Tooltip title={`Unmaximize ${panelName || 'panel'}`} disableFocusListener>
+ <IconButton onClick={doUnMaximizePanel}><UnMaximizeIcon /></IconButton>
+ </Tooltip> }
{ doMaximizePanel && !panelMaximized &&
<Tooltip title={`Maximize ${panelName || 'panel'}`} disableFocusListener>
<IconButton onClick={doMaximizePanel}><MaximizeIcon /></IconButton>
</Tooltip> }
{ doHidePanel &&
<Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
- <IconButton onClick={doHidePanel}><CloseIcon /></IconButton>
+ <IconButton disabled={panelMaximized} onClick={doHidePanel}><CloseIcon /></IconButton>
</Tooltip> }
</Toolbar>
</Grid>
import WrapText from '@material-ui/icons/WrapText';
import TextIncrease from '@material-ui/icons/ZoomIn';
import TextDecrease from '@material-ui/icons/ZoomOut';
-import CropFreeSharp from '@material-ui/icons/CropFreeSharp';
+import FullscreenSharp from '@material-ui/icons/FullscreenSharp';
+import FullscreenExitSharp from '@material-ui/icons/FullscreenExitSharp';
import ExitToApp from '@material-ui/icons/ExitToApp';
import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
import RemoveCircleOutline from '@material-ui/icons/RemoveCircleOutline';
export const KeyIcon: IconType = (props) => <VpnKey {...props} />;
export const LogIcon: IconType = (props) => <SettingsEthernet {...props} />;
export const MailIcon: IconType = (props) => <Mail {...props} />;
-export const MaximizeIcon: IconType = (props) => <CropFreeSharp {...props} />;
+export const MaximizeIcon: IconType = (props) => <FullscreenSharp {...props} />;
+export const UnMaximizeIcon: IconType = (props) => <FullscreenExitSharp {...props} />;
export const MoreOptionsIcon: IconType = (props) => <MoreVert {...props} />;
export const MoveToIcon: IconType = (props) => <Input {...props} />;
export const NewProjectIcon: IconType = (props) => <CreateNewFolder {...props} />;
configure({ adapter: new Adapter() });
-const PanelMock = ({panelName, panelMaximized, doHidePanel, doMaximizePanel, panelIlluminated, panelRef, children, ...rest}) =>
+const PanelMock = ({panelName, panelMaximized, doHidePanel, doMaximizePanel, doUnMaximizePanel, panelIlluminated, panelRef, children, ...rest}) =>
<div {...rest}>{children}</div>;
describe('<MPVContainer />', () => {
interface MPVHideablePanelActionProps {
doHidePanel: () => void;
doMaximizePanel: () => void;
+ doUnMaximizePanel: () => void;
}
type MPVHideablePanelProps = MPVHideablePanelDataProps & MPVHideablePanelActionProps;
-const MPVHideablePanel = ({doHidePanel, doMaximizePanel, name, visible, maximized, illuminated, ...props}: MPVHideablePanelProps) =>
+const MPVHideablePanel = ({doHidePanel, doMaximizePanel, doUnMaximizePanel, name, visible, maximized, illuminated, ...props}: MPVHideablePanelProps) =>
visible
? <>
- {React.cloneElement((props.children as ReactElement), { doHidePanel, doMaximizePanel, panelName: name, panelMaximized: maximized, panelIlluminated: illuminated, panelRef: props.panelRef })}
+ {React.cloneElement((props.children as ReactElement), { doHidePanel, doMaximizePanel, doUnMaximizePanel, panelName: name, panelMaximized: maximized, panelIlluminated: illuminated, panelRef: props.panelRef })}
</>
: null;
interface MPVPanelActionProps {
doHidePanel?: () => void;
doMaximizePanel?: () => void;
+ doUnMaximizePanel?: () => void;
}
// Props received by panel implementors
type MPVPanelContentProps = {children: ReactElement} & MPVPanelProps & GridProps;
// Grid item compatible component for layout and MPV props passing
-export const MPVPanelContent = ({doHidePanel, doMaximizePanel, panelName,
+export const MPVPanelContent = ({doHidePanel, doMaximizePanel, doUnMaximizePanel, panelName,
panelMaximized, panelIlluminated, panelRef, forwardProps, maxHeight,
...props}: MPVPanelContentProps) => {
useEffect(() => {
if (panelRef && panelRef.current) {
- panelRef.current.scrollIntoView({behavior: 'smooth'});
+ panelRef.current.scrollIntoView({alignToTop: true});
}
}, [panelRef]);
<span ref={panelRef} /> {/* Element to scroll to when the panel is selected */}
<Paper style={{height: '100%'}} elevation={panelIlluminated ? 8 : 0}>
{ forwardProps
- ? React.cloneElement(props.children, { doHidePanel, doMaximizePanel, panelName, panelMaximized })
+ ? React.cloneElement(props.children, { doHidePanel, doMaximizePanel, doUnMaximizePanel, panelName, panelMaximized })
: props.children }
</Paper>
</Grid>;
} else if (!isArray(children)) {
children = [children];
}
- const visibility = (children as ReactNodeArray).map((_, idx) =>
+ const initialVisibility = (children as ReactNodeArray).map((_, idx) =>
!panelStates || // if panelStates wasn't passed, default to all visible panels
(panelStates[idx] &&
(panelStates[idx].visible || panelStates[idx].visible === undefined)));
- const [panelVisibility, setPanelVisibility] = useState<boolean[]>(visibility);
+ const [panelVisibility, setPanelVisibility] = useState<boolean[]>(initialVisibility);
+ const [previousPanelVisibility, setPreviousPanelVisibility] = useState<boolean[]>(initialVisibility);
const [highlightedPanel, setHighlightedPanel] = useState<number>(-1);
const [selectedPanel, setSelectedPanel] = useState<number>(-1);
const panelRef = useRef<any>(null);
if (isArray(children)) {
for (let idx = 0; idx < children.length; idx++) {
const showFn = (idx: number) => () => {
+ setPreviousPanelVisibility(initialVisibility);
setPanelVisibility([
...panelVisibility.slice(0, idx),
true,
setSelectedPanel(idx);
};
const hideFn = (idx: number) => () => {
+ setPreviousPanelVisibility(initialVisibility);
setPanelVisibility([
...panelVisibility.slice(0, idx),
false,
])
};
const maximizeFn = (idx: number) => () => {
+ setPreviousPanelVisibility(panelVisibility);
// Maximize X == hide all but X
setPanelVisibility([
...panelVisibility.slice(0, idx).map(() => false),
true,
...panelVisibility.slice(idx+1).map(() => false),
- ])
+ ]);
};
+ const unMaximizeFn = (idx: number) => () => {
+ setPanelVisibility(previousPanelVisibility);
+ setSelectedPanel(idx);
+ }
const panelName = panelStates === undefined
? `Panel ${idx+1}`
: (panelStates[idx] && panelStates[idx].name) || `Panel ${idx+1}`;
<MPVHideablePanel key={idx} visible={panelVisibility[idx]} name={panelName}
panelRef={(idx === selectedPanel) ? panelRef : undefined}
maximized={panelIsMaximized} illuminated={idx === highlightedPanel}
- doHidePanel={hideFn(idx)} doMaximizePanel={maximizeFn(idx)}>
+ doHidePanel={hideFn(idx)} doMaximizePanel={maximizeFn(idx)} doUnMaximizePanel={panelIsMaximized ? unMaximizeFn(idx) : () => null}>
{children[idx]}
</MPVHideablePanel>;
panels = [...panels, aPanel];
// SPDX-License-Identifier: AGPL-3.0
import React, {useState, useEffect} from 'react';
-import { IconButton, StyleRulesCallback, withStyles, WithStyles, FormControl, InputLabel, Input, InputAdornment, Tooltip } from '@material-ui/core';
+import {
+ IconButton,
+ FormControl,
+ InputLabel,
+ Input,
+ InputAdornment,
+ Tooltip,
+} from '@material-ui/core';
import SearchIcon from '@material-ui/icons/Search';
-type CssRules = 'container' | 'input' | 'button';
-
-const styles: StyleRulesCallback<CssRules> = theme => {
- return {
- container: {
- position: 'relative',
- width: '100%'
- },
- input: {
- border: 'none',
- borderRadius: theme.spacing.unit / 4,
- boxSizing: 'border-box',
- padding: theme.spacing.unit,
- paddingRight: theme.spacing.unit * 4,
- width: '100%',
- },
- button: {
- position: 'absolute',
- top: theme.spacing.unit / 2,
- right: theme.spacing.unit / 2,
- width: theme.spacing.unit * 3,
- height: theme.spacing.unit * 3
- }
- };
-};
-
interface SearchInputDataProps {
value: string;
label?: string;
debounce?: number;
}
-type SearchInputProps = SearchInputDataProps & SearchInputActionProps & WithStyles<CssRules>;
+type SearchInputProps = SearchInputDataProps & SearchInputActionProps;
export const DEFAULT_SEARCH_DEBOUNCE = 1000;
-const SearchInputComponent = (props: SearchInputProps) => {
+export const SearchInput = (props: SearchInputProps) => {
const [timeout, setTimeout] = useState(0);
const [value, setValue] = useState("");
const [label, setLabel] = useState("Search");
} />
</FormControl>
</form>;
-}
-
-export const SearchInput = withStyles(styles)(SearchInputComponent);
\ No newline at end of file
+};
CircularProgress,
} from '@material-ui/core';
import { ArvadosTheme } from 'common/custom-theme';
-import { CloseIcon, ImageIcon, InputIcon, ImageOffIcon, OutputIcon, MaximizeIcon } from 'components/icon/icon';
+import {
+ CloseIcon,
+ ImageIcon,
+ InputIcon,
+ ImageOffIcon,
+ OutputIcon,
+ MaximizeIcon,
+ UnMaximizeIcon
+} from 'components/icon/icon';
import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
import {
BooleanCommandInputParameter,
type ProcessIOCardProps = ProcessIOCardDataProps & ProcessIOCardActionProps & WithStyles<CssRules> & MPVPanelProps;
export const ProcessIOCard = withStyles(styles)(connect(null, mapDispatchToProps)(
- ({ classes, label, params, raw, mounts, outputUuid, doHidePanel, doMaximizePanel, panelMaximized, panelName, process, navigateTo }: ProcessIOCardProps) => {
+ ({ classes, label, params, raw, mounts, outputUuid, doHidePanel, doMaximizePanel, doUnMaximizePanel, panelMaximized, panelName, process, navigateTo }: ProcessIOCardProps) => {
const [mainProcTabState, setMainProcTabState] = useState(0);
const handleMainProcTabChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
setMainProcTabState(value);
{ mainProcess && <Tooltip title={"Toggle Image Preview"} disableFocusListener>
<IconButton data-cy="io-preview-image-toggle" onClick={() =>{setShowImagePreview(!showImagePreview)}}>{showImagePreview ? <ImageIcon /> : <ImageOffIcon />}</IconButton>
</Tooltip> }
+ { doUnMaximizePanel && panelMaximized &&
+ <Tooltip title={`Unmaximize ${panelName || 'panel'}`} disableFocusListener>
+ <IconButton onClick={doUnMaximizePanel}><UnMaximizeIcon /></IconButton>
+ </Tooltip> }
{ doMaximizePanel && !panelMaximized &&
<Tooltip title={`Maximize ${panelName || 'panel'}`} disableFocusListener>
<IconButton onClick={doMaximizePanel}><MaximizeIcon /></IconButton>
</Tooltip> }
{ doHidePanel &&
<Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
- <IconButton onClick={doHidePanel}><CloseIcon /></IconButton>
+ <IconButton disabled={panelMaximized} onClick={doHidePanel}><CloseIcon /></IconButton>
</Tooltip> }
</div>
} />
CopyIcon,
LogIcon,
MaximizeIcon,
+ UnMaximizeIcon,
TextDecreaseIcon,
TextIncreaseIcon,
WordWrapOffIcon,
export const ProcessLogsCard = withStyles(styles)(
({ classes, process, filters, selectedFilter, lines,
onLogFilterChange, navigateToLog, onCopy,
- doHidePanel, doMaximizePanel, panelMaximized, panelName }: ProcessLogsCardProps) => {
+ doHidePanel, doMaximizePanel, doUnMaximizePanel, panelMaximized, panelName }: ProcessLogsCardProps) => {
const [wordWrap, setWordWrap] = useState<boolean>(true);
const [fontSize, setFontSize] = useState<number>(3);
const fontBaseSize = 10;
</IconButton>
</Tooltip>
</Grid>
+ { doUnMaximizePanel && panelMaximized &&
+ <Tooltip title={`Unmaximize ${panelName || 'panel'}`} disableFocusListener>
+ <IconButton onClick={doUnMaximizePanel}><UnMaximizeIcon /></IconButton>
+ </Tooltip> }
{ doMaximizePanel && !panelMaximized &&
<Tooltip title={`Maximize ${panelName || 'panel'}`} disableFocusListener>
<IconButton onClick={doMaximizePanel}><MaximizeIcon /></IconButton>
</Tooltip> }
- { doHidePanel && <Grid item>
- <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
- <IconButton onClick={doHidePanel}><CloseIcon /></IconButton>
- </Tooltip>
- </Grid> }
+ { doHidePanel &&
+ <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
+ <IconButton disabled={panelMaximized} onClick={doHidePanel}><CloseIcon /></IconButton>
+ </Tooltip> }
</Grid>}
title={
<Typography noWrap variant='h6' className={classes.title}>
import { getInitialProcessStatusFilters } from 'store/resource-type-filters/resource-type-filters';
import { ResourcesState } from 'store/resources/resources';
import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
+import { StyleRulesCallback, Typography, WithStyles, withStyles } from '@material-ui/core';
+import { ArvadosTheme } from 'common/custom-theme';
+
+type CssRules = 'iconHeader' | 'cardHeader';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ iconHeader: {
+ fontSize: '1.875rem',
+ color: theme.customs.colors.green700,
+ marginRight: theme.spacing.unit * 2,
+ },
+ cardHeader: {
+ display: 'flex',
+ },
+});
export enum SubprocessPanelColumnNames {
NAME = "Name",
'The current process may not have any or none matches current filtering.'
];
+type SubProcessesTitleProps = WithStyles<CssRules>;
+
+const SubProcessesTitle = withStyles(styles)(
+ ({classes}: SubProcessesTitleProps) =>
+ <div className={classes.cardHeader}>
+ <ProcessIcon className={classes.iconHeader} /><span></span>
+ <Typography noWrap variant='h6' color='inherit'>
+ Subprocesses
+ </Typography>
+ </div>
+);
+
export const SubprocessPanelRoot = (props: SubprocessPanelProps & MPVPanelProps) => {
return <DataExplorer
id={SUBPROCESS_PANEL_ID}
defaultViewMessages={DEFAULT_VIEW_MESSAGES}
doHidePanel={props.doHidePanel}
doMaximizePanel={props.doMaximizePanel}
+ doUnMaximizePanel={props.doUnMaximizePanel}
panelMaximized={props.panelMaximized}
- panelName={props.panelName} />;
+ panelName={props.panelName}
+ title={<SubProcessesTitle/>} />;
};