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 { Input, Dialog, DialogTitle, DialogContent, DialogActions, Button, StyleRulesCallback, withStyles, WithStyles } from '@material-ui/core';
12 DirectoryCommandInputParameter,
15 } from 'models/workflow';
16 import { GenericInputProps, GenericInput } from './generic-input';
17 import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
18 import { FileOperationLocation, getFileOperationLocation, initProjectsTreePicker } from 'store/tree-picker/tree-picker-actions';
19 import { TreeItem } from 'components/tree/tree';
20 import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
21 import { ERROR_MESSAGE } from 'validators/require';
22 import { Dispatch } from 'redux';
24 export interface DirectoryInputProps {
25 input: DirectoryCommandInputParameter;
26 options?: { showOnlyOwned: boolean, showOnlyWritable: boolean };
29 type DialogContentCssRules = 'root' | 'pickerWrapper';
31 export const DirectoryInput = ({ input, options }: DirectoryInputProps) =>
35 component={DirectoryInputComponent as any}
41 validate={getValidation(input)} />;
43 const format = (value?: Directory) => value ? value.basename : '';
45 const parse = (directory: FileOperationLocation): Directory => ({
46 class: CWLType.DIRECTORY,
47 location: `keep:${directory.pdh}${directory.subpath}`,
48 basename: directory.name,
51 const getValidation = memoize(
52 (input: DirectoryCommandInputParameter) => ([
53 isRequiredInput(input)
54 ? (directory?: Directory) => directory ? undefined : ERROR_MESSAGE
59 interface DirectoryInputComponentState {
61 directory?: FileOperationLocation;
64 interface DirectoryInputActionProps {
65 initProjectsTreePicker: (pickerId: string) => void;
66 getFileOperationLocation: (item: ProjectsTreePickerItem) => Promise<FileOperationLocation | undefined>;
69 const mapDispatchToProps = (dispatch: Dispatch): DirectoryInputActionProps => ({
70 initProjectsTreePicker: (pickerId: string) => dispatch<any>(initProjectsTreePicker(pickerId)),
71 getFileOperationLocation: (item: ProjectsTreePickerItem) => dispatch<any>(getFileOperationLocation(item)),
74 const DirectoryInputComponent = connect(null, mapDispatchToProps)(
75 class FileInputComponent extends React.Component<GenericInputProps & DirectoryInputActionProps & DispatchProp & {
76 options?: { showOnlyOwned: boolean, showOnlyWritable: boolean };
77 }, DirectoryInputComponentState> {
78 state: DirectoryInputComponentState = {
83 this.props.initProjectsTreePicker(this.props.commandInput.id);
94 this.setState({ open: true });
98 this.setState({ open: false });
103 this.props.input.onChange(this.state.directory);
106 setDirectory = async (_: {}, { data: item }: TreeItem<ProjectsTreePickerItem>) => {
107 const location = await this.props.getFileOperationLocation(item);
108 this.setState({ directory: location });
117 value={props.input.value}
118 error={props.meta.touched && !!props.meta.error}
119 disabled={props.commandInput.disabled}
120 onClick={!this.props.commandInput.disabled ? this.openDialog : undefined}
121 onKeyPress={!this.props.commandInput.disabled ? this.openDialog : undefined} />}
125 dialogContentStyles: StyleRulesCallback<DialogContentCssRules> = ({ spacing }) => ({
128 flexDirection: 'column',
131 flexBasis: `${spacing.unit * 8}vh`,
137 dialog = withStyles(this.dialogContentStyles)(
138 ({ classes }: WithStyles<DialogContentCssRules>) =>
140 open={this.state.open}
141 onClose={this.closeDialog}
143 data-cy="choose-a-directory-dialog"
145 <DialogTitle>Choose a directory</DialogTitle>
146 <DialogContent className={classes.root}>
147 <div className={classes.pickerWrapper}>
149 pickerId={this.props.commandInput.id}
152 cascadeSelection={false}
153 options={this.props.options}
154 toggleItemActive={this.setDirectory} />
158 <Button onClick={this.closeDialog}>Cancel</Button>
160 disabled={!this.state.directory}
163 onClick={this.submit}>Ok</Button>