src/components/widgets/Form/fields/RangeField/RangeField.jsx
import React from 'react';
import PropTypes from 'prop-types';
import { compose, withHandlers, mapProps } from 'recompose';
import { get } from 'lodash';
import Control from '../StandardField/Control';
import Measure from '../StandardField/Measure';
import Label from '../StandardField/Label';
import FieldActions from '../StandardField/FieldActions';
import InlineSpinner from '../../../../snippets/Spinner/InlineSpinner';
import Description from '../StandardField/Description';
import cx from 'classnames';
import { FieldActionsPropTypes } from '../StandardField/FieldPropTypes';
/**
* Компонент - RangeField формы
* @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 - подскзка рядом с лейблом
* @reactProps {string|boolean} - разделитель между полями
* @return {node|null}
* @example
* <RangeField onChange={this.onChange}
* id='DistanceInput'
* control='Input'
* label="Расстояние"
* measure="км"
* description="Введите расстояние от пункта А до пункта Б"
* style={display: 'inline-block'}/>
*/
function RangeField({
beginControl,
endControl,
id,
visible,
label,
control,
description,
measure,
required,
className,
labelPosition,
labelAlignment,
labelWidth,
style,
fieldActions,
loading,
autoFocus,
labelStyle,
controlStyle,
labelClass,
validationClass,
controlClass,
onFocus,
onBlur,
placeholder,
touched,
message,
colLength,
help,
value,
onChange,
onBeginValueChange,
onEndValueChange,
begin,
end,
divider,
...props
}) {
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 extendedLabelStyle = {
width: labelWidthPixels,
flex: labelWidthPixels ? 'none' : undefined,
...labelStyle,
};
return visible ? (
<div
className={cx(
'n2o-range-field',
'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 className="n2o-range-field-body">
<div
className={cx(
'n2o-range-field-controls-container',
'd-flex',
'align-items-center',
{
'n2o-range-field-body--divider': !divider,
}
)}
>
<div className="n2o-range-field-start n2o-range-field-item mr-3">
<div className="d-flex align-items-center">
<Control
placeholder={placeholder}
visible={visible}
autoFocus={autoFocus}
value={begin}
onBlur={onBlur}
onFocus={onFocus}
onChange={onBeginValueChange}
{...beginControl}
{...props}
className={cx(beginControl && beginControl.className, {
[validationClass]: touched,
})}
/>
<Measure value={measure} />
</div>
</div>
{divider && <div className="n2o-range-field-divider">{divider}</div>}
<div className="n2o-range-field-end n2o-range-field-item ml-3">
<div className="d-flex align-items-center">
<Control
placeholder={placeholder}
visible={visible}
autoFocus={false}
value={end}
onBlur={onBlur}
onFocus={onFocus}
onChange={onEndValueChange}
{...endControl}
{...props}
className={cx(endControl && endControl.className, {
[validationClass]: touched,
})}
/>
<Measure value={measure} />
</div>
</div>
</div>
{loading && <InlineSpinner />}
<FieldActions actions={fieldActions} />
</div>
<Description value={description} />
<div
className={cx('n2o-validation-message', validationMap[validationClass])}
>
{touched && message && message.text}
</div>
</div>
) : null;
}
RangeField.propTypes = {
label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
control: PropTypes.node,
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]),
divider: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
};
RangeField.defaultProps = {
visible: true,
required: false,
control: <input />,
loading: false,
className: '',
style: {},
enabled: true,
disabled: false,
onChange: () => {},
divider: false,
};
export default compose(
mapProps(props => ({
...props,
begin: get(props, 'value.begin', null),
end: get(props, 'value.end', null),
})),
withHandlers({
onBeginValueChange: ({ end, onChange }) => begin =>
onChange({ begin, end }),
onEndValueChange: ({ begin, onChange }) => end => onChange({ begin, end }),
})
)(RangeField);