UNPKG

react-token

Version:
472 lines (416 loc) 16 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _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; }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _token = require('./token'); var _token2 = _interopRequireDefault(_token); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var ReactToken = function (_React$Component) { _inherits(ReactToken, _React$Component); function ReactToken(props) { _classCallCheck(this, ReactToken); var _this = _possibleConstructorReturn(this, (ReactToken.__proto__ || Object.getPrototypeOf(ReactToken)).call(this, props)); _this.keyMap = { BACKSPACE: 8, DOWN_ARROW: 40, ENTER: 13, ESC: 27, UP_ARROW: 38 }; _this.onClick = function (e) { if (!_this.container.contains(e.target)) { var inputValue = _this.state.inputValue; var tokens = _this.state.tokens; var val = _this.input.value; if (val) { tokens = [].concat(_toConsumableArray(tokens), [val]); _this.input.value = ''; inputValue = ''; } if (_this.state.tokens.length !== tokens.length) { _this.props.onAdd(val); } _this.setState({ focused: false, hideAutocomplete: true, tokens: tokens, inputValue: inputValue }); } }; _this.focusInput = function (e) { _this.input.focus(); }; _this.onFocus = function (e) { _this.setState({ focused: true }, function () { _this.props.onFocus(e); }); }; _this.onBlur = function (e) { var shouldRenderAutocomplete = _this.state.inputValue !== '' && _this.props.autocompleteOptions.length > 0 && !_this.state.hideAutocomplete; if (shouldRenderAutocomplete) { return; } var inputValue = _this.state.inputValue; var tokens = _this.state.tokens; var val = _this.input.value; if (val) { tokens = [].concat(_toConsumableArray(tokens), [val]); _this.input.value = ''; inputValue = ''; } _this.setState({ focused: false, tokens: tokens, inputValue: inputValue }); _this.props.onBlur(e); if (_this.state.tokens.length !== tokens.length) { _this.props.onAdd(val); } }; _this.onKeyDown = function (e) { if ([_this.keyMap.DOWN_ARROW, _this.keyMap.UP_ARROW, _this.keyMap.ENTER].includes(e.keyCode)) { e.preventDefault(); e.stopPropagation(); } }; _this.onKeyUp = function (e) { e.preventDefault(); if (e.keyCode === _this.keyMap.ENTER && _this.input.value.length > 0) { var value = _this.state.selectedAutocompleteIdx >= 0 ? _this.props.autocompleteOptions[_this.state.selectedAutocompleteIdx] : _this.input.value; _this.setState({ selectedAutocompleteIdx: -1, tokens: [].concat(_toConsumableArray(_this.state.tokens), [value]), inputValue: '', hideAutocomplete: false }); _this.props.onAdd(value); _this.input.value = ''; _this.input.focus(); } else if (e.keyCode === _this.keyMap.BACKSPACE && _this.state.inputValue.length === 0) { _this.removeToken(_this.state.tokens.length - 1)(); _this.setState({ inputValue: '' }); } else if (e.keyCode === _this.keyMap.ESC) { _this.setState({ hideAutocomplete: true }); _this.input.focus(); } else if (e.keyCode === _this.keyMap.DOWN_ARROW && _this.state.inputValue !== '') { var nextIdx = _this.state.selectedAutocompleteIdx + 1 >= _this.props.autocompleteOptions.length ? 0 : _this.state.selectedAutocompleteIdx + 1; _this.setState({ selectedAutocompleteIdx: nextIdx }); _this.suggestions.childNodes[nextIdx].focus(); } else if (e.keyCode === _this.keyMap.UP_ARROW && _this.state.inputValue !== '') { var _nextIdx = _this.state.selectedAutocompleteIdx - 1 < 0 ? _this.props.autocompleteOptions.length - 1 : _this.state.selectedAutocompleteIdx - 1; _this.setState({ selectedAutocompleteIdx: _nextIdx }); _this.suggestions.childNodes[_nextIdx].focus(); } else { _this.setState({ inputWidth: _this.calcWidth(_this.input.value), inputValue: _this.input.value, hideAutocomplete: false, selectedAutocompleteIdx: -1 }); } if (e.target === _this.input) { _this.props.onKeyUp(e); } }; _this.highlightOption = function (idx) { return function () { _this.setState({ selectedAutocompleteIdx: idx }); }; }; _this.selectAutoCompleteOption = function (idx) { return function () { var option = _this.props.autocompleteOptions[idx]; _this.props.onAdd(option); _this.input.value = ''; // If autocomplete option is a react element then don't automatically add to // the selected token list. Consumer should be responsible for updating the // selected prop with a text value. var tokens = (typeof option === 'undefined' ? 'undefined' : _typeof(option)) === 'object' ? _this.state.tokens : [].concat(_toConsumableArray(_this.state.tokens), [_this.props.autocompleteOptions[idx]]); _this.setState({ hideAutocomplete: false, inputValue: '', selectedAutocompleteIdx: -1, tokens: tokens }); }; }; _this.removeToken = function (index) { return function () { if (_this.props.disabled) { return null; } var removedToken = _this.state.tokens[index]; var tokens = _this.state.tokens; tokens.splice(index, 1); _this.setState({ tokens: tokens }); _this.props.onRemove(removedToken); }; }; _this.state = { focused: false, hideAutocomplete: false, inputValue: '', inputWidth: null, selectedAutocompleteIdx: -1, tokens: _this.props.selected }; return _this; } _createClass(ReactToken, [{ key: 'componentDidMount', value: function componentDidMount() { window.addEventListener('click', this.onClick); } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { if (this.props.selected !== nextProps.selected) { this.setState({ tokens: nextProps.selected }); } } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { window.removeEventListener('click', this.onClick); } }, { key: 'calcWidth', value: function calcWidth(val) { var tmp = document.createElement('div'); tmp.style.padding = '0'; tmp.style.width = ''; tmp.style.position = 'absolute'; tmp.style.left = '-9999'; tmp.style.fontFamily = '"Sofia Pro", "Helvetica", "Arial", sans-serif'; tmp.style.fontSize = '11px'; tmp.innerText = val; this.input.parentNode.appendChild(tmp); var width = tmp.clientWidth; this.input.parentNode.removeChild(tmp); return width + 40; } }, { key: 'renderRequired', value: function renderRequired() { var styles = { container: { color: '#ACACAC', fontSize: 12, fontWeight: 200, overflow: 'hidden', position: 'absolute', right: 10, top: 14 }, span: { position: 'relative', transition: 'all 200ms', top: 0 } }; var spanStyle = this.state.focused || this.state.tokens.length > 0 ? _extends({}, styles.span, { top: -20 }) : styles.span; return _react2.default.createElement( 'div', { style: styles.container }, _react2.default.createElement( 'span', { style: spanStyle }, 'required' ) ); } }, { key: 'renderTokens', value: function renderTokens() { var _this2 = this; return this.state.tokens.map(function (token, idx) { return _react2.default.createElement(_token2.default, { text: token, key: idx, onRemove: _this2.removeToken(idx) }); }); } }, { key: 'renderAutoComplete', value: function renderAutoComplete() { var _this3 = this; var styles = { container: { position: 'absolute', marginTop: '5px', left: 0, paddingTop: 10, paddingBottom: 10, fontSize: 14, fontWeight: 600, letterSpacing: 0, backgroundColor: '#F1F1F1', color: '#000000', width: '100%', borderRadius: '3px', cursor: 'pointer', maxHeight: 160, overflowY: 'auto', border: '1px solid #E4E4E4', zIndex: 1 }, option: { paddingLeft: 10, paddingRight: 10, paddingTop: 5, paddingBottom: 5, border: 'none', outline: 'none' }, highlighted: { backgroundColor: '#E4E2E2' } }; return _react2.default.createElement( 'div', { style: styles.container, ref: function ref(r) { return _this3.suggestions = r; }, className: 'rt-suggestions', role: 'listbox', onKeyDown: this.onKeyDown, onKeyUp: this.onKeyUp }, this.props.autocompleteOptions.map(function (option, idx) { var style = _this3.state.selectedAutocompleteIdx === idx ? _extends({}, styles.option, styles.highlighted) : styles.option; return _react2.default.createElement( 'div', { className: 'rt-option', key: 'autocomplete-' + idx, onClick: _this3.selectAutoCompleteOption(idx), onMouseOver: _this3.highlightOption(idx), role: 'option', style: style, tabIndex: -1 }, option ); }) ); } }, { key: 'render', value: function render() { var _this4 = this; var width = this.state.inputValue !== '' ? this.state.inputWidth : '100%'; var styles = { container: { position: 'relative', backgroundColor: '#F1F1F1', border: '1px solid #F1F1F1', borderRadius: '3px', width: '100%', fontFamily: '"Sofia Pro", "Helvetica", "Arial", sans-serif' }, ul: { cursor: 'text', display: 'flex', flexWrap: 'wrap', fontSize: 14, fontWeight: 600, letterSpacing: 0, listStyle: 'none', lineHeight: 'normal', margin: 0, padding: '4px 6px', position: 'relative', willChange: 'transform' }, li: { display: 'inline-flex' }, inputContainer: { display: 'inline-flex', position: 'relative' }, input: { backgroundColor: '#F1F1F1', fontFamily: '"Sofia Pro", "Helvetica", "Arial", sans-serif', paddingTop: 1, paddingLeft: 1, paddingBottom: 1, paddingRight: 20, width: width, border: 'none', outline: 'none', marginTop: 9, marginBottom: 9, boxSizing: 'border-box' } }; var shouldRenderAutocomplete = this.state.inputValue !== '' && this.props.autocompleteOptions.length > 0 && !this.state.hideAutocomplete; return _react2.default.createElement( 'div', { style: styles.container, className: 'rt-container', ref: function ref(r) { return _this4.container = r; } }, _react2.default.createElement( 'ul', { style: styles.ul, onClick: this.focusInput }, this.renderTokens(), _react2.default.createElement( 'li', { style: styles.li }, _react2.default.createElement( 'div', { style: styles.inputContainer }, _react2.default.createElement('input', { disabled: this.props.disabled, ref: function ref(r) { return _this4.input = r; }, type: 'text', role: 'combobox', className: 'rt-input', style: styles.input, onKeyDown: this.onKeyDown, onKeyUp: this.onKeyUp, placeholder: this.props.placeholder, onFocus: this.onFocus, onBlur: this.onBlur }) ) ), this.props.required && this.renderRequired() ), shouldRenderAutocomplete && this.renderAutoComplete() ); } }]); return ReactToken; }(_react2.default.Component); ReactToken.propTypes = { autocompleteOptions: _propTypes2.default.array, disabled: _propTypes2.default.bool, onAdd: _propTypes2.default.func, onBlur: _propTypes2.default.func, onFocus: _propTypes2.default.func, onKeyUp: _propTypes2.default.func, onRemove: _propTypes2.default.func, placeholder: _propTypes2.default.string, required: _propTypes2.default.bool, selected: _propTypes2.default.array }; ReactToken.defaultProps = { autocompleteOptions: [], disabled: false, onAdd: function onAdd() {}, onBlur: function onBlur() {}, onFocus: function onFocus() {}, onKeyUp: function onKeyUp() {}, onRemove: function onRemove() {}, placeholder: '', required: false, selected: [] }; exports.default = ReactToken; module.exports = exports['default'];