Label form

main
Boris Sekachev 5 years ago
parent ae14894796
commit a60fd76f0d

@ -2,7 +2,7 @@
// //
// 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 Icon, { CloseCircleOutlined, PlusOutlined } from '@ant-design/icons'; import Icon, { CloseCircleOutlined, PlusOutlined } from '@ant-design/icons';
import Input from 'antd/lib/input'; import Input from 'antd/lib/input';
@ -10,11 +10,11 @@ import Button from 'antd/lib/button';
import Checkbox from 'antd/lib/checkbox'; import Checkbox from 'antd/lib/checkbox';
import Tooltip from 'antd/lib/tooltip'; import Tooltip from 'antd/lib/tooltip';
import Select from 'antd/lib/select'; import Select from 'antd/lib/select';
import Form, { FormComponentProps } from '@ant-design/compatible/lib/form/Form'; import Form, { FormInstance } from 'antd/lib/form';
import Text from 'antd/lib/typography/Text';
import Badge from 'antd/lib/badge'; import Badge from 'antd/lib/badge';
import ColorPicker from 'components/annotation-page/standard-workspace/objects-side-bar/color-picker'; import { Store } from 'antd/lib/form/interface';
import ColorPicker from 'components/annotation-page/standard-workspace/objects-side-bar/color-picker';
import { ColorizeIcon } from 'icons'; import { ColorizeIcon } from 'icons';
import patterns from 'utils/validation-patterns'; import patterns from 'utils/validation-patterns';
import consts from 'consts'; import consts from 'consts';
@ -30,136 +30,123 @@ export enum AttributeType {
NUMBER = 'NUMBER', NUMBER = 'NUMBER',
} }
type Props = FormComponentProps & { interface Props {
label: Label | null; label: Label | null;
labelNames?: string[]; labelNames?: string[];
onSubmit: (label: Label | null) => void; onSubmit: (label: Label | null) => void;
}; }
class LabelForm extends React.PureComponent<Props, {}> { export default class LabelForm extends React.Component<Props> {
private continueAfterSubmit: boolean; private continueAfterSubmit: boolean;
private formRef: RefObject<FormInstance>;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
this.continueAfterSubmit = false; this.continueAfterSubmit = false;
this.formRef = React.createRef<FormInstance>();
} }
private handleSubmit = (e: React.FormEvent): void => { private handleSubmit = (values: Store): void => {
const { form, label, onSubmit } = this.props; const { label, onSubmit } = this.props;
e.preventDefault(); onSubmit({
form.validateFields((error, formValues): void => { name: values.labelName,
if (!error) { id: label ? label.id : idGenerator(),
onSubmit({ color: values.labelColor,
name: formValues.labelName, attributes: values.attributes.map((attribute: Store) => {
id: label ? label.id : idGenerator(), let attrValues: any = attribute.values;
color: formValues.labelColor, if (attribute.input_type === AttributeType.NUMBER) {
attributes: formValues.keys.map( attrValues = attrValues.split(';');
(key: number, index: number): Attribute => { } else {
let attrValues = formValues.values[key]; attrValues = [attrValues];
if (!Array.isArray(attrValues)) {
if (formValues.type[key] === AttributeType.NUMBER) {
attrValues = attrValues.split(';');
} else {
attrValues = [attrValues];
}
}
attrValues = attrValues.map((value: string) => value.trim());
return {
name: formValues.attrName[key],
input_type: formValues.type[key],
mutable: formValues.mutable[key],
id: label && index < label.attributes.length ? label.attributes[index].id : key,
values: attrValues,
};
},
),
});
form.resetFields();
if (!this.continueAfterSubmit) {
onSubmit(null);
} }
} attrValues = attrValues.map((value: string) => value.trim());
return {
...attribute,
values: attrValues,
input_type: attribute.type,
};
}),
}); });
if (this.formRef.current) {
this.formRef.current.resetFields();
}
if (!this.continueAfterSubmit) {
onSubmit(null);
}
}; };
private addAttribute = (): void => { private addAttribute = (): void => {
const { form } = this.props; if (this.formRef.current) {
const keys = form.getFieldValue('keys'); const attributes = this.formRef.current.getFieldValue('attributes');
const nextKeys = keys.concat(idGenerator()); this.formRef.current.setFieldsValue({ attributes: [...attributes, { id: idGenerator() }] });
form.setFieldsValue({ }
keys: nextKeys,
});
}; };
private removeAttribute = (key: number): void => { private removeAttribute = (key: number): void => {
const { form } = this.props; if (this.formRef.current) {
const keys = form.getFieldValue('keys'); const attributes = this.formRef.current.getFieldValue('attributes');
form.setFieldsValue({ this.formRef.current.setFieldsValue({
keys: keys.filter((_key: number) => _key !== key), attributes: attributes.filter((_: any, id: number) => id !== key),
}); });
}
}; };
private renderAttributeNameInput(key: number, attr: Attribute | null): JSX.Element { /* eslint-disable class-methods-use-this */
private renderAttributeNameInput(fieldInstance: any, attr: Attribute | null): JSX.Element {
const { key } = fieldInstance;
const locked = attr ? attr.id >= 0 : false; const locked = attr ? attr.id >= 0 : false;
const value = attr ? attr.name : ''; const value = attr ? attr.name : '';
const { form } = this.props;
return ( return (
<Col span={5}> <Form.Item
<Form.Item hasFeedback> hasFeedback
{form.getFieldDecorator(`attrName[${key}]`, { name={[key, 'name']}
initialValue: value, fieldKey={[fieldInstance.fieldKey, 'name']}
rules: [ initialValue={value}
{ rules={[
required: true, {
message: 'Please specify a name', required: true,
}, message: 'Please specify a name',
{ },
pattern: patterns.validateAttributeName.pattern, {
message: patterns.validateAttributeName.message, pattern: patterns.validateAttributeName.pattern,
}, message: patterns.validateAttributeName.message,
], },
})(<Input className='cvat-attribute-name-input' disabled={locked} placeholder='Name' />)} ]}
</Form.Item> >
</Col> <Input className='cvat-attribute-name-input' disabled={locked} placeholder='Name' />
</Form.Item>
); );
} }
private renderAttributeTypeInput(key: number, attr: Attribute | null): JSX.Element { private renderAttributeTypeInput(fieldInstance: any, attr: Attribute | null): JSX.Element {
const { key } = fieldInstance;
const locked = attr ? attr.id >= 0 : false; const locked = attr ? attr.id >= 0 : false;
const type = attr ? attr.input_type.toUpperCase() : AttributeType.SELECT; const type = attr ? attr.input_type.toUpperCase() : AttributeType.SELECT;
const { form } = this.props;
return ( return (
<Col span={4}> <Tooltip title='An HTML element representing the attribute' mouseLeaveDelay={0}>
<Form.Item> <Form.Item name={[key, 'type']} fieldKey={[fieldInstance.fieldKey, 'type']} initialValue={type}>
<Tooltip title='An HTML element representing the attribute' mouseLeaveDelay={0}> <Select className='cvat-attribute-type-input' disabled={locked}>
{form.getFieldDecorator(`type[${key}]`, { <Select.Option value={AttributeType.SELECT}>Select</Select.Option>
initialValue: type, <Select.Option value={AttributeType.RADIO}>Radio</Select.Option>
})( <Select.Option value={AttributeType.CHECKBOX}>Checkbox</Select.Option>
<Select className='cvat-attribute-type-input' disabled={locked}> <Select.Option value={AttributeType.TEXT}>Text</Select.Option>
<Select.Option value={AttributeType.SELECT}>Select</Select.Option> <Select.Option value={AttributeType.NUMBER}>Number</Select.Option>
<Select.Option value={AttributeType.RADIO}>Radio</Select.Option> </Select>
<Select.Option value={AttributeType.CHECKBOX}>Checkbox</Select.Option>
<Select.Option value={AttributeType.TEXT}>Text</Select.Option>
<Select.Option value={AttributeType.NUMBER}>Number</Select.Option>
</Select>,
)}
</Tooltip>
</Form.Item> </Form.Item>
</Col> </Tooltip>
); );
} }
private renderAttributeValuesInput(key: number, attr: Attribute | null): JSX.Element { private renderAttributeValuesInput(fieldInstance: any, attr: Attribute | null): JSX.Element {
const { key } = fieldInstance;
const locked = attr ? attr.id >= 0 : false; const locked = attr ? attr.id >= 0 : false;
const existedValues = attr ? attr.values : []; const existedValues = attr ? attr.values : [];
const { form } = this.props;
const validator = (_: any, values: string[], callback: any): void => { const validator = (_: any, values: string[], callback: any): void => {
if (locked && existedValues) { if (locked && existedValues) {
@ -179,55 +166,51 @@ class LabelForm extends React.PureComponent<Props, {}> {
return ( return (
<Tooltip title='Press enter to add a new value' mouseLeaveDelay={0}> <Tooltip title='Press enter to add a new value' mouseLeaveDelay={0}>
<Form.Item> <Form.Item
{form.getFieldDecorator(`values[${key}]`, { name={[key, 'values']}
initialValue: existedValues, fieldKey={[fieldInstance.fieldKey, 'values']}
rules: [ initialValue={existedValues}
{ rules={[
required: true, {
message: 'Please specify values', required: true,
}, message: 'Please specify values',
{ },
validator, {
}, validator,
], },
})( ]}
<Select >
className='cvat-attribute-values-input' <Select
mode='tags' className='cvat-attribute-values-input'
dropdownMenuStyle={{ display: 'none' }} mode='tags'
placeholder='Attribute values' placeholder='Attribute values'
/>, dropdownStyle={{ display: 'none' }}
)} />
</Form.Item> </Form.Item>
</Tooltip> </Tooltip>
); );
} }
private renderBooleanValueInput(key: number, attr: Attribute | null): JSX.Element { private renderBooleanValueInput(fieldInstance: any, attr: Attribute | null): JSX.Element {
const { key } = fieldInstance;
const value = attr ? attr.values[0] : 'false'; const value = attr ? attr.values[0] : 'false';
const { form } = this.props;
return ( return (
<Tooltip title='Specify a default value' mouseLeaveDelay={0}> <Tooltip title='Specify a default value' mouseLeaveDelay={0}>
<Form.Item> <Form.Item name={[key, 'values']} fieldKey={[fieldInstance.fieldKey, 'values']} initialValue={value}>
{form.getFieldDecorator(`values[${key}]`, { <Select className='cvat-attribute-values-input'>
initialValue: value, <Select.Option value='false'> False </Select.Option>
})( <Select.Option value='true'> True </Select.Option>
<Select className='cvat-attribute-values-input'> </Select>
<Select.Option value='false'> False </Select.Option>
<Select.Option value='true'> True </Select.Option>
</Select>,
)}
</Form.Item> </Form.Item>
</Tooltip> </Tooltip>
); );
} }
private renderNumberRangeInput(key: number, attr: Attribute | null): JSX.Element { private renderNumberRangeInput(fieldInstance: any, attr: Attribute | null): JSX.Element {
const { key } = fieldInstance;
const locked = attr ? attr.id >= 0 : false; const locked = attr ? attr.id >= 0 : false;
const value = attr ? attr.values.join(';') : ''; const value = attr ? attr.values.join(';') : '';
const { form } = this.props;
const validator = (_: any, strNumbers: string, callback: any): void => { const validator = (_: any, strNumbers: string, callback: any): void => {
const numbers = strNumbers.split(';').map((number): number => Number.parseFloat(number)); const numbers = strNumbers.split(';').map((number): number => Number.parseFloat(number));
@ -259,65 +242,57 @@ class LabelForm extends React.PureComponent<Props, {}> {
}; };
return ( return (
<Form.Item> <Form.Item
{form.getFieldDecorator(`values[${key}]`, { name={[key, 'values']}
initialValue: value, fieldKey={[fieldInstance.fieldKey, 'values']}
rules: [ initialValue={value}
{ rules={[
required: true, {
message: 'Please set a range', required: true,
}, message: 'Please set a range',
{ },
validator, {
}, validator,
], },
})(<Input className='cvat-attribute-values-input' disabled={locked} placeholder='min;max;step' />)} ]}
>
<Input className='cvat-attribute-values-input' disabled={locked} placeholder='min;max;step' />
</Form.Item> </Form.Item>
); );
} }
private renderDefaultValueInput(key: number, attr: Attribute | null): JSX.Element { private renderDefaultValueInput(fieldInstance: any, attr: Attribute | null): JSX.Element {
const { key } = fieldInstance;
const value = attr ? attr.values[0] : ''; const value = attr ? attr.values[0] : '';
const { form } = this.props;
return ( return (
<Form.Item> <Form.Item name={[key, 'values']} fieldKey={[fieldInstance.fieldKey, 'values']} initialValue={value}>
{form.getFieldDecorator(`values[${key}]`, { <Input className='cvat-attribute-values-input' placeholder='Default value' />
initialValue: value,
})(<Input className='cvat-attribute-values-input' placeholder='Default value' />)}
</Form.Item> </Form.Item>
); );
} }
private renderMutableAttributeInput(key: number, attr: Attribute | null): JSX.Element { private renderMutableAttributeInput(fieldInstance: any, attr: Attribute | null): JSX.Element {
const { key } = fieldInstance;
const locked = attr ? attr.id >= 0 : false; const locked = attr ? attr.id >= 0 : false;
const value = attr ? attr.mutable : false; const value = attr ? attr.mutable : false;
const { form } = this.props;
return ( return (
<Form.Item> <Tooltip title='Can this attribute be changed frame to frame?' mouseLeaveDelay={0}>
<Tooltip title='Can this attribute be changed frame to frame?' mouseLeaveDelay={0}> <Form.Item name={[key, 'mutable']} fieldKey={[fieldInstance.fieldKey, 'mutable']} initialValue={value} valuePropName='checked'>
{form.getFieldDecorator(`mutable[${key}]`, { <Checkbox className='cvat-attribute-mutable-checkbox' disabled={locked}>Mutable</Checkbox>
initialValue: value, </Form.Item>
valuePropName: 'checked', </Tooltip>
})(
<Checkbox className='cvat-attribute-mutable-checkbox' disabled={locked}>
{' '}
Mutable
{' '}
</Checkbox>,
)}
</Tooltip>
</Form.Item>
); );
} }
private renderDeleteAttributeButton(key: number, attr: Attribute | null): JSX.Element { private renderDeleteAttributeButton(fieldInstance: any, attr: Attribute | null): JSX.Element {
const { key } = fieldInstance;
const locked = attr ? attr.id >= 0 : false; const locked = attr ? attr.id >= 0 : false;
return ( return (
<Form.Item> <Tooltip title='Delete the attribute' mouseLeaveDelay={0}>
<Tooltip title='Delete the attribute' mouseLeaveDelay={0}> <Form.Item>
<Button <Button
type='link' type='link'
className='cvat-delete-attribute-button' className='cvat-delete-attribute-button'
@ -328,135 +303,138 @@ class LabelForm extends React.PureComponent<Props, {}> {
> >
<CloseCircleOutlined /> <CloseCircleOutlined />
</Button> </Button>
</Tooltip> </Form.Item>
</Form.Item> </Tooltip>
); );
} }
private renderAttribute = (key: number): JSX.Element => { private renderAttribute = (fieldInstance: any): JSX.Element => {
const { label, form } = this.props; const { label } = this.props;
const attr = label ? label.attributes.filter((_attr: any): boolean => _attr.id === key)[0] : null; const { key } = fieldInstance;
const fieldValue = this.formRef.current?.getFieldValue('attributes')[key];
const attr = label ? label.attributes.filter((_attr: any): boolean => _attr.id === fieldValue.id)[0] : null;
return ( return (
<Form.Item key={key}> <Form.Item
<Row noStyle
type='flex' key={key}
justify='space-between' shouldUpdate
align='middle' >
cvat-attribute-id={key} {() => ((
className='cvat-attribute-inputs-wrapper' <Row
> justify='space-between'
{this.renderAttributeNameInput(key, attr)} align='middle'
{this.renderAttributeTypeInput(key, attr)} cvat-attribute-id={key}
<Col span={6}> className='cvat-attribute-inputs-wrapper'
{((): JSX.Element => { >
const type = form.getFieldValue(`type[${key}]`); <Col span={5}>{this.renderAttributeNameInput(fieldInstance, attr)}</Col>
let element = null; <Col span={4}>{this.renderAttributeTypeInput(fieldInstance, attr)}</Col>
if ([AttributeType.SELECT, AttributeType.RADIO].includes(type)) { <Col span={6}>
element = this.renderAttributeValuesInput(key, attr); {((): JSX.Element => {
} else if (type === AttributeType.CHECKBOX) { const currentFieldValue = this.formRef.current?.getFieldValue('attributes')[key];
element = this.renderBooleanValueInput(key, attr); const type = currentFieldValue.type || AttributeType.SELECT;
} else if (type === AttributeType.NUMBER) { let element = null;
element = this.renderNumberRangeInput(key, attr); if ([AttributeType.SELECT, AttributeType.RADIO].includes(type)) {
} else { element = this.renderAttributeValuesInput(fieldInstance, attr);
element = this.renderDefaultValueInput(key, attr); } else if (type === AttributeType.CHECKBOX) {
} element = this.renderBooleanValueInput(fieldInstance, attr);
} else if (type === AttributeType.NUMBER) {
return element; element = this.renderNumberRangeInput(fieldInstance, attr);
})()} } else {
</Col> element = this.renderDefaultValueInput(fieldInstance, attr);
<Col span={5}>{this.renderMutableAttributeInput(key, attr)}</Col> }
<Col span={2}>{this.renderDeleteAttributeButton(key, attr)}</Col>
</Row> return element;
})()}
</Col>
<Col span={5}>{this.renderMutableAttributeInput(fieldInstance, attr)}</Col>
<Col span={2}>{this.renderDeleteAttributeButton(fieldInstance, attr)}</Col>
</Row>
))}
</Form.Item> </Form.Item>
); );
}; };
private renderLabelNameInput(): JSX.Element { private renderLabelNameInput(): JSX.Element {
const { label, form, labelNames } = this.props; const { label, labelNames } = this.props;
const value = label ? label.name : ''; const value = label ? label.name : '';
const locked = label ? label.id >= 0 : false; const locked = label ? label.id >= 0 : false;
return ( return (
<Col span={10}> <Form.Item
<Form.Item hasFeedback> hasFeedback
{form.getFieldDecorator('labelName', { name='labelName'
initialValue: value, initialValue={value}
rules: [ rules={
{ [
required: true, {
message: 'Please specify a name', required: true,
}, message: 'Please specify a name',
{ },
pattern: patterns.validateAttributeName.pattern, {
message: patterns.validateAttributeName.message, pattern: patterns.validateAttributeName.pattern,
}, message: patterns.validateAttributeName.message,
{ },
validator: async (_rule: any, labelName: string, callback: Function) => { {
if (labelNames && labelNames.includes(labelName)) { validator: async (_rule: any, labelName: string, callback: Function) => {
callback('Label name must be unique for the task'); if (labelNames && labelNames.includes(labelName)) {
} callback('Label name must be unique for the task');
}, }
}, },
], },
})(<Input disabled={locked} placeholder='Label name' />)} ]
</Form.Item> }
</Col> >
<Input disabled={locked} placeholder='Label name' />
</Form.Item>
); );
} }
private renderNewAttributeButton(): JSX.Element { private renderNewAttributeButton(): JSX.Element {
return ( return (
<Col span={6}> <Form.Item>
<Form.Item> <Button type='ghost' onClick={this.addAttribute} className='cvat-new-attribute-button'>
<Button type='ghost' onClick={this.addAttribute} className='cvat-new-attribute-button'> Add an attribute
Add an attribute <PlusOutlined />
<PlusOutlined /> </Button>
</Button> </Form.Item>
</Form.Item>
</Col>
); );
} }
private renderDoneButton(): JSX.Element { private renderDoneButton(): JSX.Element {
return ( return (
<Col> <Tooltip title='Save the label and return' mouseLeaveDelay={0}>
<Tooltip title='Save the label and return' mouseLeaveDelay={0}> <Button
<Button style={{ width: '150px' }}
style={{ width: '150px' }} type='primary'
type='primary' htmlType='submit'
htmlType='submit' onClick={(): void => {
onClick={(): void => { this.continueAfterSubmit = false;
this.continueAfterSubmit = false; }}
}} >
> Done
Done </Button>
</Button> </Tooltip>
</Tooltip>
</Col>
); );
} }
private renderContinueButton(): JSX.Element { private renderContinueButton(): JSX.Element | null {
const { label } = this.props; const { label } = this.props;
return label ? ( if (label) return null;
<div /> return (
) : ( <Tooltip title='Save the label and create one more' mouseLeaveDelay={0}>
<Col offset={1}> <Button
<Tooltip title='Save the label and create one more' mouseLeaveDelay={0}> style={{ width: '150px' }}
<Button type='primary'
style={{ width: '150px' }} htmlType='submit'
type='primary' onClick={(): void => {
htmlType='submit' this.continueAfterSubmit = true;
onClick={(): void => { }}
this.continueAfterSubmit = true; >
}} Continue
> </Button>
Continue </Tooltip>
</Button>
</Tooltip>
</Col>
); );
} }
@ -464,83 +442,93 @@ class LabelForm extends React.PureComponent<Props, {}> {
const { onSubmit } = this.props; const { onSubmit } = this.props;
return ( return (
<Col offset={1}> <Tooltip title='Do not save the label and return' mouseLeaveDelay={0}>
<Tooltip title='Do not save the label and return' mouseLeaveDelay={0}> <Button
<Button danger
danger style={{ width: '150px' }}
style={{ width: '150px' }} onClick={(): void => {
onClick={(): void => { onSubmit(null);
onSubmit(null); }}
}} >
> Cancel
Cancel </Button>
</Button> </Tooltip>
</Tooltip>
</Col>
); );
} }
private renderChangeColorButton(): JSX.Element { private renderChangeColorButton(): JSX.Element {
const { label, form } = this.props; const { label } = this.props;
return ( return (
<Col span={3}> <Form.Item name='labelColor' initialValue={label && label.color ? label.color : undefined}>
<Form.Item> <ColorPicker placement='bottom'>
{form.getFieldDecorator('labelColor', { <Tooltip title='Change color of the label'>
initialValue: label && label.color ? label.color : undefined, <Button type='default' className='cvat-change-task-label-color-button'>
})( <Badge
<ColorPicker placement='bottom'> className='cvat-change-task-label-color-badge'
<Tooltip title='Change color of the label'> color={this.formRef.current?.getFieldValue('labelColor') || consts.NEW_LABEL_COLOR}
<Button type='default' className='cvat-change-task-label-color-button'> text={<Icon component={ColorizeIcon} />}
<Badge />
className='cvat-change-task-label-color-badge' </Button>
color={form.getFieldValue('labelColor') || consts.NEW_LABEL_COLOR} </Tooltip>
text={<Icon component={ColorizeIcon} />} </ColorPicker>
/> </Form.Item>
</Button>
</Tooltip>
</ColorPicker>,
)}
</Form.Item>
</Col>
); );
} }
public render(): JSX.Element { private renderAttributes() {
const { label, form } = this.props; return (fieldInstances: any[]): JSX.Element[] => fieldInstances.map(this.renderAttribute);
}
form.getFieldDecorator('keys', {
initialValue: label ? label.attributes.map((attr: Attribute): number => attr.id) : [],
});
const keys = form.getFieldValue('keys'); // eslint-disable-next-line react/sort-comp
const attributeItems = keys.map(this.renderAttribute); public componentDidMount(): void {
const { label } = this.props;
if (this.formRef.current) {
this.formRef.current.setFieldsValue({
attributes: label ? label.attributes
.map((attribute: Attribute): Store => ({
...attribute,
type: attribute.input_type,
})) : [],
});
}
}
public render(): JSX.Element {
return ( return (
<Form onSubmit={this.handleSubmit}> <Form onFinish={this.handleSubmit} layout='vertical' ref={this.formRef}>
<Row type='flex' justify='start' align='middle'> <Row justify='start' align='middle'>
{this.renderLabelNameInput()} <Col span={10}>
{this.renderLabelNameInput()}
</Col>
<Col span={1} /> <Col span={1} />
{this.renderChangeColorButton()} <Col span={3}>
{this.renderChangeColorButton()}
</Col>
<Col span={1} /> <Col span={1} />
{this.renderNewAttributeButton()} <Col span={6}>
{this.renderNewAttributeButton()}
</Col>
</Row> </Row>
{attributeItems.length > 0 && ( <Row justify='start' align='middle'>
<Row type='flex' justify='start' align='middle'> <Col span={24}>
<Col> <Form.List name='attributes'>
<Text>Attributes</Text> { this.renderAttributes() }
</Col> </Form.List>
</Row> </Col>
)} </Row>
{attributeItems.reverse()} <Row justify='start' align='middle'>
<Row type='flex' justify='start' align='middle'> <Col>
{this.renderDoneButton()} {this.renderDoneButton()}
{this.renderContinueButton()} </Col>
{this.renderCancelButton()} <Col offset={1}>
{this.renderContinueButton()}
</Col>
<Col offset={1}>
{this.renderCancelButton()}
</Col>
</Row> </Row>
</Form> </Form>
); );
} }
} }
export default Form.create<Props>()(LabelForm);

@ -298,7 +298,7 @@ export default class DetailsComponent extends React.PureComponent<Props, State>
return ( return (
<Row> <Row>
<Col> <Col span={24}>
<LabelsEditorComponent <LabelsEditorComponent
labels={taskInstance.labels.map((label: any): string => label.toJSON())} labels={taskInstance.labels.map((label: any): string => label.toJSON())}
onSubmit={(labels: any[]): void => { onSubmit={(labels: any[]): void => {

Loading…
Cancel
Save