react-dom
Version:
React package for working with the DOM.
166 lines (143 loc) • 5.75 kB
JavaScript
/**
* Copyright 2013-present, 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.
*
*
*/
;
var _assign = require('object-assign');
var ReactControlledValuePropTypes = require('./ReactControlledValuePropTypes');
var _require = require('./ReactDebugCurrentFiber'),
getCurrentFiberOwnerName = _require.getCurrentFiberOwnerName;
var warning = require('fbjs/lib/warning');
var didWarnValueDefaultValue = false;
function getDeclarationErrorAddendum() {
var ownerName = getCurrentFiberOwnerName();
if (ownerName) {
return ' Check the render method of `' + ownerName + '`.';
}
return '';
}
var valuePropNames = ['value', 'defaultValue'];
/**
* Validation function for `value` and `defaultValue`.
*/
function checkSelectPropTypes(props) {
ReactControlledValuePropTypes.checkPropTypes('select', props, getCurrentFiberOwnerName());
for (var i = 0; i < valuePropNames.length; i++) {
var propName = valuePropNames[i];
if (props[propName] == null) {
continue;
}
var isArray = Array.isArray(props[propName]);
if (props.multiple && !isArray) {
process.env.NODE_ENV !== 'production' ? warning(false, 'The `%s` prop supplied to <select> must be an array if ' + '`multiple` is true.%s', propName, getDeclarationErrorAddendum()) : void 0;
} else if (!props.multiple && isArray) {
process.env.NODE_ENV !== 'production' ? warning(false, 'The `%s` prop supplied to <select> must be a scalar ' + 'value if `multiple` is false.%s', propName, getDeclarationErrorAddendum()) : void 0;
}
}
}
function updateOptions(node, multiple, propValue) {
var options = node.options;
if (multiple) {
var selectedValues = propValue;
var selectedValue = {};
for (var i = 0; i < selectedValues.length; i++) {
selectedValue['' + selectedValues[i]] = true;
}
for (var _i = 0; _i < options.length; _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.
var _selectedValue = '' + propValue;
for (var _i2 = 0; _i2 < options.length; _i2++) {
if (options[_i2].value === _selectedValue) {
options[_i2].selected = true;
return;
}
}
if (options.length) {
options[0].selected = true;
}
}
}
/**
* Implements a <select> host 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 = {
getHostProps: function (element, props) {
return _assign({}, props, {
value: undefined
});
},
mountWrapper: function (element, props) {
var node = element;
if (process.env.NODE_ENV !== 'production') {
checkSelectPropTypes(props);
}
var value = props.value;
node._wrapperState = {
initialValue: value != null ? value : props.defaultValue,
wasMultiple: Boolean(props.multiple)
};
if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValueDefaultValue) {
process.env.NODE_ENV !== 'production' ? warning(false, 'Select elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled select ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components') : void 0;
didWarnValueDefaultValue = true;
}
node.multiple = Boolean(props.multiple);
if (value != null) {
updateOptions(node, Boolean(props.multiple), value);
} else if (props.defaultValue != null) {
updateOptions(node, Boolean(props.multiple), props.defaultValue);
}
},
postUpdateWrapper: function (element, props) {
var node = element;
// After the initial mount, we control selected-ness manually so don't pass
// this value down
node._wrapperState.initialValue = undefined;
var wasMultiple = node._wrapperState.wasMultiple;
node._wrapperState.wasMultiple = Boolean(props.multiple);
var value = props.value;
if (value != null) {
updateOptions(node, Boolean(props.multiple), value);
} else if (wasMultiple !== Boolean(props.multiple)) {
// For simplicity, reapply `defaultValue` if `multiple` is toggled.
if (props.defaultValue != null) {
updateOptions(node, Boolean(props.multiple), props.defaultValue);
} else {
// Revert the select back to its default unselected state.
updateOptions(node, Boolean(props.multiple), props.multiple ? [] : '');
}
}
},
restoreControlledState: function (element, props) {
var node = element;
var value = props.value;
if (value != null) {
updateOptions(node, Boolean(props.multiple), value);
}
}
};
module.exports = ReactDOMSelect;