mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Fork ReactDOMComponent and the wrappers to Fiber
From now on any change to these have to be replicated to these files. I will next start changing the implementation to work with Fiber.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,313 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @providesModule ReactDOMFiberInput
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
var ReactControlledValuePropTypes = require('ReactControlledValuePropTypes');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
|
||||
var invariant = require('invariant');
|
||||
var warning = require('warning');
|
||||
|
||||
var didWarnValueDefaultValue = false;
|
||||
var didWarnCheckedDefaultChecked = false;
|
||||
var didWarnControlledToUncontrolled = false;
|
||||
var didWarnUncontrolledToControlled = false;
|
||||
|
||||
function isControlled(props) {
|
||||
var usesChecked = props.type === 'checkbox' || props.type === 'radio';
|
||||
return usesChecked ? props.checked != null : props.value != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements an <input> host component that allows setting these optional
|
||||
* props: `checked`, `value`, `defaultChecked`, and `defaultValue`.
|
||||
*
|
||||
* If `checked` or `value` are not supplied (or null/undefined), user actions
|
||||
* that affect the checked state or value will trigger updates to the element.
|
||||
*
|
||||
* If they are supplied (and not null/undefined), the rendered element will not
|
||||
* trigger updates to the element. Instead, the props must change in order for
|
||||
* the rendered element to be updated.
|
||||
*
|
||||
* The rendered element will be initialized as unchecked (or `defaultChecked`)
|
||||
* with an empty value (or `defaultValue`).
|
||||
*
|
||||
* @see http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
|
||||
*/
|
||||
var ReactDOMInput = {
|
||||
getHostProps: function(inst, props) {
|
||||
var value = props.value;
|
||||
var checked = props.checked;
|
||||
|
||||
var hostProps = Object.assign({
|
||||
// Make sure we set .type before any other properties (setting .value
|
||||
// before .type means .value is lost in IE11 and below)
|
||||
type: undefined,
|
||||
// Make sure we set .step before .value (setting .value before .step
|
||||
// means .value is rounded on mount, based upon step precision)
|
||||
step: undefined,
|
||||
// Make sure we set .min & .max before .value (to ensure proper order
|
||||
// in corner cases such as min or max deriving from value, e.g. Issue #7170)
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
}, props, {
|
||||
defaultChecked: undefined,
|
||||
defaultValue: undefined,
|
||||
value: value != null ? value : inst._wrapperState.initialValue,
|
||||
checked: checked != null ? checked : inst._wrapperState.initialChecked,
|
||||
});
|
||||
|
||||
return hostProps;
|
||||
},
|
||||
|
||||
mountWrapper: function(inst, props) {
|
||||
if (__DEV__) {
|
||||
ReactControlledValuePropTypes.checkPropTypes(
|
||||
'input',
|
||||
props,
|
||||
inst._currentElement._owner
|
||||
);
|
||||
|
||||
var owner = inst._currentElement._owner;
|
||||
|
||||
if (
|
||||
props.checked !== undefined &&
|
||||
props.defaultChecked !== undefined &&
|
||||
!didWarnCheckedDefaultChecked
|
||||
) {
|
||||
warning(
|
||||
false,
|
||||
'%s contains an input of type %s with both checked and defaultChecked props. ' +
|
||||
'Input elements must be either controlled or uncontrolled ' +
|
||||
'(specify either the checked prop, or the defaultChecked prop, but not ' +
|
||||
'both). Decide between using a controlled or uncontrolled input ' +
|
||||
'element and remove one of these props. More info: ' +
|
||||
'https://fb.me/react-controlled-components',
|
||||
owner && owner.getName() || 'A component',
|
||||
props.type
|
||||
);
|
||||
didWarnCheckedDefaultChecked = true;
|
||||
}
|
||||
if (
|
||||
props.value !== undefined &&
|
||||
props.defaultValue !== undefined &&
|
||||
!didWarnValueDefaultValue
|
||||
) {
|
||||
warning(
|
||||
false,
|
||||
'%s contains an input of type %s with both value and defaultValue props. ' +
|
||||
'Input 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 input ' +
|
||||
'element and remove one of these props. More info: ' +
|
||||
'https://fb.me/react-controlled-components',
|
||||
owner && owner.getName() || 'A component',
|
||||
props.type
|
||||
);
|
||||
didWarnValueDefaultValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
var defaultValue = props.defaultValue;
|
||||
inst._wrapperState = {
|
||||
initialChecked: props.checked != null ? props.checked : props.defaultChecked,
|
||||
initialValue: props.value != null ? props.value : defaultValue,
|
||||
listeners: null,
|
||||
};
|
||||
|
||||
if (__DEV__) {
|
||||
inst._wrapperState.controlled = isControlled(props);
|
||||
}
|
||||
},
|
||||
|
||||
updateWrapper: function(inst) {
|
||||
var props = inst._currentElement.props;
|
||||
|
||||
if (__DEV__) {
|
||||
var controlled = isControlled(props);
|
||||
var owner = inst._currentElement._owner;
|
||||
|
||||
if (!inst._wrapperState.controlled && controlled && !didWarnUncontrolledToControlled) {
|
||||
warning(
|
||||
false,
|
||||
'%s is changing an uncontrolled input of type %s to be controlled. ' +
|
||||
'Input elements should not switch from uncontrolled to controlled (or vice versa). ' +
|
||||
'Decide between using a controlled or uncontrolled input ' +
|
||||
'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
|
||||
owner && owner.getName() || 'A component',
|
||||
props.type
|
||||
);
|
||||
didWarnUncontrolledToControlled = true;
|
||||
}
|
||||
if (inst._wrapperState.controlled && !controlled && !didWarnControlledToUncontrolled) {
|
||||
warning(
|
||||
false,
|
||||
'%s is changing a controlled input of type %s to be uncontrolled. ' +
|
||||
'Input elements should not switch from controlled to uncontrolled (or vice versa). ' +
|
||||
'Decide between using a controlled or uncontrolled input ' +
|
||||
'element for the lifetime of the component. More info: https://fb.me/react-controlled-components',
|
||||
owner && owner.getName() || 'A component',
|
||||
props.type
|
||||
);
|
||||
didWarnControlledToUncontrolled = true;
|
||||
}
|
||||
}
|
||||
|
||||
var checked = props.checked;
|
||||
if (checked != null) {
|
||||
DOMPropertyOperations.setValueForProperty(
|
||||
ReactDOMComponentTree.getNodeFromInstance(inst),
|
||||
'checked',
|
||||
checked || false
|
||||
);
|
||||
}
|
||||
|
||||
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
|
||||
var value = props.value;
|
||||
if (value != null) {
|
||||
|
||||
// Cast `value` to a string to ensure the value is set correctly. While
|
||||
// browsers typically do this as necessary, jsdom doesn't.
|
||||
var newValue = '' + value;
|
||||
|
||||
// To avoid side effects (such as losing text selection), only set value if changed
|
||||
if (newValue !== node.value) {
|
||||
node.value = newValue;
|
||||
}
|
||||
} else {
|
||||
if (props.value == null && props.defaultValue != null) {
|
||||
// In Chrome, assigning defaultValue to certain input types triggers input validation.
|
||||
// For number inputs, the display value loses trailing decimal points. For email inputs,
|
||||
// Chrome raises "The specified value <x> is not a valid email address".
|
||||
//
|
||||
// Here we check to see if the defaultValue has actually changed, avoiding these problems
|
||||
// when the user is inputting text
|
||||
//
|
||||
// https://github.com/facebook/react/issues/7253
|
||||
if (node.defaultValue !== '' + props.defaultValue) {
|
||||
node.defaultValue = '' + props.defaultValue;
|
||||
}
|
||||
}
|
||||
if (props.checked == null && props.defaultChecked != null) {
|
||||
node.defaultChecked = !!props.defaultChecked;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
postMountWrapper: function(inst) {
|
||||
var props = inst._currentElement.props;
|
||||
|
||||
// This is in postMount because we need access to the DOM node, which is not
|
||||
// available until after the component has mounted.
|
||||
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
|
||||
|
||||
// Detach value from defaultValue. We won't do anything if we're working on
|
||||
// submit or reset inputs as those values & defaultValues are linked. They
|
||||
// are not resetable nodes so this operation doesn't matter and actually
|
||||
// removes browser-default values (eg "Submit Query") when no value is
|
||||
// provided.
|
||||
|
||||
switch (props.type) {
|
||||
case 'submit':
|
||||
case 'reset':
|
||||
break;
|
||||
case 'color':
|
||||
case 'date':
|
||||
case 'datetime':
|
||||
case 'datetime-local':
|
||||
case 'month':
|
||||
case 'time':
|
||||
case 'week':
|
||||
// This fixes the no-show issue on iOS Safari and Android Chrome:
|
||||
// https://github.com/facebook/react/issues/7233
|
||||
node.value = '';
|
||||
node.value = node.defaultValue;
|
||||
break;
|
||||
default:
|
||||
node.value = node.value;
|
||||
break;
|
||||
}
|
||||
|
||||
// Normally, we'd just do `node.checked = node.checked` upon initial mount, less this bug
|
||||
// this is needed to work around a chrome bug where setting defaultChecked
|
||||
// will sometimes influence the value of checked (even after detachment).
|
||||
// Reference: https://bugs.chromium.org/p/chromium/issues/detail?id=608416
|
||||
// We need to temporarily unset name to avoid disrupting radio button groups.
|
||||
var name = node.name;
|
||||
if (name !== '') {
|
||||
node.name = '';
|
||||
}
|
||||
node.defaultChecked = !node.defaultChecked;
|
||||
node.defaultChecked = !node.defaultChecked;
|
||||
if (name !== '') {
|
||||
node.name = name;
|
||||
}
|
||||
},
|
||||
|
||||
restoreControlledState: function(inst) {
|
||||
if (inst._rootNodeID) {
|
||||
// DOM component is still mounted; update
|
||||
ReactDOMInput.updateWrapper(inst);
|
||||
}
|
||||
var props = inst._currentElement.props;
|
||||
updateNamedCousins(inst, props);
|
||||
},
|
||||
};
|
||||
|
||||
function updateNamedCousins(thisInstance, props) {
|
||||
var name = props.name;
|
||||
if (props.type === 'radio' && name != null) {
|
||||
var rootNode = ReactDOMComponentTree.getNodeFromInstance(thisInstance);
|
||||
var queryRoot = rootNode;
|
||||
|
||||
while (queryRoot.parentNode) {
|
||||
queryRoot = queryRoot.parentNode;
|
||||
}
|
||||
|
||||
// If `rootNode.form` was non-null, then we could try `form.elements`,
|
||||
// but that sometimes behaves strangely in IE8. We could also try using
|
||||
// `form.getElementsByName`, but that will only return direct children
|
||||
// and won't include inputs that use the HTML5 `form=` attribute. Since
|
||||
// the input might not even be in a form. It might not even be in the
|
||||
// document. Let's just use the local `querySelectorAll` to ensure we don't
|
||||
// miss anything.
|
||||
var group = queryRoot.querySelectorAll(
|
||||
'input[name=' + JSON.stringify('' + name) + '][type="radio"]');
|
||||
|
||||
for (var i = 0; i < group.length; i++) {
|
||||
var otherNode = group[i];
|
||||
if (otherNode === rootNode ||
|
||||
otherNode.form !== rootNode.form) {
|
||||
continue;
|
||||
}
|
||||
// This will throw if radio buttons rendered by different copies of React
|
||||
// and the same name are rendered into the same form (same as #1939).
|
||||
// That's probably okay; we don't support it just as we don't support
|
||||
// mixing React radio buttons with non-React ones.
|
||||
var otherInstance = ReactDOMComponentTree.getInstanceFromNode(otherNode);
|
||||
invariant(
|
||||
otherInstance,
|
||||
'ReactDOMInput: Mixing React and non-React radio inputs with the ' +
|
||||
'same `name` is not supported.'
|
||||
);
|
||||
// If this is a controlled radio button group, forcing the input that
|
||||
// was previously checked to update will cause it to be come re-checked
|
||||
// as appropriate.
|
||||
if (otherInstance._rootNodeID) {
|
||||
ReactDOMInput.updateWrapper(otherInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReactDOMInput;
|
||||
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @providesModule ReactDOMFiberOption
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactDOMSelect = require('ReactDOMSelect');
|
||||
|
||||
var warning = require('warning');
|
||||
var didWarnInvalidOptionChildren = false;
|
||||
|
||||
function flattenChildren(children) {
|
||||
var content = '';
|
||||
|
||||
// Flatten children and warn if they aren't strings or numbers;
|
||||
// invalid types are ignored.
|
||||
React.Children.forEach(children, function(child) {
|
||||
if (child == null) {
|
||||
return;
|
||||
}
|
||||
if (typeof child === 'string' || typeof child === 'number') {
|
||||
content += child;
|
||||
} else if (!didWarnInvalidOptionChildren) {
|
||||
didWarnInvalidOptionChildren = true;
|
||||
warning(
|
||||
false,
|
||||
'Only strings and numbers are supported as <option> children.'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements an <option> host component that warns when `selected` is set.
|
||||
*/
|
||||
var ReactDOMOption = {
|
||||
mountWrapper: function(inst, props, hostParent) {
|
||||
// TODO (yungsters): Remove support for `selected` in <option>.
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
props.selected == null,
|
||||
'Use the `defaultValue` or `value` props on <select> instead of ' +
|
||||
'setting `selected` on <option>.'
|
||||
);
|
||||
}
|
||||
|
||||
// Look up whether this option is 'selected'
|
||||
var selectValue = null;
|
||||
if (hostParent != null) {
|
||||
var selectParent = hostParent;
|
||||
|
||||
if (selectParent._tag === 'optgroup') {
|
||||
selectParent = selectParent._hostParent;
|
||||
}
|
||||
|
||||
if (selectParent != null && selectParent._tag === 'select') {
|
||||
selectValue = ReactDOMSelect.getSelectValueContext(selectParent);
|
||||
}
|
||||
}
|
||||
|
||||
// If the value is null (e.g., no specified value or after initial mount)
|
||||
// or missing (e.g., for <datalist>), we don't change props.selected
|
||||
var selected = null;
|
||||
if (selectValue != null) {
|
||||
var value;
|
||||
if (props.value != null) {
|
||||
value = props.value + '';
|
||||
} else {
|
||||
value = flattenChildren(props.children);
|
||||
}
|
||||
selected = false;
|
||||
if (Array.isArray(selectValue)) {
|
||||
// multiple
|
||||
for (var i = 0; i < selectValue.length; i++) {
|
||||
if ('' + selectValue[i] === value) {
|
||||
selected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
selected = ('' + selectValue === value);
|
||||
}
|
||||
}
|
||||
|
||||
inst._wrapperState = {selected: selected};
|
||||
},
|
||||
|
||||
postMountWrapper: function(inst) {
|
||||
// value="" should make a value attribute (#6219)
|
||||
var props = inst._currentElement.props;
|
||||
if (props.value != null) {
|
||||
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
|
||||
node.setAttribute('value', props.value);
|
||||
}
|
||||
},
|
||||
|
||||
getHostProps: function(inst, props) {
|
||||
var hostProps = Object.assign({selected: undefined, children: undefined}, props);
|
||||
|
||||
// Read state only from initial mount because <select> updates value
|
||||
// manually; we need the initial state only for server rendering
|
||||
if (inst._wrapperState.selected != null) {
|
||||
hostProps.selected = inst._wrapperState.selected;
|
||||
}
|
||||
|
||||
var content = flattenChildren(props.children);
|
||||
|
||||
if (content) {
|
||||
hostProps.children = content;
|
||||
}
|
||||
|
||||
return hostProps;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = ReactDOMOption;
|
||||
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @providesModule ReactDOMFiberSelect
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactControlledValuePropTypes = require('ReactControlledValuePropTypes');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
|
||||
var warning = require('warning');
|
||||
|
||||
var didWarnValueDefaultValue = false;
|
||||
|
||||
function getDeclarationErrorAddendum(owner) {
|
||||
if (owner) {
|
||||
var name = owner.getName();
|
||||
if (name) {
|
||||
return ' Check the render method of `' + name + '`.';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
var valuePropNames = ['value', 'defaultValue'];
|
||||
|
||||
/**
|
||||
* Validation function for `value` and `defaultValue`.
|
||||
* @private
|
||||
*/
|
||||
function checkSelectPropTypes(inst, props) {
|
||||
var owner = inst._currentElement._owner;
|
||||
ReactControlledValuePropTypes.checkPropTypes(
|
||||
'select',
|
||||
props,
|
||||
owner
|
||||
);
|
||||
|
||||
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) {
|
||||
warning(
|
||||
false,
|
||||
'The `%s` prop supplied to <select> must be an array if ' +
|
||||
'`multiple` is true.%s',
|
||||
propName,
|
||||
getDeclarationErrorAddendum(owner)
|
||||
);
|
||||
} else if (!props.multiple && isArray) {
|
||||
warning(
|
||||
false,
|
||||
'The `%s` prop supplied to <select> must be a scalar ' +
|
||||
'value if `multiple` is false.%s',
|
||||
propName,
|
||||
getDeclarationErrorAddendum(owner)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ReactDOMComponent} inst
|
||||
* @param {boolean} multiple
|
||||
* @param {*} propValue A stringable (with `multiple`, a list of stringables).
|
||||
* @private
|
||||
*/
|
||||
function updateOptions(inst, multiple, propValue) {
|
||||
var options = ReactDOMComponentTree.getNodeFromInstance(inst).options;
|
||||
|
||||
if (multiple) {
|
||||
let selectedValue = {};
|
||||
for (let i = 0; i < propValue.length; i++) {
|
||||
selectedValue['' + propValue[i]] = true;
|
||||
}
|
||||
for (let 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.
|
||||
let selectedValue = '' + propValue;
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
if (options[i].value === selectedValue) {
|
||||
options[i].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(inst, props) {
|
||||
return Object.assign({}, props, {
|
||||
value: undefined,
|
||||
});
|
||||
},
|
||||
|
||||
mountWrapper: function(inst, props) {
|
||||
if (__DEV__) {
|
||||
checkSelectPropTypes(inst, props);
|
||||
}
|
||||
|
||||
var value = props.value;
|
||||
inst._wrapperState = {
|
||||
initialValue: value != null ? value : props.defaultValue,
|
||||
listeners: null,
|
||||
wasMultiple: Boolean(props.multiple),
|
||||
};
|
||||
|
||||
if (
|
||||
props.value !== undefined &&
|
||||
props.defaultValue !== undefined &&
|
||||
!didWarnValueDefaultValue
|
||||
) {
|
||||
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'
|
||||
);
|
||||
didWarnValueDefaultValue = true;
|
||||
}
|
||||
},
|
||||
|
||||
getSelectValueContext: function(inst) {
|
||||
// ReactDOMOption looks at this initial value so the initial generated
|
||||
// markup has correct `selected` attributes
|
||||
return inst._wrapperState.initialValue;
|
||||
},
|
||||
|
||||
postUpdateWrapper: function(inst) {
|
||||
var props = inst._currentElement.props;
|
||||
|
||||
// After the initial mount, we control selected-ness manually so don't pass
|
||||
// this value down
|
||||
inst._wrapperState.initialValue = undefined;
|
||||
|
||||
var wasMultiple = inst._wrapperState.wasMultiple;
|
||||
inst._wrapperState.wasMultiple = Boolean(props.multiple);
|
||||
|
||||
var value = props.value;
|
||||
if (value != null) {
|
||||
updateOptions(inst, Boolean(props.multiple), value);
|
||||
} else if (wasMultiple !== Boolean(props.multiple)) {
|
||||
// For simplicity, reapply `defaultValue` if `multiple` is toggled.
|
||||
if (props.defaultValue != null) {
|
||||
updateOptions(inst, Boolean(props.multiple), props.defaultValue);
|
||||
} else {
|
||||
// Revert the select back to its default unselected state.
|
||||
updateOptions(inst, Boolean(props.multiple), props.multiple ? [] : '');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
restoreControlledState: function(inst) {
|
||||
if (inst._rootNodeID) {
|
||||
var props = inst._currentElement.props;
|
||||
var value = props.value;
|
||||
|
||||
if (value != null) {
|
||||
updateOptions(inst, Boolean(props.multiple), value);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = ReactDOMSelect;
|
||||
@@ -0,0 +1,172 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @providesModule ReactDOMFiberTextarea
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactControlledValuePropTypes = require('ReactControlledValuePropTypes');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
|
||||
var invariant = require('invariant');
|
||||
var warning = require('warning');
|
||||
|
||||
var didWarnValDefaultVal = false;
|
||||
|
||||
/**
|
||||
* Implements a <textarea> host component that allows setting `value`, and
|
||||
* `defaultValue`. This differs from the traditional DOM API because value is
|
||||
* usually set as PCDATA children.
|
||||
*
|
||||
* If `value` is not supplied (or null/undefined), user actions that affect the
|
||||
* value will trigger updates to the element.
|
||||
*
|
||||
* If `value` is supplied (and not null/undefined), the rendered element will
|
||||
* not trigger updates to the element. Instead, the `value` prop must change in
|
||||
* order for the rendered element to be updated.
|
||||
*
|
||||
* The rendered element will be initialized with an empty value, the prop
|
||||
* `defaultValue` if specified, or the children content (deprecated).
|
||||
*/
|
||||
var ReactDOMTextarea = {
|
||||
getHostProps: function(inst, props) {
|
||||
invariant(
|
||||
props.dangerouslySetInnerHTML == null,
|
||||
'`dangerouslySetInnerHTML` does not make sense on <textarea>.'
|
||||
);
|
||||
|
||||
// Always set children to the same thing. In IE9, the selection range will
|
||||
// get reset if `textContent` is mutated. We could add a check in setTextContent
|
||||
// to only set the value if/when the value differs from the node value (which would
|
||||
// completely solve this IE9 bug), but Sebastian+Ben seemed to like this solution.
|
||||
// The value can be a boolean or object so that's why it's forced to be a string.
|
||||
var hostProps = Object.assign({}, props, {
|
||||
value: undefined,
|
||||
defaultValue: undefined,
|
||||
children: '' + inst._wrapperState.initialValue,
|
||||
});
|
||||
|
||||
return hostProps;
|
||||
},
|
||||
|
||||
mountWrapper: function(inst, props) {
|
||||
if (__DEV__) {
|
||||
ReactControlledValuePropTypes.checkPropTypes(
|
||||
'textarea',
|
||||
props,
|
||||
inst._currentElement._owner
|
||||
);
|
||||
if (
|
||||
props.value !== undefined &&
|
||||
props.defaultValue !== undefined &&
|
||||
!didWarnValDefaultVal
|
||||
) {
|
||||
warning(
|
||||
false,
|
||||
'Textarea 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 textarea ' +
|
||||
'and remove one of these props. More info: ' +
|
||||
'https://fb.me/react-controlled-components'
|
||||
);
|
||||
didWarnValDefaultVal = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var value = props.value;
|
||||
var initialValue = value;
|
||||
|
||||
// Only bother fetching default value if we're going to use it
|
||||
if (value == null) {
|
||||
var defaultValue = props.defaultValue;
|
||||
// TODO (yungsters): Remove support for children content in <textarea>.
|
||||
var children = props.children;
|
||||
if (children != null) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
false,
|
||||
'Use the `defaultValue` or `value` props instead of setting ' +
|
||||
'children on <textarea>.'
|
||||
);
|
||||
}
|
||||
invariant(
|
||||
defaultValue == null,
|
||||
'If you supply `defaultValue` on a <textarea>, do not pass children.'
|
||||
);
|
||||
if (Array.isArray(children)) {
|
||||
invariant(
|
||||
children.length <= 1,
|
||||
'<textarea> can only have at most one child.'
|
||||
);
|
||||
children = children[0];
|
||||
}
|
||||
|
||||
defaultValue = '' + children;
|
||||
}
|
||||
if (defaultValue == null) {
|
||||
defaultValue = '';
|
||||
}
|
||||
initialValue = defaultValue;
|
||||
}
|
||||
|
||||
inst._wrapperState = {
|
||||
initialValue: '' + initialValue,
|
||||
listeners: null,
|
||||
};
|
||||
},
|
||||
|
||||
updateWrapper: function(inst) {
|
||||
var props = inst._currentElement.props;
|
||||
|
||||
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
|
||||
var value = props.value;
|
||||
if (value != null) {
|
||||
// Cast `value` to a string to ensure the value is set correctly. While
|
||||
// browsers typically do this as necessary, jsdom doesn't.
|
||||
var newValue = '' + value;
|
||||
|
||||
// To avoid side effects (such as losing text selection), only set value if changed
|
||||
if (newValue !== node.value) {
|
||||
node.value = newValue;
|
||||
}
|
||||
if (props.defaultValue == null) {
|
||||
node.defaultValue = newValue;
|
||||
}
|
||||
}
|
||||
if (props.defaultValue != null) {
|
||||
node.defaultValue = props.defaultValue;
|
||||
}
|
||||
},
|
||||
|
||||
postMountWrapper: function(inst) {
|
||||
// This is in postMount because we need access to the DOM node, which is not
|
||||
// available until after the component has mounted.
|
||||
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
|
||||
var textContent = node.textContent;
|
||||
|
||||
// Only set node.value if textContent is equal to the expected
|
||||
// initial value. In IE10/IE11 there is a bug where the placeholder attribute
|
||||
// will populate textContent as well.
|
||||
// https://developer.microsoft.com/microsoft-edge/platform/issues/101525/
|
||||
if (textContent === inst._wrapperState.initialValue) {
|
||||
node.value = textContent;
|
||||
}
|
||||
},
|
||||
|
||||
restoreControlledState: function(inst) {
|
||||
if (inst._rootNodeID) {
|
||||
// DOM component is still mounted; update
|
||||
ReactDOMTextarea.updateWrapper(inst);
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
module.exports = ReactDOMTextarea;
|
||||
Reference in New Issue
Block a user