Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81165 views
1
/**
2
* Copyright 2013-2014, Facebook, Inc.
3
* All rights reserved.
4
*
5
* This source code is licensed under the BSD-style license found in the
6
* LICENSE file in the root directory of this source tree. An additional grant
7
* of patent rights can be found in the PATENTS file in the same directory.
8
*
9
* @providesModule ReactDOMInput
10
*/
11
12
"use strict";
13
14
var AutoFocusMixin = require('AutoFocusMixin');
15
var DOMPropertyOperations = require('DOMPropertyOperations');
16
var LinkedValueUtils = require('LinkedValueUtils');
17
var ReactBrowserComponentMixin = require('ReactBrowserComponentMixin');
18
var ReactCompositeComponent = require('ReactCompositeComponent');
19
var ReactElement = require('ReactElement');
20
var ReactDOM = require('ReactDOM');
21
var ReactMount = require('ReactMount');
22
var ReactUpdates = require('ReactUpdates');
23
24
var assign = require('Object.assign');
25
var invariant = require('invariant');
26
27
// Store a reference to the <input> `ReactDOMComponent`. TODO: use string
28
var input = ReactElement.createFactory(ReactDOM.input.type);
29
30
var instancesByReactID = {};
31
32
function forceUpdateIfMounted() {
33
/*jshint validthis:true */
34
if (this.isMounted()) {
35
this.forceUpdate();
36
}
37
}
38
39
/**
40
* Implements an <input> native component that allows setting these optional
41
* props: `checked`, `value`, `defaultChecked`, and `defaultValue`.
42
*
43
* If `checked` or `value` are not supplied (or null/undefined), user actions
44
* that affect the checked state or value will trigger updates to the element.
45
*
46
* If they are supplied (and not null/undefined), the rendered element will not
47
* trigger updates to the element. Instead, the props must change in order for
48
* the rendered element to be updated.
49
*
50
* The rendered element will be initialized as unchecked (or `defaultChecked`)
51
* with an empty value (or `defaultValue`).
52
*
53
* @see http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
54
*/
55
var ReactDOMInput = ReactCompositeComponent.createClass({
56
displayName: 'ReactDOMInput',
57
58
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin],
59
60
getInitialState: function() {
61
var defaultValue = this.props.defaultValue;
62
return {
63
initialChecked: this.props.defaultChecked || false,
64
initialValue: defaultValue != null ? defaultValue : null
65
};
66
},
67
68
render: function() {
69
// Clone `this.props` so we don't mutate the input.
70
var props = assign({}, this.props);
71
72
props.defaultChecked = null;
73
props.defaultValue = null;
74
75
var value = LinkedValueUtils.getValue(this);
76
props.value = value != null ? value : this.state.initialValue;
77
78
var checked = LinkedValueUtils.getChecked(this);
79
props.checked = checked != null ? checked : this.state.initialChecked;
80
81
props.onChange = this._handleChange;
82
83
return input(props, this.props.children);
84
},
85
86
componentDidMount: function() {
87
var id = ReactMount.getID(this.getDOMNode());
88
instancesByReactID[id] = this;
89
},
90
91
componentWillUnmount: function() {
92
var rootNode = this.getDOMNode();
93
var id = ReactMount.getID(rootNode);
94
delete instancesByReactID[id];
95
},
96
97
componentDidUpdate: function(prevProps, prevState, prevContext) {
98
var rootNode = this.getDOMNode();
99
if (this.props.checked != null) {
100
DOMPropertyOperations.setValueForProperty(
101
rootNode,
102
'checked',
103
this.props.checked || false
104
);
105
}
106
107
var value = LinkedValueUtils.getValue(this);
108
if (value != null) {
109
// Cast `value` to a string to ensure the value is set correctly. While
110
// browsers typically do this as necessary, jsdom doesn't.
111
DOMPropertyOperations.setValueForProperty(rootNode, 'value', '' + value);
112
}
113
},
114
115
_handleChange: function(event) {
116
var returnValue;
117
var onChange = LinkedValueUtils.getOnChange(this);
118
if (onChange) {
119
returnValue = onChange.call(this, event);
120
}
121
// Here we use asap to wait until all updates have propagated, which
122
// is important when using controlled components within layers:
123
// https://github.com/facebook/react/issues/1698
124
ReactUpdates.asap(forceUpdateIfMounted, this);
125
126
var name = this.props.name;
127
if (this.props.type === 'radio' && name != null) {
128
var rootNode = this.getDOMNode();
129
var queryRoot = rootNode;
130
131
while (queryRoot.parentNode) {
132
queryRoot = queryRoot.parentNode;
133
}
134
135
// If `rootNode.form` was non-null, then we could try `form.elements`,
136
// but that sometimes behaves strangely in IE8. We could also try using
137
// `form.getElementsByName`, but that will only return direct children
138
// and won't include inputs that use the HTML5 `form=` attribute. Since
139
// the input might not even be in a form, let's just use the global
140
// `querySelectorAll` to ensure we don't miss anything.
141
var group = queryRoot.querySelectorAll(
142
'input[name=' + JSON.stringify('' + name) + '][type="radio"]');
143
144
for (var i = 0, groupLen = group.length; i < groupLen; i++) {
145
var otherNode = group[i];
146
if (otherNode === rootNode ||
147
otherNode.form !== rootNode.form) {
148
continue;
149
}
150
var otherID = ReactMount.getID(otherNode);
151
invariant(
152
otherID,
153
'ReactDOMInput: Mixing React and non-React radio inputs with the ' +
154
'same `name` is not supported.'
155
);
156
var otherInstance = instancesByReactID[otherID];
157
invariant(
158
otherInstance,
159
'ReactDOMInput: Unknown radio button ID %s.',
160
otherID
161
);
162
// If this is a controlled radio button group, forcing the input that
163
// was previously checked to update will cause it to be come re-checked
164
// as appropriate.
165
ReactUpdates.asap(forceUpdateIfMounted, otherInstance);
166
}
167
}
168
169
return returnValue;
170
}
171
172
});
173
174
module.exports = ReactDOMInput;
175
176