UNPKG

react-dom

Version:

React package for working with the DOM.

166 lines (143 loc) 5.75 kB
/** * 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. * * */ 'use strict'; 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;