159 lines
6.3 KiB
JavaScript
159 lines
6.3 KiB
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.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
var _prodInvariant = require('./reactProdInvariant'),
|
||
|
_assign = require('object-assign');
|
||
|
|
||
|
var LinkedValueUtils = require('./LinkedValueUtils');
|
||
|
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
|
||
|
var ReactUpdates = require('./ReactUpdates');
|
||
|
|
||
|
var invariant = require('fbjs/lib/invariant');
|
||
|
var warning = require('fbjs/lib/warning');
|
||
|
|
||
|
var didWarnValueLink = false;
|
||
|
var didWarnValDefaultVal = false;
|
||
|
|
||
|
function forceUpdateIfMounted() {
|
||
|
if (this._rootNodeID) {
|
||
|
// DOM component is still mounted; update
|
||
|
ReactDOMTextarea.updateWrapper(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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) {
|
||
|
!(props.dangerouslySetInnerHTML == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, '`dangerouslySetInnerHTML` does not make sense on <textarea>.') : _prodInvariant('91') : void 0;
|
||
|
|
||
|
// 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 = _assign({}, props, {
|
||
|
value: undefined,
|
||
|
defaultValue: undefined,
|
||
|
children: '' + inst._wrapperState.initialValue,
|
||
|
onChange: inst._wrapperState.onChange
|
||
|
});
|
||
|
|
||
|
return hostProps;
|
||
|
},
|
||
|
|
||
|
mountWrapper: function (inst, props) {
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
LinkedValueUtils.checkPropTypes('textarea', props, inst._currentElement._owner);
|
||
|
if (props.valueLink !== undefined && !didWarnValueLink) {
|
||
|
process.env.NODE_ENV !== 'production' ? warning(false, '`valueLink` prop on `textarea` is deprecated; set `value` and `onChange` instead.') : void 0;
|
||
|
didWarnValueLink = true;
|
||
|
}
|
||
|
if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValDefaultVal) {
|
||
|
process.env.NODE_ENV !== 'production' ? 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') : void 0;
|
||
|
didWarnValDefaultVal = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var value = LinkedValueUtils.getValue(props);
|
||
|
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 (process.env.NODE_ENV !== 'production') {
|
||
|
process.env.NODE_ENV !== 'production' ? warning(false, 'Use the `defaultValue` or `value` props instead of setting ' + 'children on <textarea>.') : void 0;
|
||
|
}
|
||
|
!(defaultValue == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'If you supply `defaultValue` on a <textarea>, do not pass children.') : _prodInvariant('92') : void 0;
|
||
|
if (Array.isArray(children)) {
|
||
|
!(children.length <= 1) ? process.env.NODE_ENV !== 'production' ? invariant(false, '<textarea> can only have at most one child.') : _prodInvariant('93') : void 0;
|
||
|
children = children[0];
|
||
|
}
|
||
|
|
||
|
defaultValue = '' + children;
|
||
|
}
|
||
|
if (defaultValue == null) {
|
||
|
defaultValue = '';
|
||
|
}
|
||
|
initialValue = defaultValue;
|
||
|
}
|
||
|
|
||
|
inst._wrapperState = {
|
||
|
initialValue: '' + initialValue,
|
||
|
listeners: null,
|
||
|
onChange: _handleChange.bind(inst)
|
||
|
};
|
||
|
},
|
||
|
|
||
|
updateWrapper: function (inst) {
|
||
|
var props = inst._currentElement.props;
|
||
|
|
||
|
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
|
||
|
var value = LinkedValueUtils.getValue(props);
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function _handleChange(event) {
|
||
|
var props = this._currentElement.props;
|
||
|
var returnValue = LinkedValueUtils.executeOnChange(props, event);
|
||
|
ReactUpdates.asap(forceUpdateIfMounted, this);
|
||
|
return returnValue;
|
||
|
}
|
||
|
|
||
|
module.exports = ReactDOMTextarea;
|