// Copyright (C) 2020 Intel Corporation // // SPDX-License-Identifier: MIT import React from 'react'; import { Row, Col, Icon, Input, Button, Select, Tooltip, Checkbox, } from 'antd'; import Form, { FormComponentProps } from 'antd/lib/form/Form'; import Text from 'antd/lib/typography/Text'; import patterns from 'utils/validation-patterns'; import { equalArrayHead, idGenerator, Label, Attribute, } from './common'; export enum AttributeType { SELECT = 'SELECT', RADIO = 'RADIO', CHECKBOX = 'CHECKBOX', TEXT = 'TEXT', NUMBER = 'NUMBER', } type Props = FormComponentProps & { label: Label | null; onSubmit: (label: Label | null) => void; }; class LabelForm extends React.PureComponent { private continueAfterSubmit: boolean; constructor(props: Props) { super(props); this.continueAfterSubmit = false; } private handleSubmit = (e: React.FormEvent): void => { e.preventDefault(); const { form, label, onSubmit, } = this.props; form.validateFields((error, formValues): void => { if (!error) { onSubmit({ name: formValues.labelName, id: label ? label.id : idGenerator(), attributes: formValues.keys.map((key: number, index: number): Attribute => { let attrValues = formValues.values[key]; 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); } } }); }; private addAttribute = (): void => { const { form } = this.props; const keys = form.getFieldValue('keys'); const nextKeys = keys.concat(idGenerator()); form.setFieldsValue({ keys: nextKeys, }); }; private removeAttribute = (key: number): void => { const { form } = this.props; const keys = form.getFieldValue('keys'); form.setFieldsValue({ keys: keys.filter((_key: number) => _key !== key), }); }; private renderAttributeNameInput(key: number, attr: Attribute | null): JSX.Element { const locked = attr ? attr.id >= 0 : false; const value = attr ? attr.name : ''; const { form } = this.props; return ( {form.getFieldDecorator(`attrName[${key}]`, { initialValue: value, rules: [{ required: true, message: 'Please specify a name', }, { pattern: patterns.validateAttributeName.pattern, message: patterns.validateAttributeName.message, }], })()} ); } private renderAttributeTypeInput(key: number, attr: Attribute | null): JSX.Element { const locked = attr ? attr.id >= 0 : false; const type = attr ? attr.input_type.toUpperCase() : AttributeType.SELECT; const { form } = this.props; return ( { form.getFieldDecorator(`type[${key}]`, { initialValue: type, })( , )} ); } private renderAttributeValuesInput(key: number, attr: Attribute | null): JSX.Element { const locked = attr ? attr.id >= 0 : false; const existedValues = attr ? attr.values : []; const { form } = this.props; const validator = (_: any, values: string[], callback: any): void => { if (locked && existedValues) { if (!equalArrayHead(existedValues, values)) { callback('You can only append new values'); } } for (const value of values) { if (!patterns.validateAttributeValue.pattern.test(value)) { callback(`Invalid attribute value: "${value}"`); } } callback(); }; return ( { form.getFieldDecorator(`values[${key}]`, { initialValue: existedValues, rules: [{ required: true, message: 'Please specify values', }, { validator, }], })( False True , )} ); } private renderNumberRangeInput(key: number, attr: Attribute | null): JSX.Element { const locked = attr ? attr.id >= 0 : false; const value = attr ? attr.values.join(';') : ''; const { form } = this.props; const validator = (_: any, strNumbers: string, callback: any): void => { const numbers = strNumbers .split(';') .map((number): number => Number.parseFloat(number)); if (numbers.length !== 3) { callback('Invalid input'); } for (const number of numbers) { if (Number.isNaN(number)) { callback('Invalid input'); } } if (numbers[0] >= numbers[1]) { callback('Invalid input'); } if (+numbers[1] - +numbers[0] < +numbers[2]) { callback('Invalid input'); } callback(); }; return ( { form.getFieldDecorator(`values[${key}]`, { initialValue: value, rules: [{ required: true, message: 'Please set a range', }, { validator, }], })( , )} ); } private renderDefaultValueInput(key: number, attr: Attribute | null): JSX.Element { const value = attr ? attr.values[0] : ''; const { form } = this.props; return ( { form.getFieldDecorator(`values[${key}]`, { initialValue: value, })( , )} ); } private renderMutableAttributeInput(key: number, attr: Attribute | null): JSX.Element { const locked = attr ? attr.id >= 0 : false; const value = attr ? attr.mutable : false; const { form } = this.props; return ( { form.getFieldDecorator(`mutable[${key}]`, { initialValue: value, valuePropName: 'checked', })( Mutable , )} ); } private renderDeleteAttributeButton(key: number, attr: Attribute | null): JSX.Element { const locked = attr ? attr.id >= 0 : false; return ( ); } private renderAttribute = (key: number, index: number): JSX.Element => { const { label, form, } = this.props; const attr = (label && index < label.attributes.length ? label.attributes[index] : null); return ( { this.renderAttributeNameInput(key, attr) } { this.renderAttributeTypeInput(key, attr) } {((): JSX.Element => { const type = form.getFieldValue(`type[${key}]`); let element = null; if ([AttributeType.SELECT, AttributeType.RADIO].includes(type)) { element = this.renderAttributeValuesInput(key, attr); } else if (type === AttributeType.CHECKBOX) { element = this.renderBooleanValueInput(key, attr); } else if (type === AttributeType.NUMBER) { element = this.renderNumberRangeInput(key, attr); } else { element = this.renderDefaultValueInput(key, attr); } return element; })()} { this.renderMutableAttributeInput(key, attr) } { this.renderDeleteAttributeButton(key, attr) } ); }; private renderLabelNameInput(): JSX.Element { const { label, form, } = this.props; const value = label ? label.name : ''; const locked = label ? label.id >= 0 : false; return ( {form.getFieldDecorator('labelName', { initialValue: value, rules: [{ required: true, message: 'Please specify a name', }, { pattern: patterns.validateAttributeName.pattern, message: patterns.validateAttributeName.message, }], })()} ); } private renderNewAttributeButton(): JSX.Element { return ( ); } private renderDoneButton(): JSX.Element { return ( ); } private renderContinueButton(): JSX.Element { const { label } = this.props; return ( label ?
: ( ) ); } private renderCancelButton(): JSX.Element { const { onSubmit } = this.props; return ( ); } public render(): JSX.Element { const { label, form, } = this.props; form.getFieldDecorator('keys', { initialValue: label ? label.attributes.map((attr: Attribute): number => attr.id) : [], }); const keys = form.getFieldValue('keys'); const attributeItems = keys.map(this.renderAttribute); return (
{ this.renderLabelNameInput() } { this.renderNewAttributeButton() } { attributeItems.length > 0 && ( Attributes )} { attributeItems.reverse() } { this.renderDoneButton() } { this.renderContinueButton() } { this.renderCancelButton() }
); } } export default Form.create()(LabelForm);