lucid-ui
Version:
A UI component library from AppNexus.
273 lines (246 loc) • 10.9 kB
JavaScript
import _map from "lodash/map";
import _omit from "lodash/omit";
import _escapeRegExp from "lodash/escapeRegExp";
import _get from "lodash/get";
import _isEmpty from "lodash/isEmpty";
import _noop from "lodash/noop";
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
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 _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import React from 'react';
import PropTypes from 'react-peek/prop-types';
import { createClass } from '../../util/component-types';
import { lucidClassNames } from '../../util/style-helpers';
import { buildHybridComponent } from '../../util/state-management';
import { partitionText } from '../../util/text-manipulation';
import * as reducers from './Autocomplete.reducers';
import * as KEYCODE from '../../constants/key-code';
import { DropMenuDumb as DropMenu } from '../DropMenu/DropMenu';
var cx = lucidClassNames.bind('&-Autocomplete');
var arrayOf = PropTypes.arrayOf,
bool = PropTypes.bool,
func = PropTypes.func,
object = PropTypes.object,
shape = PropTypes.shape,
string = PropTypes.string;
var Autocomplete = createClass({
statics: {},
displayName: 'Autocomplete',
reducers: reducers,
// TODO: typescript hack that should be removed
propTypes: {
className: string,
style: object,
isDisabled: bool,
suggestions: arrayOf(string),
value: string,
DropMenu: shape(DropMenu.propTypes),
onChange: func,
onSelect: func,
onExpand: func
},
// TODO: typescript hack that should be removed
getDefaultProps: function getDefaultProps() {
return {
isDisabled: false,
suggestions: [],
value: '',
onChange: _noop,
onSelect: _noop,
onExpand: _noop,
DropMenu: DropMenu.defaultProps
}; // TODO: typescript hack that should be removed
},
handleSelect: function handleSelect(optionIndex, _ref) {
var event = _ref.event;
var _this$props = this.props,
suggestions = _this$props.suggestions,
onChange = _this$props.onChange,
onSelect = _this$props.onSelect;
onChange(suggestions[optionIndex], {
event: event,
props: this.props
});
onSelect(optionIndex, {
event: event,
props: this.props
});
},
handleInput: function handleInput(event) {
var _this$props2 = this.props,
onChange = _this$props2.onChange,
onExpand = _this$props2.onExpand,
onCollapse = _this$props2.DropMenu.onCollapse;
onChange(event.target.value, {
event: event,
props: this.props
});
if (!_isEmpty(event.target.value)) {
onExpand({
event: event,
props: this.props
});
} else {
onCollapse();
}
},
getInputValue: function getInputValue() {
return _get(this, 'inputRef.value', this.props.value);
},
setInputValue: function setInputValue(value) {
if (this.inputRef) {
this.inputRef.value = value;
}
},
handleInputKeydown: function handleInputKeydown(event) {
var _this$props3 = this.props,
onExpand = _this$props3.onExpand,
_this$props3$DropMenu = _this$props3.DropMenu,
isExpanded = _this$props3$DropMenu.isExpanded,
focusedIndex = _this$props3$DropMenu.focusedIndex,
onCollapse = _this$props3$DropMenu.onCollapse;
var value = this.getInputValue();
if (event.keyCode === KEYCODE.Tab && isExpanded && focusedIndex !== null) {
this.handleSelect(focusedIndex, {
event: event,
props: this.props
});
event.preventDefault();
}
if (event.keyCode === KEYCODE.ArrowDown && !isExpanded) {
event.stopPropagation();
if (_isEmpty(value)) {
onExpand({
event: event,
props: this.props
});
}
}
if (event.keyCode === KEYCODE.Escape) {
event.stopPropagation();
onCollapse(event);
}
if (event.keyCode === KEYCODE.Enter && focusedIndex === null) {
event.stopPropagation();
onCollapse(event);
}
},
handleControlClick: function handleControlClick(event) {
var _this$props4 = this.props,
onExpand = _this$props4.onExpand,
_this$props4$DropMenu = _this$props4.DropMenu,
isExpanded = _this$props4$DropMenu.isExpanded,
onCollapse = _this$props4$DropMenu.onCollapse;
if (event.target === this.inputRef) {
onExpand({
event: event,
props: this.props
});
} else {
if (isExpanded) {
onCollapse(event);
} else {
onExpand({
event: event,
props: this.props
});
}
this.inputRef.focus();
}
},
componentDidMount: function componentDidMount() {
var value = this.props.value;
this.inputRef.addEventListener('input', this.handleInput);
this.setInputValue(value);
},
UNSAFE_componentWillReceiveProps: function UNSAFE_componentWillReceiveProps(nextProps) {
// TODO: typescript hack that should be removed
var value = nextProps.value;
if (value !== this.getInputValue()) {
this.setInputValue(value);
}
},
componentWillUnmount: function componentWillUnmount() {
if (this.inputRef) {
this.inputRef.removeEventListener('input', this.handleInput);
}
},
render: function render() {
var _this = this;
var _ref2 = this.props,
style = _ref2.style,
className = _ref2.className,
isDisabled = _ref2.isDisabled,
dropMenuProps = _ref2.DropMenu,
suggestions = _ref2.suggestions,
passThroughs = _objectWithoutProperties(_ref2, ["style", "className", "isDisabled", "DropMenu", "suggestions"]); // TODO: typescript hack that should be removed
var isExpanded = dropMenuProps.isExpanded;
var value = this.getInputValue();
var valuePattern = new RegExp(_escapeRegExp(value), 'i');
return /*#__PURE__*/React.createElement(DropMenu, _extends({}, dropMenuProps, {
isDisabled: isDisabled,
selectedIndices: [],
className: cx('&', className),
onSelect: this.handleSelect,
style: style
}), /*#__PURE__*/React.createElement(DropMenu.Control, {
onClick: this.handleControlClick
}, /*#__PURE__*/React.createElement("div", {
className: cx('&-Control', {
'&-Control-is-expanded': isExpanded,
'&-Control-is-disabled': isDisabled
})
}, /*#__PURE__*/React.createElement("input", _extends({}, _omit(passThroughs, ['onChange', 'onSelect', 'onExpand', 'value', 'children']), {
// TODO: typescript hack that should be removed
type: "text",
className: cx('&-Control-input'),
ref: function ref(_ref3) {
return _this.inputRef = _ref3;
},
onKeyDown: this.handleInputKeydown,
disabled: isDisabled
})))), value ? _map(suggestions, function (suggestion) {
return /*#__PURE__*/React.createElement(DropMenu.Option, {
key: 'AutocompleteOption' + suggestion
}, function () {
var _partitionText = partitionText(suggestion, valuePattern, value.length),
_partitionText2 = _slicedToArray(_partitionText, 3),
pre = _partitionText2[0],
match = _partitionText2[1],
post = _partitionText2[2];
var formattedSuggestion = [];
if (pre) {
formattedSuggestion.push( /*#__PURE__*/React.createElement("span", {
key: "AutocompleteOption-suggestion-pre-".concat(suggestion),
className: cx('&-Option-suggestion-pre')
}, pre));
}
if (match) {
formattedSuggestion.push( /*#__PURE__*/React.createElement("span", {
key: "AutocompleteOption-suggestion-match-".concat(suggestion),
className: cx('&-Option-suggestion-match')
}, match));
}
if (post) {
formattedSuggestion.push( /*#__PURE__*/React.createElement("span", {
key: "AutocompleteOption-suggestion-post-".concat(suggestion),
className: cx('&-Option-suggestion-post')
}, post));
}
return formattedSuggestion;
}());
}) : _map(suggestions, function (suggestion) {
return /*#__PURE__*/React.createElement(DropMenu.Option, {
key: 'AutocompleteOption' + suggestion
}, suggestion);
}));
}
});
export default buildHybridComponent(Autocomplete);
export { Autocomplete as AutocompleteDumb };