react-bootstrap-typeahead
Version:
React typeahead with Bootstrap styling
112 lines (111 loc) • 5.23 kB
JavaScript
import cx from 'classnames';
import PropTypes from 'prop-types';
import React, { forwardRef } from 'react';
import Typeahead from '../../core/Typeahead';
import ClearButton from '../ClearButton';
import Loader from '../Loader';
import Overlay from '../Overlay';
import RootClose from '../RootClose';
import Token from '../Token/Token';
import TypeaheadInputMulti from '../TypeaheadInputMulti';
import TypeaheadInputSingle from '../TypeaheadInputSingle';
import TypeaheadMenu from '../TypeaheadMenu';
import { getOptionLabel, isFunction, isSizeLarge, pick, preventInputBlur, } from '../../utils';
import { checkPropType, inputPropsType, sizeType } from '../../propTypes';
const propTypes = {
clearButton: PropTypes.bool,
inputProps: checkPropType(PropTypes.object, inputPropsType),
isInvalid: PropTypes.bool,
isLoading: PropTypes.bool,
isValid: PropTypes.bool,
renderInput: PropTypes.func,
renderMenu: PropTypes.func,
renderToken: PropTypes.func,
size: sizeType,
};
const defaultProps = {
isLoading: false,
};
const defaultRenderMenu = (results, menuProps, props) => (React.createElement(TypeaheadMenu, { ...menuProps, labelKey: props.labelKey, options: results, text: props.text }));
const defaultRenderToken = (option, props, idx) => (React.createElement(Token, { disabled: props.disabled, key: idx, onRemove: props.onRemove, option: option, tabIndex: props.tabIndex }, getOptionLabel(option, props.labelKey)));
const overlayPropKeys = [
'align',
'dropup',
'flip',
'positionFixed',
];
function getOverlayProps(props) {
return pick(props, overlayPropKeys);
}
class TypeaheadComponent extends React.Component {
static propTypes = propTypes;
static defaultProps = defaultProps;
_referenceElement = null;
render() {
const { children, className, instanceRef, open, options, style } = this.props;
return (React.createElement(Typeahead, { ...this.props, options: options, ref: instanceRef }, (props) => {
const { hideMenu, isMenuShown, results } = props;
const auxContent = this._renderAux(props);
return (React.createElement(RootClose, { disabled: open || !isMenuShown, onRootClose: hideMenu }, (ref) => (React.createElement("div", { className: cx('rbt', {
'has-aux': !!auxContent,
'is-invalid': this.props.isInvalid,
'is-valid': this.props.isValid,
}, className), ref: ref, style: {
...style,
outline: 'none',
position: 'relative',
}, tabIndex: -1 },
this._renderInput({
...props.getInputProps(this.props.inputProps),
referenceElementRef: this.referenceElementRef,
}, props),
React.createElement(Overlay, { ...getOverlayProps(this.props), isMenuShown: isMenuShown, referenceElement: this._referenceElement }, (menuProps) => this._renderMenu(results, menuProps, props)),
auxContent,
isFunction(children) ? children(props) : children))));
}));
}
referenceElementRef = (referenceElement) => {
this._referenceElement = referenceElement;
};
_renderInput = (inputProps, props) => {
const { isInvalid, isValid, multiple, renderInput, renderToken, size } = this.props;
if (isFunction(renderInput)) {
return renderInput(inputProps, props);
}
const commonProps = {
...inputProps,
isInvalid,
isValid,
size,
};
if (!multiple) {
return React.createElement(TypeaheadInputSingle, { ...commonProps });
}
const { labelKey, onRemove, selected } = props;
return (React.createElement(TypeaheadInputMulti, { ...commonProps, placeholder: selected.length ? '' : inputProps.placeholder, selected: selected }, selected.map((option, idx) => (renderToken || defaultRenderToken)(option, { ...commonProps, labelKey, onRemove }, idx))));
};
_renderMenu = (results, menuProps, props) => {
const { emptyLabel, id, maxHeight, newSelectionPrefix, paginationText, renderMenu, renderMenuItemChildren, } = this.props;
return (renderMenu || defaultRenderMenu)(results, {
...menuProps,
emptyLabel,
id,
maxHeight,
newSelectionPrefix,
paginationText,
renderMenuItemChildren,
}, props);
};
_renderAux = ({ onClear, selected }) => {
const { clearButton, disabled, isLoading, size } = this.props;
let content;
if (isLoading) {
content = React.createElement(Loader, null);
}
else if (clearButton && !disabled && selected.length) {
content = (React.createElement(ClearButton, { onClick: onClear, onMouseDown: preventInputBlur, size: size }));
}
return content ? (React.createElement("div", { className: cx('rbt-aux', { 'rbt-aux-lg': isSizeLarge(size) }) }, content)) : null;
};
}
export default forwardRef((props, ref) => (React.createElement(TypeaheadComponent, { ...props, instanceRef: ref })));