1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import React from 'react';
6 import { connect, DispatchProp } from 'react-redux';
7 import { memoize } from 'lodash/fp';
8 import { Field } from 'redux-form';
9 import { CustomStyleRulesCallback } from 'common/custom-theme';
10 import { Input, Dialog, DialogTitle, DialogContent, DialogActions, Button } from '@mui/material';
11 import { WithStyles } from '@mui/styles';
12 import withStyles from '@mui/styles/withStyles';
15 DirectoryCommandInputParameter,
18 } from 'models/workflow';
19 import { GenericInputProps, GenericInput } from './generic-input';
20 import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
21 import { FileOperationLocation, getFileOperationLocation, initProjectsTreePicker } from 'store/tree-picker/tree-picker-actions';
22 import { TreeItem } from 'components/tree/tree';
23 import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
24 import { ERROR_MESSAGE } from 'validators/require';
25 import { Dispatch } from 'redux';
27 export interface DirectoryInputProps {
28 input: DirectoryCommandInputParameter;
29 options?: { showOnlyOwned: boolean, showOnlyWritable: boolean };
32 type DialogContentCssRules = 'root' | 'pickerWrapper';
34 export const DirectoryInput = ({ input, options }: DirectoryInputProps) =>
38 component={DirectoryInputComponent as any}
44 validate={getValidation(input)} />;
46 const format = (value?: Directory) => value ? value.basename : '';
48 const parse = (directory: FileOperationLocation): Directory => ({
49 class: CWLType.DIRECTORY,
50 location: `keep:${directory.pdh}${directory.subpath}`,
51 basename: directory.name,
54 const getValidation = memoize(
55 (input: DirectoryCommandInputParameter) => ([
56 isRequiredInput(input)
57 ? (directory?: Directory) => directory ? undefined : ERROR_MESSAGE
62 interface DirectoryInputComponentState {
64 directory?: FileOperationLocation;
67 interface DirectoryInputActionProps {
68 initProjectsTreePicker: (pickerId: string) => void;
69 getFileOperationLocation: (item: ProjectsTreePickerItem) => Promise<FileOperationLocation | undefined>;
72 const mapDispatchToProps = (dispatch: Dispatch): DirectoryInputActionProps => ({
73 initProjectsTreePicker: (pickerId: string) => dispatch<any>(initProjectsTreePicker(pickerId)),
74 getFileOperationLocation: (item: ProjectsTreePickerItem) => dispatch<any>(getFileOperationLocation(item)),
77 const DirectoryInputComponent = connect(null, mapDispatchToProps)(
78 class FileInputComponent extends React.Component<GenericInputProps & DirectoryInputActionProps & DispatchProp & {
79 options?: { showOnlyOwned: boolean, showOnlyWritable: boolean };
80 }, DirectoryInputComponentState> {
81 state: DirectoryInputComponentState = {
86 this.props.initProjectsTreePicker(this.props.commandInput.id);
97 this.setState({ open: true });
100 closeDialog = () => {
101 this.setState({ open: false });
106 this.props.input.onChange(this.state.directory);
109 setDirectory = async (_: {}, { data: item }: TreeItem<ProjectsTreePickerItem>) => {
110 const location = await this.props.getFileOperationLocation(item);
111 this.setState({ directory: location });
120 value={props.input.value}
121 error={props.meta.touched && !!props.meta.error}
122 disabled={props.commandInput.disabled}
123 onClick={!this.props.commandInput.disabled ? this.openDialog : undefined}
124 onKeyPress={!this.props.commandInput.disabled ? this.openDialog : undefined} />}
128 dialogContentStyles: CustomStyleRulesCallback<DialogContentCssRules> = ({ spacing }) => ({
131 flexDirection: 'column',
136 flexDirection: 'column',
141 dialog = withStyles(this.dialogContentStyles)(
142 ({ classes }: WithStyles<DialogContentCssRules>) =>
144 open={this.state.open}
145 onClose={this.closeDialog}
147 data-cy="choose-a-directory-dialog"
149 <DialogTitle>Choose a directory</DialogTitle>
150 <DialogContent className={classes.root}>
151 <div className={classes.pickerWrapper}>
153 pickerId={this.props.commandInput.id}
156 cascadeSelection={false}
157 options={this.props.options}
158 toggleItemActive={this.setDirectory} />
162 <Button onClick={this.closeDialog}>Cancel</Button>
164 disabled={!this.state.directory}
167 onClick={this.submit}>Ok</Button>