react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / browser / ui / dom / components / ReactDOMSelect.js
81165 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule ReactDOMSelect9*/1011"use strict";1213var AutoFocusMixin = require('AutoFocusMixin');14var LinkedValueUtils = require('LinkedValueUtils');15var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');16var ReactCompositeComponent = require('ReactCompositeComponent');17var ReactElement = require('ReactElement');18var ReactDOM = require('ReactDOM');19var ReactUpdates = require('ReactUpdates');2021var assign = require('Object.assign');2223// Store a reference to the <select> `ReactDOMComponent`. TODO: use string24var select = ReactElement.createFactory(ReactDOM.select.type);2526function updateWithPendingValueIfMounted() {27/*jshint validthis:true */28if (this.isMounted()) {29this.setState({value: this._pendingValue});30this._pendingValue = 0;31}32}3334/**35* Validation function for `value` and `defaultValue`.36* @private37*/38function selectValueType(props, propName, componentName) {39if (props[propName] == null) {40return;41}42if (props.multiple) {43if (!Array.isArray(props[propName])) {44return new Error(45`The \`${propName}\` prop supplied to <select> must be an array if ` +46`\`multiple\` is true.`47);48}49} else {50if (Array.isArray(props[propName])) {51return new Error(52`The \`${propName}\` prop supplied to <select> must be a scalar ` +53`value if \`multiple\` is false.`54);55}56}57}5859/**60* If `value` is supplied, updates <option> elements on mount and update.61* @param {ReactComponent} component Instance of ReactDOMSelect62* @param {?*} propValue For uncontrolled components, null/undefined. For63* controlled components, a string (or with `multiple`, a list of strings).64* @private65*/66function updateOptions(component, propValue) {67var multiple = component.props.multiple;68var value = propValue != null ? propValue : component.state.value;69var options = component.getDOMNode().options;70var selectedValue, i, l;71if (multiple) {72selectedValue = {};73for (i = 0, l = value.length; i < l; ++i) {74selectedValue['' + value[i]] = true;75}76} else {77selectedValue = '' + value;78}79for (i = 0, l = options.length; i < l; i++) {80var selected = multiple ?81selectedValue.hasOwnProperty(options[i].value) :82options[i].value === selectedValue;8384if (selected !== options[i].selected) {85options[i].selected = selected;86}87}88}8990/**91* Implements a <select> native component that allows optionally setting the92* props `value` and `defaultValue`. If `multiple` is false, the prop must be a93* string. If `multiple` is true, the prop must be an array of strings.94*95* If `value` is not supplied (or null/undefined), user actions that change the96* selected option will trigger updates to the rendered options.97*98* If it is supplied (and not null/undefined), the rendered options will not99* update in response to user actions. Instead, the `value` prop must change in100* order for the rendered options to update.101*102* If `defaultValue` is provided, any options with the supplied values will be103* selected.104*/105var ReactDOMSelect = ReactCompositeComponent.createClass({106displayName: 'ReactDOMSelect',107108mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin],109110propTypes: {111defaultValue: selectValueType,112value: selectValueType113},114115getInitialState: function() {116return {value: this.props.defaultValue || (this.props.multiple ? [] : '')};117},118119componentWillMount: function() {120this._pendingValue = null;121},122123componentWillReceiveProps: function(nextProps) {124if (!this.props.multiple && nextProps.multiple) {125this.setState({value: [this.state.value]});126} else if (this.props.multiple && !nextProps.multiple) {127this.setState({value: this.state.value[0]});128}129},130131render: function() {132// Clone `this.props` so we don't mutate the input.133var props = assign({}, this.props);134135props.onChange = this._handleChange;136props.value = null;137138return select(props, this.props.children);139},140141componentDidMount: function() {142updateOptions(this, LinkedValueUtils.getValue(this));143},144145componentDidUpdate: function(prevProps) {146var value = LinkedValueUtils.getValue(this);147var prevMultiple = !!prevProps.multiple;148var multiple = !!this.props.multiple;149if (value != null || prevMultiple !== multiple) {150updateOptions(this, value);151}152},153154_handleChange: function(event) {155var returnValue;156var onChange = LinkedValueUtils.getOnChange(this);157if (onChange) {158returnValue = onChange.call(this, event);159}160161var selectedValue;162if (this.props.multiple) {163selectedValue = [];164var options = event.target.options;165for (var i = 0, l = options.length; i < l; i++) {166if (options[i].selected) {167selectedValue.push(options[i].value);168}169}170} else {171selectedValue = event.target.value;172}173174this._pendingValue = selectedValue;175ReactUpdates.asap(updateWithPendingValueIfMounted, this);176return returnValue;177}178179});180181module.exports = ReactDOMSelect;182183184