Project form, raw label form

main
Boris Sekachev 5 years ago
parent 87f360aa13
commit ae14894796

@ -18,7 +18,7 @@ $completed-progress-color: #61c200;
$inprogress-progress-color: #1890ff; $inprogress-progress-color: #1890ff;
$pending-progress-color: #c1c1c1; $pending-progress-color: #c1c1c1;
$border-color-1: #c3c3c3; $border-color-1: #c3c3c3;
$border-color-2: #d9d9d9; $border-color-2: rgb(240, 240, 240);
$border-color-3: #242424; $border-color-3: #242424;
$border-color-hover: #40a9ff; $border-color-hover: #40a9ff;
$background-color-1: white; $background-color-1: white;

@ -3,13 +3,13 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React, { import React, {
useState, useRef, useEffect, Component, useState, useRef, useEffect, RefObject,
} from 'react'; } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import { Col, Row } from 'antd/lib/grid'; import { Col, Row } from 'antd/lib/grid';
import Text from 'antd/lib/typography/Text'; import Text from 'antd/lib/typography/Text';
import Form, { FormComponentProps, WrappedFormUtils } from '@ant-design/compatible/lib/form/Form'; import Form, { FormInstance } from 'antd/lib/form';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import notification from 'antd/lib/notification'; import notification from 'antd/lib/notification';
@ -19,66 +19,57 @@ import { CombinedState } from 'reducers/interfaces';
import LabelsEditor from 'components/labels-editor/labels-editor'; import LabelsEditor from 'components/labels-editor/labels-editor';
import { createProjectAsync } from 'actions/projects-actions'; import { createProjectAsync } from 'actions/projects-actions';
type FormRefType = Component<FormComponentProps<any>, any, any> & WrappedFormUtils; function NameConfigurationForm({ formRef }: { formRef: RefObject<FormInstance> }): JSX.Element {
return (
const ProjectNameEditor = Form.create<FormComponentProps>()( <Form layout='vertical' ref={formRef}>
(props: FormComponentProps): JSX.Element => { <Form.Item
const { form } = props; name='name'
const { getFieldDecorator } = form; hasFeedback
label='Name'
return ( rules={[
<Form onSubmit={(e): void => e.preventDefault()}> {
<Form.Item hasFeedback label={<span>Name</span>}> required: true,
{getFieldDecorator('name', { message: 'Please, specify a name',
rules: [ },
{ ]}
required: true, >
message: 'Please, specify a name', <Input />
}, </Form.Item>
], </Form>
})(<Input />)} );
</Form.Item> }
</Form>
); function AdvanvedConfigurationForm({ formRef }: { formRef: RefObject<FormInstance> }): JSX.Element {
}, return (
); <Form layout='vertical' ref={formRef}>
<Form.Item
const AdvanvedConfigurationForm = Form.create<FormComponentProps>()( name='bug_tracker'
(props: FormComponentProps): JSX.Element => { label='Issue tracker'
const { form } = props; extra='Attach issue tracker where the project is described'
const { getFieldDecorator } = form; hasFeedback
rules={[
return ( {
<Form onSubmit={(e): void => e.preventDefault()}> validator: (_, value, callback): void => {
<Form.Item if (value && !patterns.validateURL.pattern.test(value)) {
label={<span>Issue tracker</span>} callback('Issue tracker must be URL');
extra='Attach issue tracker where the project is described' } else {
hasFeedback callback();
> }
{getFieldDecorator('bug_tracker', { },
rules: [ },
{ ]}
validator: (_, value, callback): void => { >
if (value && !patterns.validateURL.pattern.test(value)) { <Input />
callback('Issue tracker must be URL'); </Form.Item>
} else { </Form>
callback(); );
} }
},
},
],
})(<Input />)}
</Form.Item>
</Form>
);
},
);
export default function CreateProjectContent(): JSX.Element { export default function CreateProjectContent(): JSX.Element {
const [projectLabels, setProjectLabels] = useState<any[]>([]); const [projectLabels, setProjectLabels] = useState<any[]>([]);
const shouldShowNotification = useRef(false); const shouldShowNotification = useRef(false);
const nameFormRef = useRef<FormRefType>(null); const nameFormRef = useRef<FormInstance>(null);
const advancedFormRef = useRef<FormRefType>(null); const advancedFormRef = useRef<FormInstance>(null);
const dispatch = useDispatch(); const dispatch = useDispatch();
const history = useHistory(); const history = useHistory();
@ -102,28 +93,19 @@ export default function CreateProjectContent(): JSX.Element {
shouldShowNotification.current = true; shouldShowNotification.current = true;
}, [newProjectId]); }, [newProjectId]);
const onSumbit = (): void => { const onSumbit = async (): Promise<void> => {
interface Project { interface Project {
[key: string]: any; [key: string]: any;
} }
const projectData: Project = {}; const projectData: Project = {};
if (nameFormRef.current !== null) { if (nameFormRef.current && advancedFormRef.current) {
nameFormRef.current.validateFields((error, value) => { const basicValues = await nameFormRef.current.validateFields();
if (!error) { const advancedValues = await advancedFormRef.current.validateFields();
projectData.name = value.name; projectData.name = basicValues.name;
} for (const [field, value] of Object.entries(advancedValues)) {
}); projectData[field] = value;
} }
if (advancedFormRef.current !== null) {
advancedFormRef.current.validateFields((error, values) => {
if (!error) {
for (const [field, value] of Object.entries(values)) {
projectData[field] = value;
}
}
});
} }
projectData.labels = projectLabels; projectData.labels = projectLabels;
@ -136,7 +118,7 @@ export default function CreateProjectContent(): JSX.Element {
return ( return (
<Row type='flex' justify='start' align='middle' className='cvat-create-project-content'> <Row type='flex' justify='start' align='middle' className='cvat-create-project-content'>
<Col span={24}> <Col span={24}>
<ProjectNameEditor ref={nameFormRef} /> <NameConfigurationForm formRef={nameFormRef} />
</Col> </Col>
<Col span={24}> <Col span={24}>
<Text className='cvat-text-color'>Labels:</Text> <Text className='cvat-text-color'>Labels:</Text>
@ -148,7 +130,7 @@ export default function CreateProjectContent(): JSX.Element {
/> />
</Col> </Col>
<Col span={24}> <Col span={24}>
<AdvanvedConfigurationForm ref={advancedFormRef} /> <AdvanvedConfigurationForm formRef={advancedFormRef} />
</Col> </Col>
<Col span={24}> <Col span={24}>
<Button type='primary' onClick={onSumbit}> <Button type='primary' onClick={onSumbit}>

@ -2,62 +2,64 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import React from 'react'; import React, { RefObject } from 'react';
import { Row, Col } from 'antd/lib/grid'; import { Row, Col } from 'antd/lib/grid';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
import Button from 'antd/lib/button'; import Button from 'antd/lib/button';
import Tooltip from 'antd/lib/tooltip'; import Tooltip from 'antd/lib/tooltip';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form'; import Form, { FormInstance, RuleObject } from 'antd/lib/form';
import { Store } from 'antd/lib/form/interface';
import { Label, Attribute, validateParsedLabel, idGenerator } from './common'; import { Label, Attribute, validateParsedLabel, idGenerator } from './common';
type Props = FormComponentProps & { function validateLabels(_: RuleObject, value: string): Promise<void> {
labels: Label[]; try {
onSubmit: (labels: Label[]) => void; const parsed = JSON.parse(value);
}; if (!Array.isArray(parsed)) {
return Promise.reject(new Error('Field is expected to be a JSON array'));
class RawViewer extends React.PureComponent<Props> { }
private validateLabels = (_: any, value: string, callback: any): void => { const labelNames = parsed.map((label: Label) => label.name);
try { if (new Set(labelNames).size !== labelNames.length) {
const parsed = JSON.parse(value); return Promise.reject(new Error('Label names must be unique for the task'));
if (!Array.isArray(parsed)) { }
callback('Field is expected to be a JSON array');
}
const labelNames = parsed.map((label: Label) => label.name);
if (new Set(labelNames).size !== labelNames.length) {
callback('Label names must be unique for the task');
}
for (const label of parsed) { for (const label of parsed) {
try { try {
validateParsedLabel(label); validateParsedLabel(label);
} catch (error) { } catch (error) {
callback(error.toString()); return Promise.reject(error);
}
} }
} catch (error) {
callback(error.toString());
} }
} catch (error) {
return Promise.reject(error);
}
callback(); return Promise.resolve();
}; }
private handleSubmit = (e: React.FormEvent): void => { interface Props {
const { form, onSubmit } = this.props; labels: Label[];
onSubmit: (labels: Label[]) => void;
}
e.preventDefault(); export default class RawViewer extends React.PureComponent<Props> {
form.validateFields((error, values): void => { private formRef: RefObject<FormInstance>;
if (!error) {
const parsed = JSON.parse(values.labels); public constructor(props: Props) {
for (const label of parsed) { super(props);
label.id = label.id || idGenerator(); this.formRef = React.createRef<FormInstance>();
for (const attr of label.attributes) { }
attr.id = attr.id || idGenerator();
} private handleSubmit = (values: Store): void => {
} const { onSubmit } = this.props;
onSubmit(parsed); const parsed = JSON.parse(values.labels);
for (const label of parsed) {
label.id = label.id || idGenerator();
for (const attr of label.attributes) {
attr.id = attr.id || idGenerator();
} }
}); }
onSubmit(parsed);
}; };
public render(): JSX.Element { public render(): JSX.Element {
@ -76,19 +78,14 @@ class RawViewer extends React.PureComponent<Props> {
); );
const textLabels = JSON.stringify(convertedLabels, null, 2); const textLabels = JSON.stringify(convertedLabels, null, 2);
const { form } = this.props;
return ( return (
<Form onSubmit={this.handleSubmit}> <Form layout='vertical' onFinish={this.handleSubmit} ref={this.formRef}>
<Form.Item> <Form.Item
{form.getFieldDecorator('labels', { name='labels'
initialValue: textLabels, initialValue={textLabels}
rules: [ rules={[{ validator: validateLabels }]}
{ >
validator: this.validateLabels, <Input.TextArea rows={5} className='cvat-raw-labels-viewer' />
},
],
})(<Input.TextArea rows={5} className='cvat-raw-labels-viewer' />)}
</Form.Item> </Form.Item>
<Row type='flex' justify='start' align='middle'> <Row type='flex' justify='start' align='middle'>
<Col> <Col>
@ -104,7 +101,9 @@ class RawViewer extends React.PureComponent<Props> {
danger danger
style={{ width: '150px' }} style={{ width: '150px' }}
onClick={(): void => { onClick={(): void => {
form.resetFields(); if (this.formRef.current) {
this.formRef.current.resetFields();
}
}} }}
> >
Reset Reset
@ -116,5 +115,3 @@ class RawViewer extends React.PureComponent<Props> {
); );
} }
} }
export default Form.create<Props>()(RawViewer);

Loading…
Cancel
Save