react-pivot
Version:
React-Pivot is a data-grid component with pivot-table-like functionality for data display, filtering, and exploration.
183 lines (158 loc) • 5.45 kB
JavaScript
/**
* Copyright 2013-2014, 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 ReactDOMSelect
*/
"use strict";
var AutoFocusMixin = require("./AutoFocusMixin");
var LinkedValueUtils = require("./LinkedValueUtils");
var ReactBrowserComponentMixin = require("./ReactBrowserComponentMixin");
var ReactCompositeComponent = require("./ReactCompositeComponent");
var ReactElement = require("./ReactElement");
var ReactDOM = require("./ReactDOM");
var ReactUpdates = require("./ReactUpdates");
var assign = require("./Object.assign");
// Store a reference to the <select> `ReactDOMComponent`. TODO: use string
var select = ReactElement.createFactory(ReactDOM.select.type);
function updateWithPendingValueIfMounted() {
/*jshint validthis:true */
if (this.isMounted()) {
this.setState({value: this._pendingValue});
this._pendingValue = 0;
}
}
/**
* Validation function for `value` and `defaultValue`.
* @private
*/
function selectValueType(props, propName, componentName) {
if (props[propName] == null) {
return;
}
if (props.multiple) {
if (!Array.isArray(props[propName])) {
return new Error(
("The `" + propName + "` prop supplied to <select> must be an array if ") +
("`multiple` is true.")
);
}
} else {
if (Array.isArray(props[propName])) {
return new Error(
("The `" + propName + "` prop supplied to <select> must be a scalar ") +
("value if `multiple` is false.")
);
}
}
}
/**
* If `value` is supplied, updates <option> elements on mount and update.
* @param {ReactComponent} component Instance of ReactDOMSelect
* @param {?*} propValue For uncontrolled components, null/undefined. For
* controlled components, a string (or with `multiple`, a list of strings).
* @private
*/
function updateOptions(component, propValue) {
var multiple = component.props.multiple;
var value = propValue != null ? propValue : component.state.value;
var options = component.getDOMNode().options;
var selectedValue, i, l;
if (multiple) {
selectedValue = {};
for (i = 0, l = value.length; i < l; ++i) {
selectedValue['' + value[i]] = true;
}
} else {
selectedValue = '' + value;
}
for (i = 0, l = options.length; i < l; i++) {
var selected = multiple ?
selectedValue.hasOwnProperty(options[i].value) :
options[i].value === selectedValue;
if (selected !== options[i].selected) {
options[i].selected = selected;
}
}
}
/**
* Implements a <select> native component that allows optionally setting the
* props `value` and `defaultValue`. If `multiple` is false, the prop must be a
* string. If `multiple` is true, the prop must be an array of strings.
*
* 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 = ReactCompositeComponent.createClass({
displayName: 'ReactDOMSelect',
mixins: [AutoFocusMixin, LinkedValueUtils.Mixin, ReactBrowserComponentMixin],
propTypes: {
defaultValue: selectValueType,
value: selectValueType
},
getInitialState: function() {
return {value: this.props.defaultValue || (this.props.multiple ? [] : '')};
},
componentWillMount: function() {
this._pendingValue = null;
},
componentWillReceiveProps: function(nextProps) {
if (!this.props.multiple && nextProps.multiple) {
this.setState({value: [this.state.value]});
} else if (this.props.multiple && !nextProps.multiple) {
this.setState({value: this.state.value[0]});
}
},
render: function() {
// Clone `this.props` so we don't mutate the input.
var props = assign({}, this.props);
props.onChange = this._handleChange;
props.value = null;
return select(props, this.props.children);
},
componentDidMount: function() {
updateOptions(this, LinkedValueUtils.getValue(this));
},
componentDidUpdate: function(prevProps) {
var value = LinkedValueUtils.getValue(this);
var prevMultiple = !!prevProps.multiple;
var multiple = !!this.props.multiple;
if (value != null || prevMultiple !== multiple) {
updateOptions(this, value);
}
},
_handleChange: function(event) {
var returnValue;
var onChange = LinkedValueUtils.getOnChange(this);
if (onChange) {
returnValue = onChange.call(this, event);
}
var selectedValue;
if (this.props.multiple) {
selectedValue = [];
var options = event.target.options;
for (var i = 0, l = options.length; i < l; i++) {
if (options[i].selected) {
selectedValue.push(options[i].value);
}
}
} else {
selectedValue = event.target.value;
}
this._pendingValue = selectedValue;
ReactUpdates.asap(updateWithPendingValueIfMounted, this);
return returnValue;
}
});
module.exports = ReactDOMSelect;