You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
5.1 KiB
176 lines
5.1 KiB
/** |
|
* 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 ReactDOMSelect |
|
*/ |
|
|
|
'use strict'; |
|
|
|
var AutoFocusMixin = require("./AutoFocusMixin"); |
|
var LinkedValueUtils = require("./LinkedValueUtils"); |
|
var ReactBrowserComponentMixin = require("./ReactBrowserComponentMixin"); |
|
var ReactClass = require("./ReactClass"); |
|
var ReactElement = require("./ReactElement"); |
|
var ReactUpdates = require("./ReactUpdates"); |
|
|
|
var assign = require("./Object.assign"); |
|
|
|
var select = ReactElement.createFactory('select'); |
|
|
|
function updateOptionsIfPendingUpdateAndMounted() { |
|
/*jshint validthis:true */ |
|
if (this._pendingUpdate) { |
|
this._pendingUpdate = false; |
|
var value = LinkedValueUtils.getValue(this); |
|
if (value != null && this.isMounted()) { |
|
updateOptions(this, value); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Validation function for `value` and `defaultValue`. |
|
* @private |
|
*/ |
|
function selectValueType(props, propName, componentName) { |
|
if (props[propName] == null) { |
|
return null; |
|
} |
|
if (props.multiple) { |
|
if (!Array.isArray(props[propName])) { |
|
return new Error( |
|
("The `" + propName + "` prop supplied to <select> must be an array if ") + |
|
("`multiple` is true.") |
|
); |
|
} |
|
} else { |
|
if (Array.isArray(props[propName])) { |
|
return new Error( |
|
("The `" + propName + "` prop supplied to <select> must be a scalar ") + |
|
("value if `multiple` is false.") |
|
); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* @param {ReactComponent} component Instance of ReactDOMSelect |
|
* @param {*} propValue A stringable (with `multiple`, a list of stringables). |
|
* @private |
|
*/ |
|
function updateOptions(component, propValue) { |
|
var selectedValue, i, l; |
|
var options = component.getDOMNode().options; |
|
|
|
if (component.props.multiple) { |
|
selectedValue = {}; |
|
for (i = 0, l = propValue.length; i < l; i++) { |
|
selectedValue['' + propValue[i]] = true; |
|
} |
|
for (i = 0, l = options.length; i < l; i++) { |
|
var selected = selectedValue.hasOwnProperty(options[i].value); |
|
if (options[i].selected !== selected) { |
|
options[i].selected = selected; |
|
} |
|
} |
|
} else { |
|
// Do not set `select.value` as exact behavior isn't consistent across all |
|
// browsers for all cases. |
|
selectedValue = '' + propValue; |
|
for (i = 0, l = options.length; i < l; i++) { |
|
if (options[i].value === selectedValue) { |
|
options[i].selected = true; |
|
return; |
|
} |
|
} |
|
if (options.length) { |
|
options[0].selected = true; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Implements a <select> native component that allows optionally setting the |
|
* props `value` and `defaultValue`. If `multiple` is false, the prop must be a |
|
* stringable. If `multiple` is true, the prop must be an array of stringables. |
|
* |
|
* If `value` is not supplied (or null/undefined), user actions that change the |
|
* selected option will trigger updates to the rendered options. |
|
* |
|
* If it is supplied (and not null/undefined), the rendered options will not |
|
* update in response to user actions. Instead, the `value` prop must change in |
|
* order for the rendered options to update. |
|
* |
|
* If `defaultValue` is provided, any options with the supplied values will be |
|
* selected. |
|
*/ |
|
var ReactDOMSelect = ReactClass.createClass({ |
|
displayName: 'ReactDOMSelect', |
|
tagName: 'SELECT', |
|
|
|
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin], |
|
|
|
propTypes: { |
|
defaultValue: selectValueType, |
|
value: selectValueType |
|
}, |
|
|
|
render: function() { |
|
// Clone `this.props` so we don't mutate the input. |
|
var props = assign({}, this.props); |
|
|
|
props.onChange = this._handleChange; |
|
props.value = null; |
|
|
|
return select(props, this.props.children); |
|
}, |
|
|
|
componentWillMount: function() { |
|
this._pendingUpdate = false; |
|
}, |
|
|
|
componentDidMount: function() { |
|
var value = LinkedValueUtils.getValue(this); |
|
if (value != null) { |
|
updateOptions(this, value); |
|
} else if (this.props.defaultValue != null) { |
|
updateOptions(this, this.props.defaultValue); |
|
} |
|
}, |
|
|
|
componentDidUpdate: function(prevProps) { |
|
var value = LinkedValueUtils.getValue(this); |
|
if (value != null) { |
|
this._pendingUpdate = false; |
|
updateOptions(this, value); |
|
} else if (!prevProps.multiple !== !this.props.multiple) { |
|
// For simplicity, reapply `defaultValue` if `multiple` is toggled. |
|
if (this.props.defaultValue != null) { |
|
updateOptions(this, this.props.defaultValue); |
|
} else { |
|
// Revert the select back to its default unselected state. |
|
updateOptions(this, this.props.multiple ? [] : ''); |
|
} |
|
} |
|
}, |
|
|
|
_handleChange: function(event) { |
|
var returnValue; |
|
var onChange = LinkedValueUtils.getOnChange(this); |
|
if (onChange) { |
|
returnValue = onChange.call(this, event); |
|
} |
|
|
|
this._pendingUpdate = true; |
|
ReactUpdates.asap(updateOptionsIfPendingUpdateAndMounted, this); |
|
return returnValue; |
|
} |
|
|
|
}); |
|
|
|
module.exports = ReactDOMSelect;
|
|
|