UNPKG

lucid-ui

Version:

A UI component library from AppNexus.

273 lines (246 loc) 10.9 kB
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 };