diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..b0c0c8b --- /dev/null +++ b/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "airbnb" +} diff --git a/.gitignore b/.gitignore index 6c56e6e..8b5fa29 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules tmp example/bundle.js +.idea/ diff --git a/css-default-theme.js b/css-default-theme.js new file mode 100644 index 0000000..efbc3df --- /dev/null +++ b/css-default-theme.js @@ -0,0 +1 @@ +require('./lib/css/theme-default.css'); \ No newline at end of file diff --git a/dist/react-tokeninput.css b/dist/react-tokeninput.css new file mode 100644 index 0000000..9569e1c --- /dev/null +++ b/dist/react-tokeninput.css @@ -0,0 +1,129 @@ +.ic-tokeninput { + display: inline-block; +} + +.ic-tokeninput-list { + display: none; + position: absolute; + z-index: 1; + top: 100%; + left: 0; + padding: 5px 0px; + max-height: 400px; + overflow: auto; + font-size: 12px; + font-family: sans-serif; + width: 100%; + box-sizing: border-box; +} + +.ic-tokeninput-is-open .ic-tokeninput-list { + display: block; +} + +.ic-tokeninput-option:focus { + outline: 0; +} + +.ic-tokeninput-input { + padding-right: 20px; + width: 100%; + box-sizing: border-box; + border: 0 none; + outline: 0; + margin-top: 6px; + margin-bottom: 6px; + background-color: transparent; +} + +.ic-token-label { + line-height: 20px; +} + +.ic-tokeninput-button { + display: inline-block; + position: absolute; + cursor: default; + outline: none; + top: 2px; + right: 6px; + font-size: 14px; +} + +.ic-tokeninput-option { + display: block; + padding: 2px 16px; + cursor: default; +} + +.ic-tokeninput-selected:before { + content: '\2713'; + position: absolute; + left: 4px; +} + +.ic-tokens { + box-sizing: border-box; + position: relative; + z-index: 1; + list-style: none; + padding: 6px 6px; + cursor: text; + will-change: transform; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + min-height: 41px; +} + +.ic-tokens-disabled { + cursor: default; +} + +.ic-token { + border-radius: 3px; + padding: 3px 8px; + margin-right: 6px; + margin-top: 2px; + margin-bottom: 2px; +} + +.ic-token-delete-button { + font-size: 1rem; + margin-right: 5px; + cursor: pointer; +} + +.ic-tokeninput { + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.ic-tokeninput-button { + display: none; +} + +.ic-tokeninput-loading { + position: absolute; + bottom: 9px; + right: 2px; + -webkit-align-items: center; + -ms-flex-align: center; + -ms-grid-row-align: center; + align-items: center; + padding-right: 6px; +} + +.ic-tokeninput-flex { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} + +.ic-tokeninput-inline-flex { + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} +/*# sourceMappingURL=react-tokeninput.css.map*/ \ No newline at end of file diff --git a/dist/react-tokeninput.js b/dist/react-tokeninput.js index 6afeabf..0c08264 100644 --- a/dist/react-tokeninput.js +++ b/dist/react-tokeninput.js @@ -1,13 +1,13 @@ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(require("react")); + module.exports = factory(require("react"), require("classnames")); else if(typeof define === 'function' && define.amd) - define(["react"], factory); + define(["react", "classnames"], factory); else if(typeof exports === 'object') - exports["TokenInput"] = factory(require("react")); + exports["TokenInput"] = factory(require("react"), require("classnames")); else - root["TokenInput"] = factory(root["React"]); -})(this, function(__WEBPACK_EXTERNAL_MODULE_2__) { + root["TokenInput"] = factory(root["React"], root["classnames"]); +})(this, function(__WEBPACK_EXTERNAL_MODULE_7__, __WEBPACK_EXTERNAL_MODULE_9__) { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; @@ -54,23211 +54,918 @@ return /******/ (function(modules) { // webpackBootstrap /* 0 */ /***/ function(module, exports, __webpack_require__) { - __webpack_require__(1); - module.exports = __webpack_require__(149); + module.exports = __webpack_require__(1); /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { - var React = __webpack_require__(2) - var ReactDOM = __webpack_require__(3) - var TokenInput = __webpack_require__(149) - var ComboboxOption = __webpack_require__(149).Option - - var without = __webpack_require__(156) - var uniq = __webpack_require__(169) - var names = __webpack_require__(194) - - var App = React.createClass({displayName: "App", - getInitialState: function() { - return { - input: '', - loading: false, - selected: [], - options: names - }; - }, - - handleChange: function(value) { - this.setState({ - selected: value - }) - }, - - handleRemove: function(value) { - var selectedOptions = uniq(without(this.state.selected,value)) - this.handleChange(selectedOptions) - }, - - handleSelect: function(value, combobox) { - if(typeof value === 'string') { - value = {id: value, name: value}; - } - - var selected = uniq(this.state.selected.concat([value])) - this.setState({ - selected: selected, - selectedToken: null - }) - - this.handleChange(selected) - }, - - handleInput: function(userInput) { - this.setState({ - input: userInput, - loading: true, - options: [] - }) - setTimeout(function () { - this.filterTags(this.state.input) - this.setState({ - loading: false - }) - }.bind(this), 500) - }, - - filterTags: function(userInput) { - if (userInput === '') - return this.setState({options: []}); - var filter = new RegExp('^'+userInput, 'i'); - var filteredNames = names.filter(function(state) { - return filter.test(state.name); // || filter.test(state.id); - }).filter(function(state) { - return this.state.selected - .map(function(value) { return value.name }) - .indexOf(state.name) === -1 - }.bind(this)) - this.setState({ - options: filteredNames - }); - }, - - renderComboboxOptions: function() { - return this.state.options.map(function(name) { - return ( - React.createElement(ComboboxOption, { - key: name.id, - value: name - }, name.name) - ); - }); - }, - - render: function() { - var selectedNames = this.state.selected.map(function(tag) { - return React.createElement("li", {key: tag.id}, tag.name) - }) - - var options = this.state.options.length ? - this.renderComboboxOptions() : []; - - const loadingComponent = ( - React.createElement("img", {src: "spinner.gif"}) - ) + 'use strict'; - return ( - React.createElement("div", null, - React.createElement("h1", null, "React TokenInput Example"), + /* eslint no-var:0 */ - React.createElement(TokenInput, { - isLoading: this.state.loading, - loadingComponent: loadingComponent, - menuContent: options, - onChange: this.handleChange, - onInput: this.handleInput, - onSelect: this.handleSelect, - onRemove: this.handleRemove, - selected: this.state.selected, - placeholder: "Enter tokens here"} - ), + __webpack_require__(2); - React.createElement("h2", null, "Selected"), - React.createElement("ul", null, - selectedNames - ) - ) - ); - } - }) + var TokenInput = __webpack_require__(6).default; + TokenInput.Option = __webpack_require__(10).default; - ReactDOM.render(React.createElement(App, null), document.getElementById('application')) - + module.exports = TokenInput; /***/ }, /* 2 */ /***/ function(module, exports) { - module.exports = __WEBPACK_EXTERNAL_MODULE_2__; + // removed by extract-text-webpack-plugin /***/ }, -/* 3 */ +/* 3 */, +/* 4 */, +/* 5 */, +/* 6 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; - module.exports = __webpack_require__(4); - - -/***/ }, -/* 4 */ -/***/ function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(process) {/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactDOM - */ + Object.defineProperty(exports, "__esModule", { + value: true + }); - /* globals __REACT_DEVTOOLS_GLOBAL_HOOK__*/ + 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; }; }(); - 'use strict'; + var _react = __webpack_require__(7); - var ReactCurrentOwner = __webpack_require__(6); - var ReactDOMTextComponent = __webpack_require__(7); - var ReactDefaultInjection = __webpack_require__(72); - var ReactInstanceHandles = __webpack_require__(46); - var ReactMount = __webpack_require__(29); - var ReactPerf = __webpack_require__(19); - var ReactReconciler = __webpack_require__(51); - var ReactUpdates = __webpack_require__(55); - var ReactVersion = __webpack_require__(147); + var _react2 = _interopRequireDefault(_react); - var findDOMNode = __webpack_require__(92); - var renderSubtreeIntoContainer = __webpack_require__(148); - var warning = __webpack_require__(26); + var _combobox = __webpack_require__(8); - ReactDefaultInjection.inject(); + var _combobox2 = _interopRequireDefault(_combobox); - var render = ReactPerf.measure('React', 'render', ReactMount.render); + var _token = __webpack_require__(11); - var React = { - findDOMNode: findDOMNode, - render: render, - unmountComponentAtNode: ReactMount.unmountComponentAtNode, - version: ReactVersion, + var _token2 = _interopRequireDefault(_token); - /* eslint-disable camelcase */ - unstable_batchedUpdates: ReactUpdates.batchedUpdates, - unstable_renderSubtreeIntoContainer: renderSubtreeIntoContainer - }; + var _classnames = __webpack_require__(9); - // Inject the runtime into a devtools global hook regardless of browser. - // Allows for debugging when the hook is injected on the page. - /* eslint-enable camelcase */ - if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' && typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') { - __REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ - CurrentOwner: ReactCurrentOwner, - InstanceHandles: ReactInstanceHandles, - Mount: ReactMount, - Reconciler: ReactReconciler, - TextComponent: ReactDOMTextComponent - }); - } + var _classnames2 = _interopRequireDefault(_classnames); - if (process.env.NODE_ENV !== 'production') { - var ExecutionEnvironment = __webpack_require__(10); - if (ExecutionEnvironment.canUseDOM && window.top === window.self) { + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - // First check if devtools is not installed - if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') { - // If we're in Chrome or Firefox, provide a download link if not installed. - if (navigator.userAgent.indexOf('Chrome') > -1 && navigator.userAgent.indexOf('Edge') === -1 || navigator.userAgent.indexOf('Firefox') > -1) { - console.debug('Download the React DevTools for a better development experience: ' + 'https://fb.me/react-devtools'); - } - } + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - // If we're in IE8, check to see if we are in compatibility mode and provide - // information on preventing compatibility mode - var ieCompatibilityMode = document.documentMode && document.documentMode < 8; + 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; } - process.env.NODE_ENV !== 'production' ? warning(!ieCompatibilityMode, 'Internet Explorer is running in compatibility mode; please add the ' + 'following tag to your HTML to prevent this from happening: ' + '') : undefined; + 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 expectedFeatures = [ - // shims - Array.isArray, Array.prototype.every, Array.prototype.forEach, Array.prototype.indexOf, Array.prototype.map, Date.now, Function.prototype.bind, Object.keys, String.prototype.split, String.prototype.trim, + var TokenInput = function (_React$Component) { + _inherits(TokenInput, _React$Component); - // shams - Object.create, Object.freeze]; + function TokenInput(props, context) { + _classCallCheck(this, TokenInput); - for (var i = 0; i < expectedFeatures.length; i++) { - if (!expectedFeatures[i]) { - console.error('One or more ES5 shim/shams expected by React are not available: ' + 'https://fb.me/react-warning-polyfills'); - break; - } - } - } - } + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(TokenInput).call(this, props, context)); - module.exports = React; - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(5))) - -/***/ }, -/* 5 */ -/***/ function(module, exports) { - - // shim for using process in browser + _this.handleClick = _this.handleClick.bind(_this); + _this.handleInput = _this.handleInput.bind(_this); + _this.handleRemove = _this.handleRemove.bind(_this); + _this.handleRemoveLast = _this.handleRemoveLast.bind(_this); + _this.handleSelect = _this.handleSelect.bind(_this); - var process = module.exports = {}; - var queue = []; - var draining = false; - var currentQueue; - var queueIndex = -1; + _this.state = { + selectedToken: null + }; + return _this; + } - function cleanUpNextTick() { - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); + _createClass(TokenInput, [{ + key: 'handleClick', + value: function handleClick() { + // TODO: Expand combobox API for focus + this.refs['combo-li'].querySelector('input').focus(); } - } - - function drainQueue() { - if (draining) { - return; + }, { + key: 'handleInput', + value: function handleInput(inputValue) { + this.props.onInput(inputValue); } - var timeout = setTimeout(cleanUpNextTick); - draining = true; - - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; + }, { + key: 'handleSelect', + value: function handleSelect(event) { + var input = this.refs['combo-li'].querySelector('input'); + this.props.onSelect(event); + this.setState({ + selectedToken: null + }); + this.props.onInput(input.value); } - currentQueue = null; - draining = false; - clearTimeout(timeout); - } - - process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } + }, { + key: 'handleRemove', + value: function handleRemove(value) { + var input = this.refs['combo-li'].querySelector('input'); + this.props.onRemove(value); + input.focus(); } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - setTimeout(drainQueue, 0); + }, { + key: 'handleRemoveLast', + value: function handleRemoveLast() { + this.props.onRemove(this.props.selected[this.props.selected.length - 1]); } - }; + }, { + key: 'render', + value: function render() { + var _this2 = this; + + var isDisabled = this.props.isDisabled; - // v8 likes predictible objects - function Item(fun, array) { - this.fun = fun; - this.array = array; - } - Item.prototype.run = function () { - this.fun.apply(null, this.array); - }; - process.title = 'browser'; - process.browser = true; - process.env = {}; - process.argv = []; - process.version = ''; // empty string to avoid regexp issues - process.versions = {}; - function noop() {} + var tokens = this.props.selected.map(function (token) { + return _react2.default.createElement(_token2.default, { + onRemove: _this2.handleRemove, + value: token, + name: token.name, + key: token.id + }); + }); - process.on = noop; - process.addListener = noop; - process.once = noop; - process.off = noop; - process.removeListener = noop; - process.removeAllListeners = noop; - process.emit = noop; + var classes = (0, _classnames2.default)('ic-tokens ic-tokeninput-flex', { + 'ic-tokens-disabled': isDisabled + }); - process.binding = function (name) { - throw new Error('process.binding is not supported'); - }; + var loadingWidget = this.props.isLoading && _react2.default.createElement( + 'li', + { className: 'ic-tokeninput-loading ic-tokeninput-flex' }, + this.props.loadingComponent + ); - process.cwd = function () { return '/' }; - process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); - }; - process.umask = function() { return 0; }; - - -/***/ }, -/* 6 */ -/***/ function(module, exports) { - - /** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactCurrentOwner - */ + return _react2.default.createElement( + 'ul', + { className: classes, onClick: this.handleClick }, + tokens, + _react2.default.createElement( + 'li', + { className: 'ic-tokeninput-inline-flex', ref: 'combo-li' }, + _react2.default.createElement( + _combobox2.default, + { + id: this.props.id, + ariaLabel: this.props.ariaLabel, + ariaDisabled: isDisabled, + onInput: this.handleInput, + showListOnFocus: this.props.showListOnFocus, + onSelect: this.handleSelect, + onRemoveLast: this.handleRemoveLast, + value: this.state.selectedToken, + isDisabled: isDisabled, + placeholder: this.props.placeholder + }, + this.props.menuContent + ) + ), + loadingWidget + ); + } + }]); - 'use strict'; + return TokenInput; + }(_react2.default.Component); - /** - * Keeps track of the current owner. - * - * The current owner is the component who should own any components that are - * currently being constructed. - */ - var ReactCurrentOwner = { + exports.default = TokenInput; - /** - * @internal - * @type {ReactComponent} - */ - current: null + TokenInput.propTypes = { + isLoading: _react2.default.PropTypes.bool, + loadingComponent: _react2.default.PropTypes.any, + onInput: _react2.default.PropTypes.func, + onSelect: _react2.default.PropTypes.func.isRequired, + onRemove: _react2.default.PropTypes.func.isRequired, + selected: _react2.default.PropTypes.array.isRequired, + menuContent: _react2.default.PropTypes.any, + showListOnFocus: _react2.default.PropTypes.bool, + placeholder: _react2.default.PropTypes.string, + isDisabled: _react2.default.PropTypes.bool, + ariaLabel: _react2.default.PropTypes.string, + id: _react2.default.PropTypes.string }; - - module.exports = ReactCurrentOwner; /***/ }, /* 7 */ +/***/ function(module, exports) { + + module.exports = __WEBPACK_EXTERNAL_MODULE_7__; + +/***/ }, +/* 8 */ /***/ function(module, exports, __webpack_require__) { - /* WEBPACK VAR INJECTION */(function(process) {/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ReactDOMTextComponent - * @typechecks static-only - */ - 'use strict'; - var DOMChildrenOperations = __webpack_require__(8); - var DOMPropertyOperations = __webpack_require__(23); - var ReactComponentBrowserEnvironment = __webpack_require__(27); - var ReactMount = __webpack_require__(29); - - var assign = __webpack_require__(40); - var escapeTextContentForBrowser = __webpack_require__(22); - var setTextContent = __webpack_require__(21); - var validateDOMNesting = __webpack_require__(71); - - /** - * Text nodes violate a couple assumptions that React makes about components: - * - * - When mounting text into the DOM, adjacent text nodes are merged. - * - Text nodes cannot be assigned a React root ID. - * - * This component is used to wrap strings in elements so that they can undergo - * the same reconciliation that is applied to elements. - * - * TODO: Investigate representing React components in the DOM with text nodes. - * - * @class ReactDOMTextComponent - * @extends ReactComponent - * @internal - */ - var ReactDOMTextComponent = function (props) { - // This constructor and its argument is currently used by mocks. - }; - - assign(ReactDOMTextComponent.prototype, { - - /** - * @param {ReactText} text - * @internal - */ - construct: function (text) { - // TODO: This is really a ReactText (ReactNode), not a ReactElement - this._currentElement = text; - this._stringText = '' + text; + Object.defineProperty(exports, "__esModule", { + value: true + }); - // Properties - this._rootNodeID = null; - this._mountIndex = 0; - }, + 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; }; }(); - /** - * Creates the markup for this text node. This node is not intended to have - * any features besides containing text content. - * - * @param {string} rootID DOM ID of the root node. - * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction - * @return {string} Markup for this text node. - * @internal - */ - mountComponent: function (rootID, transaction, context) { - if (process.env.NODE_ENV !== 'production') { - if (context[validateDOMNesting.ancestorInfoContextKey]) { - validateDOMNesting('span', null, context[validateDOMNesting.ancestorInfoContextKey]); - } - } + var _inputKeydownMap; - this._rootNodeID = rootID; - if (transaction.useCreateElement) { - var ownerDocument = context[ReactMount.ownerDocumentContextKey]; - var el = ownerDocument.createElement('span'); - DOMPropertyOperations.setAttributeForID(el, rootID); - // Populate node cache - ReactMount.getID(el); - setTextContent(el, this._stringText); - return el; - } else { - var escapedText = escapeTextContentForBrowser(this._stringText); + var _react = __webpack_require__(7); - if (transaction.renderToStaticMarkup) { - // Normally we'd wrap this in a `span` for the reasons stated above, but - // since this is a situation where React won't take over (static pages), - // we can simply return the text as it is. - return escapedText; - } + var _react2 = _interopRequireDefault(_react); - return '' + escapedText + ''; - } - }, + var _classnames = __webpack_require__(9); - /** - * Updates this component by updating the text content. - * - * @param {ReactText} nextText The next text content - * @param {ReactReconcileTransaction} transaction - * @internal - */ - receiveComponent: function (nextText, transaction) { - if (nextText !== this._currentElement) { - this._currentElement = nextText; - var nextStringText = '' + nextText; - if (nextStringText !== this._stringText) { - // TODO: Save this as pending props and use performUpdateIfNecessary - // and/or updateComponent to do the actual update for consistency with - // other component types? - this._stringText = nextStringText; - var node = ReactMount.getNode(this._rootNodeID); - DOMChildrenOperations.updateTextContent(node, nextStringText); - } - } - }, + var _classnames2 = _interopRequireDefault(_classnames); - unmountComponent: function () { - ReactComponentBrowserEnvironment.unmountIDFromEnvironment(this._rootNodeID); - } + var _option = __webpack_require__(10); - }); + var _option2 = _interopRequireDefault(_option); - module.exports = ReactDOMTextComponent; - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(5))) - -/***/ }, -/* 8 */ -/***/ function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(process) {/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule DOMChildrenOperations - * @typechecks static-only - */ + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - 'use strict'; + function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - var Danger = __webpack_require__(9); - var ReactMultiChildUpdateTypes = __webpack_require__(17); - var ReactPerf = __webpack_require__(19); + 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; } - var setInnerHTML = __webpack_require__(20); - var setTextContent = __webpack_require__(21); - var invariant = __webpack_require__(14); + 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; } - /** - * Inserts `childNode` as a child of `parentNode` at the `index`. - * - * @param {DOMElement} parentNode Parent node in which to insert. - * @param {DOMElement} childNode Child node to insert. - * @param {number} index Index at which to insert the child. - * @internal - */ - function insertChildAt(parentNode, childNode, index) { - // By exploiting arrays returning `undefined` for an undefined index, we can - // rely exclusively on `insertBefore(node, null)` instead of also using - // `appendChild(node)`. However, using `undefined` is not allowed by all - // browsers so we must replace it with `null`. + 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; } - // fix render order error in safari - // IE8 will throw error when index out of list size. - var beforeChild = index >= parentNode.childNodes.length ? null : parentNode.childNodes.item(index); + var guid = 0; - parentNode.insertBefore(childNode, beforeChild); - } + var K_BACKSPACE = 8; - /** - * Operations for updating with DOM children. - */ - var DOMChildrenOperations = { + function emptyFunction() {} - dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup, + var KEYDOWN_MAPS = { + inputKeydownMap: (_inputKeydownMap = {}, _defineProperty(_inputKeydownMap, K_BACKSPACE, 'removeLastToken'), _defineProperty(_inputKeydownMap, 13, 'selectOnEnter'), _defineProperty(_inputKeydownMap, 27, 'hideOnEscape'), _defineProperty(_inputKeydownMap, 38, 'focusPrevious'), _defineProperty(_inputKeydownMap, 40, 'focusNext'), _inputKeydownMap), - updateTextContent: setTextContent, + optionKeydownMap: { + 13: 'selectOption', + 27: 'hideOnEscape', + 38: 'focusPrevious', + 40: 'focusNext' + } + }; - /** - * Updates a component's children by processing a series of updates. The - * update configurations are each expected to have a `parentNode` property. - * - * @param {array} updates List of update configurations. - * @param {array} markupList List of markup strings. - * @internal - */ - processUpdates: function (updates, markupList) { - var update; - // Mapping from parent IDs to initial child orderings. - var initialChildren = null; - // List of children that will be moved or removed. - var updatedChildren = null; + var ComboBox = function (_React$Component) { + _inherits(ComboBox, _React$Component); - for (var i = 0; i < updates.length; i++) { - update = updates[i]; - if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING || update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) { - var updatedIndex = update.fromIndex; - var updatedChild = update.parentNode.childNodes[updatedIndex]; - var parentID = update.parentID; + function ComboBox(props, context) { + _classCallCheck(this, ComboBox); - !updatedChild ? process.env.NODE_ENV !== 'production' ? invariant(false, 'processUpdates(): Unable to find child %s of element. This ' + 'probably means the DOM was unexpectedly mutated (e.g., by the ' + 'browser), usually due to forgetting a when using tables, ' + 'nesting tags like , , or , or using non-SVG elements ' + 'in an parent. Try inspecting the child nodes of the element ' + 'with React ID `%s`.', updatedIndex, parentID) : invariant(false) : undefined; + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(ComboBox).call(this, props, context)); - initialChildren = initialChildren || {}; - initialChildren[parentID] = initialChildren[parentID] || []; - initialChildren[parentID][updatedIndex] = updatedChild; + _this.focusOption = _this.focusOption.bind(_this); + _this.handleButtonClick = _this.handleButtonClick.bind(_this); + _this.handleInputBlur = _this.handleInputBlur.bind(_this); + _this.handleInputChange = _this.handleInputChange.bind(_this); + _this.handleInputClick = _this.handleInputClick.bind(_this); + _this.handleInputFocus = _this.handleInputFocus.bind(_this); + _this.handleInputKeyUp = _this.handleInputKeyUp.bind(_this); + _this.handleKeydown = _this.handleKeydown.bind(_this); + _this.handleOptionBlur = _this.handleOptionBlur.bind(_this); + _this.handleOptionFocus = _this.handleOptionFocus.bind(_this); + _this.handleOptionKeyDown = _this.handleOptionKeyDown.bind(_this); + _this.handleOptionMouseEnter = _this.handleOptionMouseEnter.bind(_this); + _this.hideList = _this.hideList.bind(_this); + _this.selectOption = _this.selectOption.bind(_this); - updatedChildren = updatedChildren || []; - updatedChildren.push(updatedChild); + _this.state = { + value: props.value, + // the value displayed in the input + inputValue: _this.findInitialInputValue(), + isOpen: false, + focusedIndex: null, + matchedAutocompleteOption: null, + // this prevents crazy jumpiness since we focus options on mouseenter + usingKeyboard: false, + activedescendant: null, + listId: 'ic-tokeninput-list-' + ++guid, + menu: { + children: [], + activedescendant: null, + isEmpty: true } - } + }; + return _this; + } - var renderedMarkup; - // markupList is either a list of markup or just a list of elements - if (markupList.length && typeof markupList[0] === 'string') { - renderedMarkup = Danger.dangerouslyRenderMarkup(markupList); - } else { - renderedMarkup = markupList; + _createClass(ComboBox, [{ + key: 'componentWillMount', + value: function componentWillMount() { + this.setState({ menu: this.makeMenu(this.props.children) }); } + }, { + key: 'componentWillReceiveProps', + value: function componentWillReceiveProps(newProps) { + var _this2 = this; - // Remove updated children first so that `toIndex` is consistent. - if (updatedChildren) { - for (var j = 0; j < updatedChildren.length; j++) { - updatedChildren[j].parentNode.removeChild(updatedChildren[j]); - } + this.setState({ menu: this.makeMenu(newProps.children) }, function () { + if (newProps.children.length && (_this2.isOpen || document.activeElement === _this2.refs.input)) { + if (!_this2.state.menu.children.length) { + return; + } + _this2.setState({ isOpen: true }, function () { + _this2.refs.list.scrollTop = 0; + }); + } else { + _this2.hideList(); + } + }); } - - for (var k = 0; k < updates.length; k++) { - update = updates[k]; - switch (update.type) { - case ReactMultiChildUpdateTypes.INSERT_MARKUP: - insertChildAt(update.parentNode, renderedMarkup[update.markupIndex], update.toIndex); - break; - case ReactMultiChildUpdateTypes.MOVE_EXISTING: - insertChildAt(update.parentNode, initialChildren[update.parentID][update.fromIndex], update.toIndex); - break; - case ReactMultiChildUpdateTypes.SET_MARKUP: - setInnerHTML(update.parentNode, update.content); - break; - case ReactMultiChildUpdateTypes.TEXT_CONTENT: - setTextContent(update.parentNode, update.content); - break; - case ReactMultiChildUpdateTypes.REMOVE_NODE: - // Already removed by the for-loop above. - break; - } + }, { + key: 'getClassName', + value: function getClassName() { + return (0, _classnames2.default)(this.props.className, 'ic-tokeninput', { + 'ic-tokeninput-is-open': this.state.isOpen + }); } - } - - }; - - ReactPerf.measureMethods(DOMChildrenOperations, 'DOMChildrenOperations', { - updateTextContent: 'updateTextContent' - }); - module.exports = DOMChildrenOperations; - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(5))) - -/***/ }, -/* 9 */ -/***/ function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(process) {/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule Danger - * @typechecks static-only - */ - - 'use strict'; + /** + * We don't create the components, the user supplies them, + * so before rendering we attach handlers to facilitate communication from + * the ComboboxOption to the Combobox. + */ - var ExecutionEnvironment = __webpack_require__(10); + }, { + key: 'makeMenu', + value: function makeMenu(children) { + var _this3 = this; + + var activedescendant = void 0; + var isEmpty = true; + + // Should this instead use React.addons.cloneWithProps or React.cloneElement? + var _children = _react2.default.Children.map(children, function (child, index) { + // console.log(child.type, ComboboxOption.type) + if (child.type !== _option2.default) { + // allow random elements to live in this list + return child; + } + isEmpty = false; + // TODO: cloneWithProps and map instead of altering the children in-place + var props = child.props; + + var newProps = { + onBlur: _this3.handleOptionBlur, + onClick: _this3.selectOption.bind(_this3, child), + onFocus: _this3.handleOptionFocus, + onKeyDown: _this3.handleOptionKeyDown.bind(_this3, child), + onMouseEnter: _this3.handleOptionMouseEnter.bind(_this3, index), + isSelected: _this3.state.value === props.value + }; - var createNodesFromMarkup = __webpack_require__(11); - var emptyFunction = __webpack_require__(16); - var getMarkupWrap = __webpack_require__(15); - var invariant = __webpack_require__(14); + if (_this3.state.value === props.value) { + // need an ID for WAI-ARIA + newProps.id = props.id || 'ic-tokeninput-selected-' + ++guid; + activedescendant = props.id; + } - var OPEN_TAG_NAME_EXP = /^(<[^ \/>]+)/; - var RESULT_INDEX_ATTR = 'data-danger-index'; + return _react2.default.cloneElement(child, newProps); + }); - /** - * Extracts the `nodeName` from a string of markup. - * - * NOTE: Extracting the `nodeName` does not require a regular expression match - * because we make assumptions about React-generated markup (i.e. there are no - * spaces surrounding the opening tag and there is at least one attribute). - * - * @param {string} markup String of markup. - * @return {string} Node name of the supplied markup. - * @see http://jsperf.com/extract-nodename - */ - function getNodeName(markup) { - return markup.substring(1, markup.indexOf(' ')); - } + return { + children: _children, + activedescendant: activedescendant, + isEmpty: isEmpty + }; + } - var Danger = { + /** + * When the user begins typing again we need to clear out any state that has + * to do with an existing or potential selection. + */ - /** - * Renders markup into an array of nodes. The markup is expected to render - * into a list of root nodes. Also, the length of `resultList` and - * `markupList` should be the same. - * - * @param {array} markupList List of markup strings to render. - * @return {array} List of rendered nodes. - * @internal - */ - dangerouslyRenderMarkup: function (markupList) { - !ExecutionEnvironment.canUseDOM ? process.env.NODE_ENV !== 'production' ? invariant(false, 'dangerouslyRenderMarkup(...): Cannot render markup in a worker ' + 'thread. Make sure `window` and `document` are available globally ' + 'before requiring React when unit testing or use ' + 'ReactDOMServer.renderToString for server rendering.') : invariant(false) : undefined; - var nodeName; - var markupByNodeName = {}; - // Group markup by `nodeName` if a wrap is necessary, else by '*'. - for (var i = 0; i < markupList.length; i++) { - !markupList[i] ? process.env.NODE_ENV !== 'production' ? invariant(false, 'dangerouslyRenderMarkup(...): Missing markup.') : invariant(false) : undefined; - nodeName = getNodeName(markupList[i]); - nodeName = getMarkupWrap(nodeName) ? nodeName : '*'; - markupByNodeName[nodeName] = markupByNodeName[nodeName] || []; - markupByNodeName[nodeName][i] = markupList[i]; + }, { + key: 'clearSelectedState', + value: function clearSelectedState(cb) { + this.setState({ + focusedIndex: null, + inputValue: null, + value: null, + matchedAutocompleteOption: null, + activedescendant: null + }, cb); + } + }, { + key: 'handleInputChange', + value: function handleInputChange() { + var _this4 = this; + + var value = this.refs.input.value; + + this.clearSelectedState(function () { + _this4.props.onInput(value); + }); + } + }, { + key: 'handleInputFocus', + value: function handleInputFocus() { + this.maybeShowList(); + } + }, { + key: 'handleInputClick', + value: function handleInputClick() { + this.maybeShowList(); + } + }, { + key: 'maybeShowList', + value: function maybeShowList() { + if (this.props.showListOnFocus) { + this.showList(); + } + } + }, { + key: 'handleInputBlur', + value: function handleInputBlur() { + var focusedAnOption = this.state.focusedIndex !== null && this.state.focusedIndex !== undefined; + if (focusedAnOption) { + return; + } + this.maybeSelectAutocompletedOption(); + this.hideList(); + } + }, { + key: 'handleOptionBlur', + value: function handleOptionBlur() { + // don't want to hide the list if we focused another option + this.blurTimer = setTimeout(this.hideList, 0); + } + }, { + key: 'handleOptionFocus', + value: function handleOptionFocus() { + // see `handleOptionBlur` + clearTimeout(this.blurTimer); + } + }, { + key: 'handleInputKeyUp', + value: function handleInputKeyUp(event) { + if (this.state.menu.isEmpty + // autocompleting while backspacing feels super weird, so let's not + || event.keyCode === K_BACKSPACE || !this.props.autocomplete.match(/both|inline/)) return; + } + }, { + key: 'handleButtonClick', + value: function handleButtonClick() { + if (this.state.isOpen) { + this.hideList(); + } else { + this.showList(); + } + this.focusInput(); + } + }, { + key: 'showList', + value: function showList() { + if (!this.state.menu.children.length) { + return; + } + this.setState({ isOpen: true }); + } + }, { + key: 'hideList', + value: function hideList() { + this.setState({ + isOpen: false, + focusedIndex: null + }); + } + }, { + key: 'hideOnEscape', + value: function hideOnEscape(event) { + this.hideList(); + this.focusInput(); + event.preventDefault(); + } + }, { + key: 'focusInput', + value: function focusInput() { + this.refs.input.focus(); + } + }, { + key: 'selectInput', + value: function selectInput() { + this.refs.input.select(); + } + }, { + key: 'handleKeydown', + value: function handleKeydown(event) { + var handlerName = KEYDOWN_MAPS.inputKeydownMap[event.keyCode]; + if (!handlerName) { + return null; + } + this.setState({ usingKeyboard: true }); + return this[handlerName].call(this, event); + } + }, { + key: 'handleOptionKeyDown', + value: function handleOptionKeyDown(child, event) { + var handlerName = KEYDOWN_MAPS.optionKeydownMap[event.keyCode]; + if (!handlerName) { + // if the user starts typing again while focused on an option, move focus + // to the inpute, select so it wipes out any existing value + this.selectInput(); + return; + } + event.preventDefault(); + this.setState({ usingKeyboard: true }); + this[handlerName].call(this, child); + } + }, { + key: 'handleOptionMouseEnter', + value: function handleOptionMouseEnter(index) { + if (this.state.usingKeyboard) { + this.setState({ usingKeyboard: false }); + } else { + this.focusOptionAtIndex(index); + } + } + }, { + key: 'selectOnEnter', + value: function selectOnEnter(event) { + event.preventDefault(); + this.maybeSelectAutocompletedOption(); } - var resultList = []; - var resultListAssignmentCount = 0; - for (nodeName in markupByNodeName) { - if (!markupByNodeName.hasOwnProperty(nodeName)) { - continue; + }, { + key: 'maybeSelectAutocompletedOption', + value: function maybeSelectAutocompletedOption() { + if (!this.state.matchedAutocompleteOption) { + this.selectText(); + } else { + this.selectOption(this.state.matchedAutocompleteOption, { focus: false }); } - var markupListByNodeName = markupByNodeName[nodeName]; + } + }, { + key: 'selectOption', + value: function selectOption(child) { + var _this5 = this; - // This for-in loop skips the holes of the sparse array. The order of - // iteration should follow the order of assignment, which happens to match - // numerical index order, but we don't rely on that. - var resultIndex; - for (resultIndex in markupListByNodeName) { - if (markupListByNodeName.hasOwnProperty(resultIndex)) { - var markup = markupListByNodeName[resultIndex]; + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - // Push the requested markup with an additional RESULT_INDEX_ATTR - // attribute. If the markup does not start with a < character, it - // will be discarded below (with an appropriate console.error). - markupListByNodeName[resultIndex] = markup.replace(OPEN_TAG_NAME_EXP, - // This index will be parsed back out below. - '$1 ' + RESULT_INDEX_ATTR + '="' + resultIndex + '" '); + this.setState({ + // value: child.props.value, + // inputValue: getLabel(child), + matchedAutocompleteOption: null + }, function () { + _this5.props.onSelect(child.props.value, child); + _this5.hideList(); + _this5.clearSelectedState(); // added + if (options.focus !== false) { + _this5.selectInput(); } + }); + this.refs.input.value = ''; // added + } + }, { + key: 'selectText', + value: function selectText() { + var value = this.refs.input.value; + if (!value) { + return; + } + this.props.onSelect(value); + this.clearSelectedState(); + this.refs.input.value = ''; // added + } + }, { + key: 'focusNext', + value: function focusNext(event) { + if (event.preventDefault) { + event.preventDefault(); } - // Render each group of markup with similar wrapping `nodeName`. - var renderNodes = createNodesFromMarkup(markupListByNodeName.join(''), emptyFunction // Do nothing special with + +
, or , or using non-SVG elements ' + 'in an parent. Try inspecting the child nodes of the element ' + 'with React ID `%s`.', updatedIndex, parentID) : invariant(false) : undefined; + var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(ComboBox).call(this, props, context)); - initialChildren = initialChildren || {}; - initialChildren[parentID] = initialChildren[parentID] || []; - initialChildren[parentID][updatedIndex] = updatedChild; + _this.focusOption = _this.focusOption.bind(_this); + _this.handleButtonClick = _this.handleButtonClick.bind(_this); + _this.handleInputBlur = _this.handleInputBlur.bind(_this); + _this.handleInputChange = _this.handleInputChange.bind(_this); + _this.handleInputClick = _this.handleInputClick.bind(_this); + _this.handleInputFocus = _this.handleInputFocus.bind(_this); + _this.handleInputKeyUp = _this.handleInputKeyUp.bind(_this); + _this.handleKeydown = _this.handleKeydown.bind(_this); + _this.handleOptionBlur = _this.handleOptionBlur.bind(_this); + _this.handleOptionFocus = _this.handleOptionFocus.bind(_this); + _this.handleOptionKeyDown = _this.handleOptionKeyDown.bind(_this); + _this.handleOptionMouseEnter = _this.handleOptionMouseEnter.bind(_this); + _this.hideList = _this.hideList.bind(_this); + _this.selectOption = _this.selectOption.bind(_this); - updatedChildren = updatedChildren || []; - updatedChildren.push(updatedChild); + _this.state = { + value: props.value, + // the value displayed in the input + inputValue: _this.findInitialInputValue(), + isOpen: false, + focusedIndex: null, + matchedAutocompleteOption: null, + // this prevents crazy jumpiness since we focus options on mouseenter + usingKeyboard: false, + activedescendant: null, + listId: 'ic-tokeninput-list-' + ++guid, + menu: { + children: [], + activedescendant: null, + isEmpty: true } - } + }; + return _this; + } - var renderedMarkup; - // markupList is either a list of markup or just a list of elements - if (markupList.length && typeof markupList[0] === 'string') { - renderedMarkup = Danger.dangerouslyRenderMarkup(markupList); - } else { - renderedMarkup = markupList; + _createClass(ComboBox, [{ + key: 'componentWillMount', + value: function componentWillMount() { + this.setState({ menu: this.makeMenu(this.props.children) }); } + }, { + key: 'componentWillReceiveProps', + value: function componentWillReceiveProps(newProps) { + var _this2 = this; - // Remove updated children first so that `toIndex` is consistent. - if (updatedChildren) { - for (var j = 0; j < updatedChildren.length; j++) { - updatedChildren[j].parentNode.removeChild(updatedChildren[j]); - } + this.setState({ menu: this.makeMenu(newProps.children) }, function () { + if (newProps.children.length && (_this2.isOpen || document.activeElement === _this2.refs.input)) { + if (!_this2.state.menu.children.length) { + return; + } + _this2.setState({ isOpen: true }, function () { + _this2.refs.list.scrollTop = 0; + }); + } else { + _this2.hideList(); + } + }); } - - for (var k = 0; k < updates.length; k++) { - update = updates[k]; - switch (update.type) { - case ReactMultiChildUpdateTypes.INSERT_MARKUP: - insertChildAt(update.parentNode, renderedMarkup[update.markupIndex], update.toIndex); - break; - case ReactMultiChildUpdateTypes.MOVE_EXISTING: - insertChildAt(update.parentNode, initialChildren[update.parentID][update.fromIndex], update.toIndex); - break; - case ReactMultiChildUpdateTypes.SET_MARKUP: - setInnerHTML(update.parentNode, update.content); - break; - case ReactMultiChildUpdateTypes.TEXT_CONTENT: - setTextContent(update.parentNode, update.content); - break; - case ReactMultiChildUpdateTypes.REMOVE_NODE: - // Already removed by the for-loop above. - break; - } + }, { + key: 'getClassName', + value: function getClassName() { + return (0, _classnames2.default)(this.props.className, 'ic-tokeninput', { + 'ic-tokeninput-is-open': this.state.isOpen + }); } - } - - }; - - ReactPerf.measureMethods(DOMChildrenOperations, 'DOMChildrenOperations', { - updateTextContent: 'updateTextContent' - }); - module.exports = DOMChildrenOperations; - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(5))) - -/***/ }, -/* 9 */ -/***/ function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(process) {/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule Danger - * @typechecks static-only - */ - - 'use strict'; + /** + * We don't create the components, the user supplies them, + * so before rendering we attach handlers to facilitate communication from + * the ComboboxOption to the Combobox. + */ - var ExecutionEnvironment = __webpack_require__(10); + }, { + key: 'makeMenu', + value: function makeMenu(children) { + var _this3 = this; + + var activedescendant = void 0; + var isEmpty = true; + + // Should this instead use React.addons.cloneWithProps or React.cloneElement? + var _children = _react2.default.Children.map(children, function (child, index) { + // console.log(child.type, ComboboxOption.type) + if (child.type !== _option2.default) { + // allow random elements to live in this list + return child; + } + isEmpty = false; + // TODO: cloneWithProps and map instead of altering the children in-place + var props = child.props; + + var newProps = { + onBlur: _this3.handleOptionBlur, + onClick: _this3.selectOption.bind(_this3, child), + onFocus: _this3.handleOptionFocus, + onKeyDown: _this3.handleOptionKeyDown.bind(_this3, child), + onMouseEnter: _this3.handleOptionMouseEnter.bind(_this3, index), + isSelected: _this3.state.value === props.value + }; - var createNodesFromMarkup = __webpack_require__(11); - var emptyFunction = __webpack_require__(16); - var getMarkupWrap = __webpack_require__(15); - var invariant = __webpack_require__(14); + if (_this3.state.value === props.value) { + // need an ID for WAI-ARIA + newProps.id = props.id || 'ic-tokeninput-selected-' + ++guid; + activedescendant = props.id; + } - var OPEN_TAG_NAME_EXP = /^(<[^ \/>]+)/; - var RESULT_INDEX_ATTR = 'data-danger-index'; + return _react2.default.cloneElement(child, newProps); + }); - /** - * Extracts the `nodeName` from a string of markup. - * - * NOTE: Extracting the `nodeName` does not require a regular expression match - * because we make assumptions about React-generated markup (i.e. there are no - * spaces surrounding the opening tag and there is at least one attribute). - * - * @param {string} markup String of markup. - * @return {string} Node name of the supplied markup. - * @see http://jsperf.com/extract-nodename - */ - function getNodeName(markup) { - return markup.substring(1, markup.indexOf(' ')); - } + return { + children: _children, + activedescendant: activedescendant, + isEmpty: isEmpty + }; + } - var Danger = { + /** + * When the user begins typing again we need to clear out any state that has + * to do with an existing or potential selection. + */ - /** - * Renders markup into an array of nodes. The markup is expected to render - * into a list of root nodes. Also, the length of `resultList` and - * `markupList` should be the same. - * - * @param {array} markupList List of markup strings to render. - * @return {array} List of rendered nodes. - * @internal - */ - dangerouslyRenderMarkup: function (markupList) { - !ExecutionEnvironment.canUseDOM ? process.env.NODE_ENV !== 'production' ? invariant(false, 'dangerouslyRenderMarkup(...): Cannot render markup in a worker ' + 'thread. Make sure `window` and `document` are available globally ' + 'before requiring React when unit testing or use ' + 'ReactDOMServer.renderToString for server rendering.') : invariant(false) : undefined; - var nodeName; - var markupByNodeName = {}; - // Group markup by `nodeName` if a wrap is necessary, else by '*'. - for (var i = 0; i < markupList.length; i++) { - !markupList[i] ? process.env.NODE_ENV !== 'production' ? invariant(false, 'dangerouslyRenderMarkup(...): Missing markup.') : invariant(false) : undefined; - nodeName = getNodeName(markupList[i]); - nodeName = getMarkupWrap(nodeName) ? nodeName : '*'; - markupByNodeName[nodeName] = markupByNodeName[nodeName] || []; - markupByNodeName[nodeName][i] = markupList[i]; + }, { + key: 'clearSelectedState', + value: function clearSelectedState(cb) { + this.setState({ + focusedIndex: null, + inputValue: null, + value: null, + matchedAutocompleteOption: null, + activedescendant: null + }, cb); + } + }, { + key: 'handleInputChange', + value: function handleInputChange() { + var _this4 = this; + + var value = this.refs.input.value; + + this.clearSelectedState(function () { + _this4.props.onInput(value); + }); + } + }, { + key: 'handleInputFocus', + value: function handleInputFocus() { + this.maybeShowList(); + } + }, { + key: 'handleInputClick', + value: function handleInputClick() { + this.maybeShowList(); + } + }, { + key: 'maybeShowList', + value: function maybeShowList() { + if (this.props.showListOnFocus) { + this.showList(); + } + } + }, { + key: 'handleInputBlur', + value: function handleInputBlur() { + var focusedAnOption = this.state.focusedIndex !== null && this.state.focusedIndex !== undefined; + if (focusedAnOption) { + return; + } + this.maybeSelectAutocompletedOption(); + this.hideList(); + } + }, { + key: 'handleOptionBlur', + value: function handleOptionBlur() { + // don't want to hide the list if we focused another option + this.blurTimer = setTimeout(this.hideList, 0); + } + }, { + key: 'handleOptionFocus', + value: function handleOptionFocus() { + // see `handleOptionBlur` + clearTimeout(this.blurTimer); + } + }, { + key: 'handleInputKeyUp', + value: function handleInputKeyUp(event) { + if (this.state.menu.isEmpty + // autocompleting while backspacing feels super weird, so let's not + || event.keyCode === K_BACKSPACE || !this.props.autocomplete.match(/both|inline/)) return; + } + }, { + key: 'handleButtonClick', + value: function handleButtonClick() { + if (this.state.isOpen) { + this.hideList(); + } else { + this.showList(); + } + this.focusInput(); + } + }, { + key: 'showList', + value: function showList() { + if (!this.state.menu.children.length) { + return; + } + this.setState({ isOpen: true }); + } + }, { + key: 'hideList', + value: function hideList() { + this.setState({ + isOpen: false, + focusedIndex: null + }); + } + }, { + key: 'hideOnEscape', + value: function hideOnEscape(event) { + this.hideList(); + this.focusInput(); + event.preventDefault(); + } + }, { + key: 'focusInput', + value: function focusInput() { + this.refs.input.focus(); + } + }, { + key: 'selectInput', + value: function selectInput() { + this.refs.input.select(); + } + }, { + key: 'handleKeydown', + value: function handleKeydown(event) { + var handlerName = KEYDOWN_MAPS.inputKeydownMap[event.keyCode]; + if (!handlerName) { + return null; + } + this.setState({ usingKeyboard: true }); + return this[handlerName].call(this, event); + } + }, { + key: 'handleOptionKeyDown', + value: function handleOptionKeyDown(child, event) { + var handlerName = KEYDOWN_MAPS.optionKeydownMap[event.keyCode]; + if (!handlerName) { + // if the user starts typing again while focused on an option, move focus + // to the inpute, select so it wipes out any existing value + this.selectInput(); + return; + } + event.preventDefault(); + this.setState({ usingKeyboard: true }); + this[handlerName].call(this, child); + } + }, { + key: 'handleOptionMouseEnter', + value: function handleOptionMouseEnter(index) { + if (this.state.usingKeyboard) { + this.setState({ usingKeyboard: false }); + } else { + this.focusOptionAtIndex(index); + } + } + }, { + key: 'selectOnEnter', + value: function selectOnEnter(event) { + event.preventDefault(); + this.maybeSelectAutocompletedOption(); } - var resultList = []; - var resultListAssignmentCount = 0; - for (nodeName in markupByNodeName) { - if (!markupByNodeName.hasOwnProperty(nodeName)) { - continue; + }, { + key: 'maybeSelectAutocompletedOption', + value: function maybeSelectAutocompletedOption() { + if (!this.state.matchedAutocompleteOption) { + this.selectText(); + } else { + this.selectOption(this.state.matchedAutocompleteOption, { focus: false }); } - var markupListByNodeName = markupByNodeName[nodeName]; + } + }, { + key: 'selectOption', + value: function selectOption(child) { + var _this5 = this; - // This for-in loop skips the holes of the sparse array. The order of - // iteration should follow the order of assignment, which happens to match - // numerical index order, but we don't rely on that. - var resultIndex; - for (resultIndex in markupListByNodeName) { - if (markupListByNodeName.hasOwnProperty(resultIndex)) { - var markup = markupListByNodeName[resultIndex]; + var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - // Push the requested markup with an additional RESULT_INDEX_ATTR - // attribute. If the markup does not start with a < character, it - // will be discarded below (with an appropriate console.error). - markupListByNodeName[resultIndex] = markup.replace(OPEN_TAG_NAME_EXP, - // This index will be parsed back out below. - '$1 ' + RESULT_INDEX_ATTR + '="' + resultIndex + '" '); + this.setState({ + // value: child.props.value, + // inputValue: getLabel(child), + matchedAutocompleteOption: null + }, function () { + _this5.props.onSelect(child.props.value, child); + _this5.hideList(); + _this5.clearSelectedState(); // added + if (options.focus !== false) { + _this5.selectInput(); } + }); + this.refs.input.value = ''; // added + } + }, { + key: 'selectText', + value: function selectText() { + var value = this.refs.input.value; + if (!value) { + return; + } + this.props.onSelect(value); + this.clearSelectedState(); + this.refs.input.value = ''; // added + } + }, { + key: 'focusNext', + value: function focusNext(event) { + if (event.preventDefault) { + event.preventDefault(); } - // Render each group of markup with similar wrapping `nodeName`. - var renderNodes = createNodesFromMarkup(markupListByNodeName.join(''), emptyFunction // Do nothing special with + +