src/components/controls/InputSelect/InputContent.jsx
import React from 'react';
import cn from 'classnames';
import PropTypes from 'prop-types';
import SelectedItems from './SelectedItems';
import ReactDOM from 'react-dom';
import { map, find, reduce, split, isEqual } from 'lodash';
import { getNextId, getPrevId, getFirstNotDisabledId } from './utils';
/**
* InputSelectGroup
* @reactProps {boolean} disabled - флаг неактивности
* @reactProps {string} value - текущее значение инпута
* @reactProps {string} placeHolder - подсказка в инпуте
* @reactProps {function} onRemoveItem - callback при нажатии на удаление элемента из выбранных при мульти выборе
* @reactProps {function} onFocus - событие фокуса
* @reactProps {function} onBlur - событие потери фокуса
* @reactProps {node} inputFocus - элемент на котором произошло событие фокуса
* @reactProps {boolean} isSelected
* @reactProps {array} selected - список выбранных элементов
* @reactProps {string} labelFieldId - значение ключа label в данных
* @reactProps {string} valueFieldId
* @reactProps {function} clearSelected - callback удаление всех выбранных элементов при мульти выборе
* @reactProps {boolean} multiSelect - фдаг мульти выбора
* @reactProps {boolean} collapseSelected - флаг сжатия выбранных элементов
* @reactProps {number} lengthToGroup - от скольки элементов сжимать выбранные элементы
* @reactProps {function} onInputChange - callback при изменение инпута
* @reactProps {function} openPopUp - открытие попапа
* @reactProps {function} closePopUp - закрытие попапа
* @reactProps {string} activeValueId
* @reactProps {function} setActiveValueId
* @reactProps {array} disabledValues
* @reactProps {object} options
* @reactProps {function} onSelect - событие выбора
* @reactProps {function} onClick - событие клика
* @reactProps {boolean} isExpanded - флаг видимости popUp
*/
function InputContent({
disabled,
value,
placeholder,
onRemoveItem,
onFocus,
onBlur,
inputFocus,
isSelected,
selected,
labelFieldId,
valueFieldId,
clearSelected,
multiSelect,
collapseSelected,
lengthToGroup,
onInputChange,
openPopUp,
closePopUp,
activeValueId,
setActiveValueId,
disabledValues,
options,
onSelect,
onClick,
isExpanded,
autoFocus,
selectedPadding,
setTextareaRef,
setSelectedListRef,
_textarea,
_selectedList,
setRef,
}) {
/**
* Обработчик изменения инпута при нажатии на клавишу
* @param e - событие изменения
* @private
*/
const handleKeyDown = e => {
if (
multiSelect &&
e.key === 'Backspace' &&
selected.length &&
!e.target.value
) {
const endElementOfSelect = selected[selected.length - 1];
onRemoveItem(endElementOfSelect);
} else if (e.key === 'ArrowDown') {
e.preventDefault();
if (!isExpanded) {
openPopUp(true);
setActiveValueId(
getFirstNotDisabledId(options, selected, disabledValues, valueFieldId)
);
} else {
if (activeValueId) {
setActiveValueId(
getNextId(
options,
activeValueId,
valueFieldId,
selected,
disabledValues
)
);
} else {
setActiveValueId(
getFirstNotDisabledId(
options,
selected,
disabledValues,
valueFieldId
)
);
}
}
} else if (e.key === 'ArrowUp') {
e.preventDefault();
setActiveValueId(
getPrevId(
options,
activeValueId,
valueFieldId,
selected,
disabledValues
)
);
} else if (e.key === 'Enter') {
const newValaue = find(
options,
item => item[valueFieldId] === activeValueId
);
newValaue && onSelect(newValaue);
} else if (e.key === 'Escape') {
closePopUp(false);
}
};
const handleClick = ({ target }) => {
target.select();
onClick && onClick();
};
/**
* Обработчик изменения инпута
* @param e - событие изменения
* @private
*/
const handleInputChange = e => {
onInputChange(e.target.value);
};
const getPlaceholder = selected.length > 0 ? '' : placeholder;
let inputEl = null;
const handleRef = input => {
const el = input && ReactDOM.findDOMNode(input);
if (el && isSelected) {
el.select();
} else if (el && inputFocus) {
inputFocus && el.focus();
}
};
const getHeight = el => {
return el.clientHeight;
};
const getWidth = el => {
return el.clientWidth;
};
const getMargin = (item, propertyName) => {
return +split(window.getComputedStyle(item)[propertyName], 'px')[0];
};
const calcPaddingTextarea = () => {
if (_textarea && _selectedList) {
let mainWidth = undefined;
let mainHeight = undefined;
const selectedList = ReactDOM.findDOMNode(_selectedList).querySelectorAll(
'.selected-item'
);
mainWidth = reduce(
selectedList,
(acc, item) => {
const marginLeft = getMargin(item, 'margin-left');
const marginRight = getMargin(item, 'margin-right');
const newWidth = acc + item.offsetWidth + marginRight + marginLeft;
if (newWidth >= getWidth(_selectedList)) {
acc = 0;
}
return acc + item.offsetWidth + marginLeft + marginRight;
},
0
);
const lastItem = selectedList[selectedList.length - 1];
if (lastItem) {
mainHeight = getHeight(_textarea) - getHeight(lastItem);
}
return {
paddingTop: mainHeight,
paddingLeft: mainWidth || undefined,
};
}
};
const INPUT_STYLE = {
paddingLeft: selectedPadding ? selectedPadding : undefined,
};
return (
<React.Fragment>
{multiSelect ? (
<React.Fragment>
<SelectedItems
selected={selected}
labelFieldId={labelFieldId}
onRemoveItem={onRemoveItem}
onDeleteAll={clearSelected}
disabled={disabled}
collapseSelected={collapseSelected}
lengthToGroup={lengthToGroup}
setRef={setSelectedListRef}
/>
<textarea
onKeyDown={handleKeyDown}
ref={setTextareaRef}
placeholder={getPlaceholder}
disabled={disabled}
value={value}
onChange={handleInputChange}
onClick={handleClick}
onFocus={onFocus}
onBlur={onBlur}
className={cn('form-control n2o-inp', {
'n2o-inp--multi': multiSelect,
})}
autoFocus={autoFocus}
style={{
...calcPaddingTextarea(),
}}
/>
</React.Fragment>
) : (
<input
onKeyDown={handleKeyDown}
ref={setRef}
placeholder={getPlaceholder}
disabled={disabled}
value={value}
onChange={handleInputChange}
onClick={handleClick}
onFocus={onFocus}
onBlur={onBlur}
type="text"
className="form-control n2o-inp"
autoFocus={autoFocus}
style={INPUT_STYLE}
/>
)}
</React.Fragment>
);
}
InputContent.propTypes = {
isExpanded: PropTypes.bool,
disabled: PropTypes.bool,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
placeholder: PropTypes.string,
onInputChange: PropTypes.func,
selected: PropTypes.array,
labelFieldId: PropTypes.string,
onRemoveItem: PropTypes.func,
clearSelected: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
inputFocus: PropTypes.node,
multiSelect: PropTypes.bool,
collapseSelected: PropTypes.bool,
lengthToGroup: PropTypes.number,
openPopUp: PropTypes.func,
closePopUp: PropTypes.func,
activeValueId: PropTypes.string,
setActiveValueId: PropTypes.func,
disabledValues: PropTypes.array,
options: PropTypes.object,
onSelect: PropTypes.func,
onClick: PropTypes.func,
isSelected: PropTypes.bool,
valueFieldId: PropTypes.string,
autoFocus: PropTypes.bool,
};
InputContent.defaultProps = {
multiSelect: false,
disabled: false,
collapseSelected: true,
autoFocus: false,
};
export default InputContent;