src/components/widgets/Form/fields/StandardField/StandardField.jsx
import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import Control from './Control';
import Label from './Label';
import Measure from './Measure';
import Description from './Description';
import FieldActions from './FieldActions';
import InlineSpinner from '../../../../snippets/Spinner/InlineSpinner';
import { FieldActionsPropTypes } from './FieldPropTypes';
/**
* Компонент - поле формы
* @reactProps {string} id - уникальный идентификатор поля
* @reactProps {boolean} visible - отображать / не отображать Поле
* @reactProps {string} label - лэйбл поля
* @reactProps {string} labelClass - css-класс для лейбела
* @reactProps {string} controlClass - css-класс для контрола
* @reactProps {object} labelStyle- объект стилей для лейбела
* @reactProps {object} controlStyle - объект стилей для контрола
* @reactProps {string} className - css-класс для поля
* @reactProps {boolean} required - обязательное / необязательное поле
* @reactProps {boolean} disabled - контрол доступен только для чтения / нет
* @reactProps {boolean} enabled - контрол активирован / нет
* @reactProps {string|element} control - строка с названием компонента (тем, которое указано в мэпе index.js) или элемент
* @reactProps {string} description - описание поля (находится под контролом)
* @reactProps {string} measure - единица измерения, находится после контрола (например, км, кг, л)
* @reactProps {object} style - объект с css-стилями для поля
* @reactProps {object} fieldActions - объект для создания экшенов, связанных с полем
* @reactProps {function} onChange - вызывается при изменении контрола
* @reactProps {boolean} loading - показывать лоадер(спиннер) или нет
* @reactProps {boolean} autofocus - есть автофокус на это поле или нет
* @reactProps {string} validationClass - css-класс валидации(has-error, has-warning или has-success)
* @reactProps {object} message - содержит поле text c текстом сообщения(ошибки)
* @reactProps {string|node} help - подскзка рядом с лейблом
* @example
* <Field onChange={this.onChange}
* id='DistanceInput'
* control='Input'
* label="Расстояние"
* measure="км"
* description="Введите расстояние от пункта А до пункта Б"
* style={display: 'inline-block'}/>
*/
class StandardField extends React.Component {
/**
* Базовый рендер компонента
*/
render() {
const {
id,
value,
visible,
label,
control,
description,
measure,
required,
className,
labelPosition,
labelAlignment,
labelWidth,
style,
fieldActions,
loading,
autoFocus,
labelStyle,
controlStyle,
labelClass,
validationClass,
controlClass,
onChange,
onFocus,
onBlur,
placeholder,
touched,
message,
colLength,
help,
...props
} = this.props;
const flexStyle = { display: 'flex' };
const validationMap = {
'is-valid': 'text-success',
'is-invalid': 'text-danger',
'has-warning': 'text-warning',
};
const labelWidthPixels =
labelWidth === 'default'
? 180
: labelWidth === 'min' || labelWidth === '100%'
? undefined
: labelWidth;
const styleHelper =
labelWidthPixels && colLength > 1
? {
maxWidth: `calc(100% - ${labelWidthPixels})`,
}
: { width: '100%' };
const extendedLabelStyle = {
width: labelWidthPixels,
flex: labelWidthPixels ? 'none' : undefined,
...labelStyle,
};
return (
visible && (
<div
className={cx('n2o-form-group', 'form-group', className, {
['label-' + labelPosition]: labelPosition,
})}
style={style}
>
<Label
id={id}
value={label}
style={extendedLabelStyle}
className={cx(
labelClass,
{ ['label-alignment-' + labelAlignment]: labelAlignment },
'n2o-label'
)}
required={required}
help={help}
/>
<div style={styleHelper}>
<div style={flexStyle}>
<Control
placeholder={placeholder}
visible={visible}
autoFocus={autoFocus}
value={value}
onBlur={onBlur}
onFocus={onFocus}
onChange={onChange}
{...control}
{...props}
className={cx(control && control.className, {
[validationClass]: touched,
})}
/>
<Measure value={measure} />
<FieldActions actions={fieldActions} />
{loading && <InlineSpinner />}
</div>
<Description value={description} />
<div
className={cx(
'n2o-validation-message',
validationMap[validationClass]
)}
>
{touched && message && message.text}
</div>
</div>
</div>
)
);
}
}
StandardField.propTypes = {
label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
control: PropTypes.oneOfType([
PropTypes.func,
PropTypes.node,
PropTypes.string,
PropTypes.object,
]),
visible: PropTypes.bool,
required: PropTypes.bool,
disabled: PropTypes.bool,
autoFocus: PropTypes.bool,
onChange: PropTypes.func,
description: PropTypes.string,
measure: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
fieldActions: FieldActionsPropTypes,
valiastionClass: PropTypes.string,
loading: PropTypes.bool,
touched: PropTypes.bool,
labelWidth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
labelAlignment: PropTypes.oneOf(['left', 'right']),
labelPosition: PropTypes.oneOf(['top-left', 'top-right', 'left', 'right']),
message: PropTypes.object,
help: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
};
StandardField.defaultProps = {
visible: true,
required: false,
control: <input />,
loading: false,
className: '',
style: {},
enabled: true,
disabled: false,
onChange: () => {},
};
export default StandardField;