diff --git a/examples/dist/app.js b/examples/dist/app.js index f3802c6b3f..4acb55afc5 100644 --- a/examples/dist/app.js +++ b/examples/dist/app.js @@ -129,7 +129,7 @@ var RemoteSelectField = React.createClass({ null, this.props.label ), - React.createElement(Select, { asyncOptions: this.loadOptions, className: 'remote-example' }) + React.createElement(Select, { asyncOptions: this.loadOptions, className: 'remote-example', createable: true }) ); } }); @@ -147,7 +147,7 @@ var MultiSelectField = React.createClass({ null, this.props.label ), - React.createElement(Select, { multi: true, placeholder: 'Select your favourite(s)', options: ops, onChange: logChange }) + React.createElement(Select, { multi: true, placeholder: 'Select your favourite(s)', options: ops, onChange: logChange, createable: true }) ); } }); @@ -185,9 +185,9 @@ React.render(React.createElement( null, React.createElement(StatesField, null), React.createElement(StatesField, { label: 'States (non-searchable):', searchable: false }), - React.createElement(MultiSelectField, { label: 'Multiselect:' }), + React.createElement(MultiSelectField, { label: 'Multiselect (createable):' }), React.createElement(SelectedValuesField, { label: 'Clickable labels (labels as links):' }), - React.createElement(RemoteSelectField, { label: 'Remote Options:' }) + React.createElement(RemoteSelectField, { label: 'Remote Options (createable):' }) ), document.getElementById('example')); },{"./data/states":2,"react":undefined,"react-select":undefined}],2:[function(require,module,exports){ @@ -195,6 +195,6 @@ React.render(React.createElement( exports.AU = [{ value: 'australian-capital-territory', label: 'Australian Capital Territory' }, { value: 'new-south-wales', label: 'New South Wales' }, { value: 'victoria', label: 'Victoria' }, { value: 'queensland', label: 'Queensland' }, { value: 'western-australia', label: 'Western Australia' }, { value: 'south-australia', label: 'South Australia' }, { value: 'tasmania', label: 'Tasmania' }, { value: 'northern-territory', label: 'Northern Territory' }]; -exports.US = [{ value: 'AL', label: 'Alabama' }, { value: 'AK', label: 'Alaska' }, { value: 'AS', label: 'American Samoa' }, { value: 'AZ', label: 'Arizona' }, { value: 'AR', label: 'Arkansas' }, { value: 'CA', label: 'California' }, { value: 'CO', label: 'Colorado' }, { value: 'CT', label: 'Connecticut' }, { value: 'DE', label: 'Delaware' }, { value: 'DC', label: 'District Of Columbia' }, { value: 'FM', label: 'Federated States Of Micronesia' }, { value: 'FL', label: 'Florida' }, { value: 'GA', label: 'Georgia' }, { value: 'GU', label: 'Guam' }, { value: 'HI', label: 'Hawaii' }, { value: 'ID', label: 'Idaho' }, { value: 'IL', label: 'Illinois' }, { value: 'IN', label: 'Indiana' }, { value: 'IA', label: 'Iowa' }, { value: 'KS', label: 'Kansas' }, { value: 'KY', label: 'Kentucky' }, { value: 'LA', label: 'Louisiana' }, { value: 'ME', label: 'Maine' }, { value: 'MH', label: 'Marshall Islands' }, { value: 'MD', label: 'Maryland' }, { value: 'MA', label: 'Massachusetts' }, { value: 'MI', label: 'Michigan' }, { value: 'MN', label: 'Minnesota' }, { value: 'MS', label: 'Mississippi' }, { value: 'MO', label: 'Missouri' }, { value: 'MT', label: 'Montana' }, { value: 'NE', label: 'Nebraska' }, { value: 'NV', label: 'Nevada' }, { value: 'NH', label: 'New Hampshire' }, { value: 'NJ', label: 'New Jersey' }, { value: 'NM', label: 'New Mexico' }, { value: 'NY', label: 'New York' }, { value: 'NC', label: 'North Carolina' }, { value: 'ND', label: 'North Dakota' }, { value: 'MP', label: 'Northern Mariana Islands' }, { value: 'OH', label: 'Ohio' }, { value: 'OK', label: 'Oklahoma' }, { value: 'OR', label: 'Oregon' }, { value: 'PW', label: 'Palau' }, { value: 'PA', label: 'Pennsylvania' }, { value: 'PR', label: 'Puerto Rico' }, { value: 'RI', label: 'Rhode Island' }, { value: 'SC', label: 'South Carolina' }, { value: 'SD', label: 'South Dakota' }, { value: 'TN', label: 'Tennessee' }, { value: 'TX', label: 'Texas' }, { value: 'UT', label: 'Utah' }, { value: 'VT', label: 'Vermont' }, { value: 'VI', label: 'Virgin Islands' }, { value: 'VA', label: 'Virginia' }, { value: 'WA', label: 'Washington' }, { value: 'WV', label: 'West Virginia' }, { value: 'WI', label: 'Wisconsin' }, { value: 'WY', label: 'Wyoming' }]; +exports.US = [{ value: 'AL', label: 'Alabama', disabled: true }, { value: 'AK', label: 'Alaska' }, { value: 'AS', label: 'American Samoa' }, { value: 'AZ', label: 'Arizona' }, { value: 'AR', label: 'Arkansas' }, { value: 'CA', label: 'California' }, { value: 'CO', label: 'Colorado' }, { value: 'CT', label: 'Connecticut' }, { value: 'DE', label: 'Delaware' }, { value: 'DC', label: 'District Of Columbia' }, { value: 'FM', label: 'Federated States Of Micronesia' }, { value: 'FL', label: 'Florida' }, { value: 'GA', label: 'Georgia' }, { value: 'GU', label: 'Guam' }, { value: 'HI', label: 'Hawaii' }, { value: 'ID', label: 'Idaho' }, { value: 'IL', label: 'Illinois' }, { value: 'IN', label: 'Indiana' }, { value: 'IA', label: 'Iowa' }, { value: 'KS', label: 'Kansas' }, { value: 'KY', label: 'Kentucky' }, { value: 'LA', label: 'Louisiana' }, { value: 'ME', label: 'Maine' }, { value: 'MH', label: 'Marshall Islands' }, { value: 'MD', label: 'Maryland' }, { value: 'MA', label: 'Massachusetts' }, { value: 'MI', label: 'Michigan' }, { value: 'MN', label: 'Minnesota' }, { value: 'MS', label: 'Mississippi' }, { value: 'MO', label: 'Missouri' }, { value: 'MT', label: 'Montana' }, { value: 'NE', label: 'Nebraska' }, { value: 'NV', label: 'Nevada' }, { value: 'NH', label: 'New Hampshire' }, { value: 'NJ', label: 'New Jersey' }, { value: 'NM', label: 'New Mexico' }, { value: 'NY', label: 'New York' }, { value: 'NC', label: 'North Carolina' }, { value: 'ND', label: 'North Dakota' }, { value: 'MP', label: 'Northern Mariana Islands' }, { value: 'OH', label: 'Ohio' }, { value: 'OK', label: 'Oklahoma' }, { value: 'OR', label: 'Oregon' }, { value: 'PW', label: 'Palau' }, { value: 'PA', label: 'Pennsylvania' }, { value: 'PR', label: 'Puerto Rico' }, { value: 'RI', label: 'Rhode Island' }, { value: 'SC', label: 'South Carolina' }, { value: 'SD', label: 'South Dakota' }, { value: 'TN', label: 'Tennessee' }, { value: 'TX', label: 'Texas' }, { value: 'UT', label: 'Utah' }, { value: 'VT', label: 'Vermont' }, { value: 'VI', label: 'Virgin Islands' }, { value: 'VA', label: 'Virginia' }, { value: 'WA', label: 'Washington' }, { value: 'WV', label: 'West Virginia' }, { value: 'WI', label: 'Wisconsin' }, { value: 'WY', label: 'Wyoming' }]; },{}]},{},[1]); diff --git a/examples/dist/bundle.js b/examples/dist/bundle.js index 874aa13aa1..f5290ef1eb 100644 --- a/examples/dist/bundle.js +++ b/examples/dist/bundle.js @@ -93,6 +93,8 @@ var Select = React.createClass({ matchPos: React.PropTypes.string, // (any|start) match the start or entire string when filtering matchProp: React.PropTypes.string, // (any|label|value) which option property to filter on inputProps: React.PropTypes.object, // custom attributes for the Input (in the Select-control) e.g: {'data-foo': 'bar'} + createable: React.PropTypes.bool, // whether a new option can be created by giving a name + createText: React.PropTypes.string, // text to be displayed after the new option /* * Allow user to make option label clickable. When this handler is defined we should @@ -125,6 +127,8 @@ var Select = React.createClass({ matchPos: 'any', matchProp: 'any', inputProps: {}, + createable: false, + createText: '(create new)', onOptionLabelClick: undefined }; @@ -157,31 +161,32 @@ var Select = React.createClass({ this.autoloadAsyncOptions(); } - this._closeMenuIfClickedOutside = (function (event) { - if (!this.state.isOpen) { + var self = this; + this._closeMenuIfClickedOutside = function (event) { + if (!self.state.isOpen) { return; } - var menuElem = this.refs.selectMenuContainer.getDOMNode(); - var controlElem = this.refs.control.getDOMNode(); + var menuElem = self.refs.selectMenuContainer.getDOMNode(); + var controlElem = self.refs.control.getDOMNode(); - var eventOccuredOutsideMenu = this.clickedOutsideElement(menuElem, event); - var eventOccuredOutsideControl = this.clickedOutsideElement(controlElem, event); + var eventOccuredOutsideMenu = self.clickedOutsideElement(menuElem, event); + var eventOccuredOutsideControl = self.clickedOutsideElement(controlElem, event); // Hide dropdown menu if click occurred outside of menu if (eventOccuredOutsideMenu && eventOccuredOutsideControl) { - this.setState({ + self.setState({ isOpen: false - }, this._unbindCloseMenuIfClickedOutside); + }, self._unbindCloseMenuIfClickedOutside); } - }).bind(this); + }; - this._bindCloseMenuIfClickedOutside = (function () { - document.addEventListener('click', this._closeMenuIfClickedOutside); - }).bind(this); + this._bindCloseMenuIfClickedOutside = function () { + document.addEventListener('click', self._closeMenuIfClickedOutside); + }; - this._unbindCloseMenuIfClickedOutside = (function () { - document.removeEventListener('click', this._closeMenuIfClickedOutside); - }).bind(this); + this._unbindCloseMenuIfClickedOutside = function () { + document.removeEventListener('click', self._closeMenuIfClickedOutside); + }; }, componentWillUnmount: function componentWillUnmount() { @@ -206,12 +211,15 @@ var Select = React.createClass({ }, componentDidUpdate: function componentDidUpdate() { + var self = this; + if (!this.props.disabled && this._focusAfterUpdate) { clearTimeout(this._blurTimeout); - this._focusTimeout = setTimeout((function () { - this.getInputNode().focus(); - this._focusAfterUpdate = false; - }).bind(this), 50); + + this._focusTimeout = setTimeout(function () { + self.getInputNode().focus(); + self._focusAfterUpdate = false; + }, 50); } if (this._focusedOptionReveal) { @@ -233,9 +241,8 @@ var Select = React.createClass({ clickedOutsideElement: function clickedOutsideElement(element, event) { var eventTarget = event.target ? event.target : event.srcElement; while (eventTarget != null) { - if (eventTarget === element) { - return false; - }eventTarget = eventTarget.offsetParent; + if (eventTarget === element) return false; + eventTarget = eventTarget.offsetParent; } return true; }, @@ -380,12 +387,15 @@ var Select = React.createClass({ }, handleInputBlur: function handleInputBlur(event) { - this._blurTimeout = setTimeout((function () { - if (this._focusAfterUpdate) return; - this.setState({ + var self = this; + + this._blurTimeout = setTimeout(function () { + if (self._focusAfterUpdate) return; + + self.setState({ isFocused: false }); - }).bind(this), 50); + }, 50); if (this.props.onBlur) { this.props.onBlur(event); @@ -393,9 +403,9 @@ var Select = React.createClass({ }, handleKeyDown: function handleKeyDown(event) { - if (this.state.disabled) { - return; - }switch (event.keyCode) { + if (this.state.disabled) return; + + switch (event.keyCode) { case 8: // backspace @@ -470,6 +480,8 @@ var Select = React.createClass({ }, this._bindCloseMenuIfClickedOutside); } else { var filteredOptions = this.filterOptions(this.state.options); + filteredOptions = this.addCreateOption(filteredOptions, event.target.value); + this.setState({ isOpen: true, inputValue: event.target.value, @@ -495,6 +507,7 @@ var Select = React.createClass({ if (this._optionsCache[cacheKey] && (input === cacheKey || this._optionsCache[cacheKey].complete)) { var options = this._optionsCache[cacheKey].options; var filteredOptions = this.filterOptions(options); + filteredOptions = this.addCreateOption(filteredOptions, input); var newState = { options: options, @@ -512,31 +525,33 @@ var Select = React.createClass({ } } - this.props.asyncOptions(input, (function (err, data) { + var self = this; + this.props.asyncOptions(input, function (err, data) { if (err) throw err; - this._optionsCache[input] = data; + self._optionsCache[input] = data; - if (thisRequestId !== this._currentRequestId) { + if (thisRequestId !== self._currentRequestId) { return; } - var filteredOptions = this.filterOptions(data.options); + var filteredOptions = self.filterOptions(data.options); + filteredOptions = self.addCreateOption(filteredOptions, input); var newState = { options: data.options, filteredOptions: filteredOptions, - focusedOption: this._getNewFocusedOption(filteredOptions) + focusedOption: self._getNewFocusedOption(filteredOptions) }; for (var key in state) { if (state.hasOwnProperty(key)) { newState[key] = state[key]; } } - this.setState(newState); + self.setState(newState); if (callback) callback({}); - }).bind(this)); + }); }, filterOptions: function filterOptions(options, values) { @@ -552,11 +567,9 @@ var Select = React.createClass({ return this.props.filterOptions.call(this, options, filterValue, exclude); } else { var filterOption = function filterOption(op) { - if (this.props.multi && exclude.indexOf(op.value) > -1) { - return false; - }if (this.props.filterOption) { - return this.props.filterOption.call(this, op, filterValue); - }var valueTest = String(op.value), + if (this.props.multi && exclude.indexOf(op.value) > -1) return false; + if (this.props.filterOption) return this.props.filterOption.call(this, op, filterValue); + var valueTest = String(op.value), labelTest = String(op.label); return !filterValue || this.props.matchPos === 'start' ? this.props.matchProp !== 'label' && valueTest.toLowerCase().substr(0, filterValue.length) === filterValue || this.props.matchProp !== 'value' && labelTest.toLowerCase().substr(0, filterValue.length) === filterValue : this.props.matchProp !== 'label' && valueTest.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0 || this.props.matchProp !== 'value' && labelTest.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0; }; @@ -647,7 +660,8 @@ var Select = React.createClass({ var optionClass = classes({ 'Select-option': true, - 'is-focused': isFocused + 'is-focused': isFocused, + 'is-disabled': op.disabled }); var ref = isFocused ? 'focused' : null; @@ -656,11 +670,19 @@ var Select = React.createClass({ mouseLeave = this.unfocusOption.bind(this, op), mouseDown = this.selectValue.bind(this, op); - return React.createElement( - 'div', - { ref: ref, key: 'option-' + op.value, className: optionClass, onMouseEnter: mouseEnter, onMouseLeave: mouseLeave, onMouseDown: mouseDown, onClick: mouseDown }, - op.label - ); + if (op.disabled) { + return React.createElement( + 'div', + { ref: ref, key: 'option-' + op.value, className: optionClass }, + op.label + ); + } else { + return React.createElement( + 'div', + { ref: ref, key: 'option-' + op.value, className: optionClass, onMouseEnter: mouseEnter, onMouseLeave: mouseLeave, onMouseDown: mouseDown, onClick: mouseDown }, + op.label + ); + } }, this); return ops.length ? ops : React.createElement( @@ -678,6 +700,17 @@ var Select = React.createClass({ } }, + addCreateOption: function addCreateOption(options, input) { + options = _.cloneDeep(options); + if (this.props.createable && input && !_.findWhere(options, { 'label': input })) { + options.push({ + 'value': input, + 'label': input + ' ' + this.props.createText + }); + } + return options; + }, + render: function render() { var selectClass = classes('Select', this.props.className, { 'is-multi': this.props.multi, diff --git a/examples/dist/common.js b/examples/dist/common.js index 1bd9d9d6ec..af158fbc14 100644 --- a/examples/dist/common.js +++ b/examples/dist/common.js @@ -4366,7 +4366,7 @@ if ("production" !== process.env.NODE_ENV) { if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') { console.debug( 'Download the React DevTools for a better development experience: ' + - 'http://fb.me/react-devtools' + 'https://fb.me/react-devtools' ); } } @@ -4393,7 +4393,7 @@ if ("production" !== process.env.NODE_ENV) { if (!expectedFeatures[i]) { console.error( 'One or more ES5 shim/shams expected by React are not available: ' + - 'http://fb.me/react-warning-polyfills' + 'https://fb.me/react-warning-polyfills' ); break; } @@ -4401,7 +4401,7 @@ if ("production" !== process.env.NODE_ENV) { } } -React.version = '0.13.2'; +React.version = '0.13.3'; module.exports = React; @@ -5908,7 +5908,7 @@ var ReactClass = { ("production" !== process.env.NODE_ENV ? warning( this instanceof Constructor, 'Something is calling a React component directly. Use a factory or ' + - 'JSX instead. See: http://fb.me/react-legacyfactory' + 'JSX instead. See: https://fb.me/react-legacyfactory' ) : null); } @@ -6120,20 +6120,38 @@ ReactComponent.prototype.forceUpdate = function(callback) { */ if ("production" !== process.env.NODE_ENV) { var deprecatedAPIs = { - getDOMNode: 'getDOMNode', - isMounted: 'isMounted', - replaceProps: 'replaceProps', - replaceState: 'replaceState', - setProps: 'setProps' + getDOMNode: [ + 'getDOMNode', + 'Use React.findDOMNode(component) instead.' + ], + isMounted: [ + 'isMounted', + 'Instead, make sure to clean up subscriptions and pending requests in ' + + 'componentWillUnmount to prevent memory leaks.' + ], + replaceProps: [ + 'replaceProps', + 'Instead, call React.render again at the top level.' + ], + replaceState: [ + 'replaceState', + 'Refactor your code to use setState instead (see ' + + 'https://github.com/facebook/react/issues/3236).' + ], + setProps: [ + 'setProps', + 'Instead, call React.render again at the top level.' + ] }; - var defineDeprecationWarning = function(methodName, displayName) { + var defineDeprecationWarning = function(methodName, info) { try { Object.defineProperty(ReactComponent.prototype, methodName, { get: function() { ("production" !== process.env.NODE_ENV ? warning( false, - '%s(...) is deprecated in plain JavaScript React classes.', - displayName + '%s(...) is deprecated in plain JavaScript React classes. %s', + info[0], + info[1] ) : null); return undefined; } @@ -6482,6 +6500,7 @@ var ReactCompositeComponentMixin = { this._pendingReplaceState = false; this._pendingForceUpdate = false; + var childContext; var renderedElement; var previouslyMounting = ReactLifeCycle.currentlyMountingInstance; @@ -6496,7 +6515,8 @@ var ReactCompositeComponentMixin = { } } - renderedElement = this._renderValidatedComponent(); + childContext = this._getValidatedChildContext(context); + renderedElement = this._renderValidatedComponent(childContext); } finally { ReactLifeCycle.currentlyMountingInstance = previouslyMounting; } @@ -6510,7 +6530,7 @@ var ReactCompositeComponentMixin = { this._renderedComponent, rootID, transaction, - this._processChildContext(context) + this._mergeChildContext(context, childContext) ); if (inst.componentDidMount) { transaction.getReactMountReady().enqueue(inst.componentDidMount, inst); @@ -6640,7 +6660,7 @@ var ReactCompositeComponentMixin = { * @return {object} * @private */ - _processChildContext: function(currentContext) { + _getValidatedChildContext: function(currentContext) { var inst = this._instance; var childContext = inst.getChildContext && inst.getChildContext(); if (childContext) { @@ -6665,6 +6685,13 @@ var ReactCompositeComponentMixin = { name ) : invariant(name in inst.constructor.childContextTypes)); } + return childContext; + } + return null; + }, + + _mergeChildContext: function(currentContext, childContext) { + if (childContext) { return assign({}, currentContext, childContext); } return currentContext; @@ -6924,6 +6951,10 @@ var ReactCompositeComponentMixin = { return inst.state; } + if (replace && queue.length === 1) { + return queue[0]; + } + var nextState = assign({}, replace ? queue[0] : inst.state); for (var i = replace ? 1 : 0; i < queue.length; i++) { var partial = queue[i]; @@ -6993,13 +7024,14 @@ var ReactCompositeComponentMixin = { _updateRenderedComponent: function(transaction, context) { var prevComponentInstance = this._renderedComponent; var prevRenderedElement = prevComponentInstance._currentElement; - var nextRenderedElement = this._renderValidatedComponent(); + var childContext = this._getValidatedChildContext(); + var nextRenderedElement = this._renderValidatedComponent(childContext); if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) { ReactReconciler.receiveComponent( prevComponentInstance, nextRenderedElement, transaction, - this._processChildContext(context) + this._mergeChildContext(context, childContext) ); } else { // These two IDs are actually the same! But nothing should rely on that. @@ -7015,7 +7047,7 @@ var ReactCompositeComponentMixin = { this._renderedComponent, thisID, transaction, - this._processChildContext(context) + this._mergeChildContext(context, childContext) ); this._replaceNodeWithMarkupByID(prevComponentID, nextMarkup); } @@ -7053,11 +7085,12 @@ var ReactCompositeComponentMixin = { /** * @private */ - _renderValidatedComponent: function() { + _renderValidatedComponent: function(childContext) { var renderedComponent; var previousContext = ReactContext.current; - ReactContext.current = this._processChildContext( - this._currentElement._context + ReactContext.current = this._mergeChildContext( + this._currentElement._context, + childContext ); ReactCurrentOwner.current = this; try { @@ -7426,6 +7459,7 @@ var ReactDOM = mapObject({ // SVG circle: 'circle', + clipPath: 'clipPath', defs: 'defs', ellipse: 'ellipse', g: 'g', @@ -7577,11 +7611,13 @@ function assertValidProps(props) { 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.' ) : invariant(props.children == null)); ("production" !== process.env.NODE_ENV ? invariant( - props.dangerouslySetInnerHTML.__html != null, + typeof props.dangerouslySetInnerHTML === 'object' && + '__html' in props.dangerouslySetInnerHTML, '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' + - 'Please visit http://fb.me/react-invariant-dangerously-set-inner-html ' + + 'Please visit https://fb.me/react-invariant-dangerously-set-inner-html ' + 'for more information.' - ) : invariant(props.dangerouslySetInnerHTML.__html != null)); + ) : invariant(typeof props.dangerouslySetInnerHTML === 'object' && + '__html' in props.dangerouslySetInnerHTML)); } if ("production" !== process.env.NODE_ENV) { ("production" !== process.env.NODE_ENV ? warning( @@ -10387,7 +10423,7 @@ function warnAndMonitorForKeyUse(message, element, parentType) { ("production" !== process.env.NODE_ENV ? warning( false, - message + '%s%s See http://fb.me/react-warning-keys for more information.', + message + '%s%s See https://fb.me/react-warning-keys for more information.', parentOrOwnerAddendum, childOwnerAddendum ) : null); @@ -15208,6 +15244,7 @@ var MUST_USE_ATTRIBUTE = DOMProperty.injection.MUST_USE_ATTRIBUTE; var SVGDOMPropertyConfig = { Properties: { + clipPath: MUST_USE_ATTRIBUTE, cx: MUST_USE_ATTRIBUTE, cy: MUST_USE_ATTRIBUTE, d: MUST_USE_ATTRIBUTE, @@ -15253,6 +15290,7 @@ var SVGDOMPropertyConfig = { y: MUST_USE_ATTRIBUTE }, DOMAttributeNames: { + clipPath: 'clip-path', fillOpacity: 'fill-opacity', fontFamily: 'font-family', fontSize: 'font-size', @@ -18065,6 +18103,7 @@ var shouldWrap = { // Force wrapping for SVG elements because if they get created inside a