@crave/farmblocks-input-select
Version:
Select box form component
293 lines (254 loc) • 9.46 kB
JavaScript
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
import * as React from "react";
import ReactAutocomplete from "react-autocomplete";
import PropTypes from "prop-types";
import { compose } from "recompose";
import memoize from "memoize-one";
import groupBy from "lodash.groupby";
import xor from "lodash.xor";
import formInput, { formInputProps } from "@crave/farmblocks-hoc-input";
import { DropdownMenuWrapper, DropdownItemWrapper } from "@crave/farmblocks-dropdown";
import Tag from "@crave/farmblocks-tags";
import withImage, { refName } from "./components/withImage";
import Item from "./components/Item";
import EmptyCard from "./components/EmptyCard";
import DropdownWrapper from "./styledComponents/DropdownWrapper";
import InputWithTags from "./components/InputWithTags";
const EnhancedInput = compose(formInput, withImage)(InputWithTags);
EnhancedInput.displayName = "EnhancedInput";
const getValues = ({
multi,
value
}) => {
if (!multi) return value;
if (Array.isArray(value)) return value;
if (value === undefined) return [];
return [value];
};
class Select extends React.Component {
constructor(_props) {
super(_props);
_defineProperty(this, "componentDidUpdate", prevProps => {
const {
value
} = this.props;
if ((value || value === 0) && value !== prevProps.value && value !== this.state.selectedValue || prevProps.items !== this.props.items) {
this.setState({
selectedValue: value,
selectedLabel: this.getSelectedLabel(this.props)
});
}
});
_defineProperty(this, "onMenuVisibilityChange", isMenuOpen => {
var _this$props$onMenuVis, _this$props;
if (!this.props.disableSearch && isMenuOpen && this.input) {
this.input.select();
}
this.setState({
isMenuOpen
});
(_this$props$onMenuVis = (_this$props = this.props).onMenuVisibilityChange) === null || _this$props$onMenuVis === void 0 ? void 0 : _this$props$onMenuVis.call(_this$props, isMenuOpen);
});
_defineProperty(this, "onFilter", event => {
if (!this.state.isSearching && !this.props.multi) {
this.props.onChange("");
}
this.setState({
selectedLabel: event.target.value,
isSearching: true
});
});
_defineProperty(this, "onSelect", (selectedLabel, item) => {
const {
onChange
} = this.props;
this.setState({
selectedLabel,
isSearching: false
});
if (this.props.multi) {
onChange(xor(this.state.selectedValue, [item.value]));
return;
}
onChange(item.value);
});
_defineProperty(this, "onRemoveTag", value => this.onSelect("", {
value
}));
_defineProperty(this, "onKeyDown", autoCompleteOnKeyDown => event => {
const {
value
} = this.props;
if (this.props.multi && event.key === "Backspace" && !this.state.selectedLabel) {
var _value$slice;
const lastTagValue = Array.isArray(value) && ((_value$slice = value.slice(-1)) === null || _value$slice === void 0 ? void 0 : _value$slice[0]);
if (lastTagValue) this.onRemoveTag(lastTagValue);
}
autoCompleteOnKeyDown === null || autoCompleteOnKeyDown === void 0 ? void 0 : autoCompleteOnKeyDown(event);
});
_defineProperty(this, "getSelectedLabel", props => {
const item = (props.value || props.value === 0) && props.items.find(x => x.value === props.value);
if (item) {
return item.label;
}
});
_defineProperty(this, "normalizeItems", memoize(items => groupBy(items, "value")));
_defineProperty(this, "renderTags", () => {
var _getValues;
const items = this.normalizeItems(this.props.items);
return (_getValues = getValues(this.props)) === null || _getValues === void 0 ? void 0 : _getValues.map(value => {
var _items$value;
const item = (_items$value = items[value]) === null || _items$value === void 0 ? void 0 : _items$value[0];
if (!item) return null;
return /*#__PURE__*/React.createElement(Tag, {
className: "tag",
key: item.value,
value: item.value,
text: item.label,
onRemove: this.onRemoveTag,
disabled: this.props.disabled
});
});
});
_defineProperty(this, "renderInput", autoCompleteProps => {
var _getValues2;
const {
ref,
...rest
} = autoCompleteProps;
const {
renderItem,
disableSearch,
items,
zIndex,
maxHeight,
multi,
placeholder,
className = "",
onMenuVisibilityChange,
...inputProps
} = this.props;
inputProps.validationMessages = this.state.isMenuOpen ? [] : this.props.validationMessages;
const selectedItem = items.find(item => item.label === autoCompleteProps.value);
const image = selectedItem && selectedItem.image;
return /*#__PURE__*/React.createElement(EnhancedInput, _extends({
className: `select__search ${className}`,
readOnly: disableSearch,
placeholder: (_getValues2 = getValues(this.props)) !== null && _getValues2 !== void 0 && _getValues2.length ? "" : placeholder
}, inputProps, rest, {
innerRef: ref,
refName: refName,
image: image,
onKeyDown: this.onKeyDown(rest.onKeyDown),
mb: 0
}), multi && this.renderTags());
});
_defineProperty(this, "renderMenu", items => {
const {
noResultsMessage,
maxHeight
} = this.props;
if (!(items !== null && items !== void 0 && items.length)) {
return /*#__PURE__*/React.createElement(EmptyCard, {
className: "emptyCard",
noResultsMessage: noResultsMessage
});
}
return /*#__PURE__*/React.createElement(DropdownMenuWrapper, {
className: "dropdownMenu",
maxHeight: maxHeight
}, /*#__PURE__*/React.createElement("ul", null, items));
});
_defineProperty(this, "renderItem", (item, highlighted) => {
const {
selectedValue
} = this.state;
const selected = Array.isArray(selectedValue) ? selectedValue.includes(item.value) : selectedValue === item.value;
return /*#__PURE__*/React.createElement(DropdownItemWrapper, {
className: "itemWrapper",
key: item.value,
highlighted: highlighted,
selected: selected
}, this.props.renderItem ? this.props.renderItem(item, selected) : /*#__PURE__*/React.createElement(Item, {
className: "item",
label: item.label,
id: this.props.id && `${this.props.id}-item-${item.value}`,
image: item.image,
selected: selected,
checkbox: this.props.multi
}));
});
_defineProperty(this, "shouldItemRender", item => {
const {
selectedLabel
} = this.state;
if (this.state.isSearching) {
return item.label.toLowerCase().indexOf(selectedLabel && selectedLabel.toLowerCase()) > -1;
} // If user is not searching, we render the full list of items
return true;
});
this.state = {
selectedValue: getValues(_props),
selectedLabel: this.getSelectedLabel(_props),
isSearching: false,
isMenuOpen: false
};
}
render() {
const {
width,
zIndex,
items,
className
} = this.props;
return /*#__PURE__*/React.createElement(DropdownWrapper, {
className: className,
width: width,
zIndex: zIndex
}, /*#__PURE__*/React.createElement(ReactAutocomplete, {
items: items,
shouldItemRender: this.shouldItemRender,
getItemValue: item => item.label,
value: this.state.selectedLabel,
onChange: this.onFilter,
onSelect: this.onSelect,
renderInput: this.renderInput,
renderMenu: this.renderMenu,
renderItem: this.renderItem,
autoHighlight: false,
onMenuVisibilityChange: this.onMenuVisibilityChange,
wrapperStyle: {},
ref: ref => {
this.input = ref;
}
}));
}
}
Select.defaultProps = {
onChange: () => false,
width: "200px",
items: []
};
Select.propTypes = { ...formInputProps,
items: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
label: PropTypes.string,
image: PropTypes.string
})),
value: (props, ...rest) => {
const valueTypes = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
return (props.multi ? PropTypes.arrayOf(valueTypes) : valueTypes)(props, ...rest);
},
width: PropTypes.string,
onChange: PropTypes.func,
renderItem: PropTypes.func,
noResultsMessage: PropTypes.string,
disableSearch: PropTypes.bool,
zIndex: PropTypes.number,
maxHeight: PropTypes.string,
multi: PropTypes.bool,
className: PropTypes.string,
onMenuVisibilityChange: PropTypes.func
};
export default Select;