components/Input/MultiSelectInput/Options/index.js
import React from 'react';
import PropTypes from 'prop-types';
import { listToMap, isListEqual } from '@togglecorp/fujs';
import FloatingContainer from '../../../View/FloatingContainer';
import List from '../../../View/List';
import Option from '../Option';
import styles from './styles.scss';
const propTypes = {
activeKeys: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
className: PropTypes.string,
data: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types
keySelector: PropTypes.func.isRequired,
labelSelector: PropTypes.func.isRequired,
optionLabelSelector: PropTypes.func,
onBlur: PropTypes.func.isRequired,
onInvalidate: PropTypes.func.isRequired,
onOptionClick: PropTypes.func.isRequired,
onOptionFocus: PropTypes.func.isRequired,
parentContainer: PropTypes.object, // eslint-disable-line react/forbid-prop-types
renderEmpty: PropTypes.func,
show: PropTypes.bool.isRequired,
focusedKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};
const defaultProps = {
className: '',
renderEmpty: () => 'No option available',
parentContainer: undefined,
focusedKey: undefined,
optionLabelSelector: undefined,
};
export default class Options extends React.PureComponent {
static propTypes = propTypes;
static defaultProps = defaultProps;
constructor(props) {
super(props);
this.generateActiveMap(props);
}
// eslint-disable-next-line camelcase
UNSAFE_componentWillReceiveProps(nextProps) {
const { activeKeys: oldActiveKeys } = this.props;
const { activeKeys: newActiveKeys } = nextProps;
if (!isListEqual(oldActiveKeys, newActiveKeys)) {
this.generateActiveMap(nextProps);
}
}
getClassName = () => {
const { className } = this.props;
const classNames = [
className,
styles.options,
];
return classNames.join(' ');
}
generateActiveMap = (props) => {
const { activeKeys } = props;
this.activeKeysMap = listToMap(
activeKeys,
optionKey => optionKey,
() => true,
);
}
renderOption = (k, data) => {
const {
keySelector,
labelSelector,
onOptionClick,
onOptionFocus,
optionLabelSelector,
focusedKey,
} = this.props;
const key = keySelector(data);
const label = optionLabelSelector ? (
optionLabelSelector(data)
) : (
labelSelector(data)
);
const isActive = !!this.activeKeysMap[key];
const isFocused = key === focusedKey;
return (
<Option
key={key}
optionKey={key}
optionLabel={label}
onClick={onOptionClick}
onFocus={onOptionFocus}
active={isActive}
focused={isFocused}
/>
);
}
renderEmpty = () => {
const {
renderEmpty: EmptyComponent,
data,
} = this.props;
if (data.length > 0) {
return null;
}
const className = `empty ${styles.empty}`;
return (
<div className={className}>
<EmptyComponent />
</div>
);
}
render() {
const {
onBlur,
onInvalidate,
parentContainer,
data,
show,
} = this.props;
const className = this.getClassName();
const Empty = this.renderEmpty;
if (!show) {
return null;
}
return (
<FloatingContainer
onBlur={onBlur}
onInvalidate={onInvalidate}
parent={parentContainer}
className={className}
>
<List
data={data}
modifier={this.renderOption}
/>
<Empty />
</FloatingContainer>
);
}
}