react-select-v1
Version:
A Select control built with and for ReactJS
372 lines (287 loc) • 12.4 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _defaultFilterOptions = require('./utils/defaultFilterOptions');
var _defaultFilterOptions2 = _interopRequireDefault(_defaultFilterOptions);
var _defaultMenuRenderer = require('./utils/defaultMenuRenderer');
var _defaultMenuRenderer2 = _interopRequireDefault(_defaultMenuRenderer);
var _Select = require('./Select');
var _Select2 = _interopRequireDefault(_Select);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var CreatableSelect = function (_React$Component) {
_inherits(CreatableSelect, _React$Component);
function CreatableSelect(props, context) {
_classCallCheck(this, CreatableSelect);
var _this = _possibleConstructorReturn(this, (CreatableSelect.__proto__ || Object.getPrototypeOf(CreatableSelect)).call(this, props, context));
_this.filterOptions = _this.filterOptions.bind(_this);
_this.menuRenderer = _this.menuRenderer.bind(_this);
_this.onInputKeyDown = _this.onInputKeyDown.bind(_this);
_this.onInputChange = _this.onInputChange.bind(_this);
_this.onOptionSelect = _this.onOptionSelect.bind(_this);
return _this;
}
_createClass(CreatableSelect, [{
key: 'createNewOption',
value: function createNewOption() {
var _props = this.props,
isValidNewOption = _props.isValidNewOption,
newOptionCreator = _props.newOptionCreator,
onNewOptionClick = _props.onNewOptionClick,
_props$options = _props.options,
options = _props$options === undefined ? [] : _props$options;
if (isValidNewOption({ label: this.inputValue })) {
var option = newOptionCreator({ label: this.inputValue, labelKey: this.labelKey, valueKey: this.valueKey });
var _isOptionUnique = this.isOptionUnique({ option: option, options: options });
// Don't add the same option twice.
if (_isOptionUnique) {
if (onNewOptionClick) {
onNewOptionClick(option);
} else {
options.unshift(option);
this.select.selectValue(option);
}
}
}
}
}, {
key: 'filterOptions',
value: function filterOptions() {
var _props2 = this.props,
filterOptions = _props2.filterOptions,
isValidNewOption = _props2.isValidNewOption,
promptTextCreator = _props2.promptTextCreator;
// TRICKY Check currently selected options as well.
// Don't display a create-prompt for a value that's selected.
// This covers async edge-cases where a newly-created Option isn't yet in the async-loaded array.
var excludeOptions = (arguments.length <= 2 ? undefined : arguments[2]) || [];
var filteredOptions = filterOptions.apply(undefined, arguments) || [];
if (isValidNewOption({ label: this.inputValue })) {
var _newOptionCreator = this.props.newOptionCreator;
var option = _newOptionCreator({
label: this.inputValue,
labelKey: this.labelKey,
valueKey: this.valueKey
});
// TRICKY Compare to all options (not just filtered options) in case option has already been selected).
// For multi-selects, this would remove it from the filtered list.
var _isOptionUnique2 = this.isOptionUnique({
option: option,
options: excludeOptions.concat(filteredOptions)
});
if (_isOptionUnique2) {
var prompt = promptTextCreator(this.inputValue);
this._createPlaceholderOption = _newOptionCreator({
label: prompt,
labelKey: this.labelKey,
valueKey: this.valueKey
});
filteredOptions.unshift(this._createPlaceholderOption);
}
}
return filteredOptions;
}
}, {
key: 'isOptionUnique',
value: function isOptionUnique(_ref) {
var option = _ref.option,
options = _ref.options;
var isOptionUnique = this.props.isOptionUnique;
options = options || this.props.options;
return isOptionUnique({
labelKey: this.labelKey,
option: option,
options: options,
valueKey: this.valueKey
});
}
}, {
key: 'menuRenderer',
value: function menuRenderer(params) {
var menuRenderer = this.props.menuRenderer;
return menuRenderer(_extends({}, params, {
onSelect: this.onOptionSelect,
selectValue: this.onOptionSelect
}));
}
}, {
key: 'onInputChange',
value: function onInputChange(input) {
var onInputChange = this.props.onInputChange;
// This value may be needed in between Select mounts (when this.select is null)
this.inputValue = input;
if (onInputChange) {
this.inputValue = onInputChange(input);
}
return this.inputValue;
}
}, {
key: 'onInputKeyDown',
value: function onInputKeyDown(event) {
var _props3 = this.props,
shouldKeyDownEventCreateNewOption = _props3.shouldKeyDownEventCreateNewOption,
onInputKeyDown = _props3.onInputKeyDown;
var focusedOption = this.select.getFocusedOption();
if (focusedOption && focusedOption === this._createPlaceholderOption && shouldKeyDownEventCreateNewOption({ keyCode: event.keyCode })) {
this.createNewOption();
// Prevent decorated Select from doing anything additional with this keyDown event
event.preventDefault();
} else if (onInputKeyDown) {
onInputKeyDown(event);
}
}
}, {
key: 'onOptionSelect',
value: function onOptionSelect(option) {
if (option === this._createPlaceholderOption) {
this.createNewOption();
} else {
this.select.selectValue(option);
}
}
}, {
key: 'focus',
value: function focus() {
this.select.focus();
}
}, {
key: 'render',
value: function render() {
var _this2 = this;
var _props4 = this.props,
refProp = _props4.ref,
restProps = _objectWithoutProperties(_props4, ['ref']);
var children = this.props.children;
// We can't use destructuring default values to set the children,
// because it won't apply work if `children` is null. A falsy check is
// more reliable in real world use-cases.
if (!children) {
children = defaultChildren;
}
var props = _extends({}, restProps, {
allowCreate: true,
filterOptions: this.filterOptions,
menuRenderer: this.menuRenderer,
onInputChange: this.onInputChange,
onInputKeyDown: this.onInputKeyDown,
ref: function ref(_ref2) {
_this2.select = _ref2;
// These values may be needed in between Select mounts (when this.select is null)
if (_ref2) {
_this2.labelKey = _ref2.props.labelKey;
_this2.valueKey = _ref2.props.valueKey;
}
if (refProp) {
refProp(_ref2);
}
}
});
return children(props);
}
}]);
return CreatableSelect;
}(_react2.default.Component);
var defaultChildren = function defaultChildren(props) {
return _react2.default.createElement(_Select2.default, props);
};
var isOptionUnique = function isOptionUnique(_ref3) {
var option = _ref3.option,
options = _ref3.options,
labelKey = _ref3.labelKey,
valueKey = _ref3.valueKey;
if (!options || !options.length) {
return true;
}
return options.filter(function (existingOption) {
return existingOption[labelKey] === option[labelKey] || existingOption[valueKey] === option[valueKey];
}).length === 0;
};
var isValidNewOption = function isValidNewOption(_ref4) {
var label = _ref4.label;
return !!label;
};
var newOptionCreator = function newOptionCreator(_ref5) {
var label = _ref5.label,
labelKey = _ref5.labelKey,
valueKey = _ref5.valueKey;
var option = {};
option[valueKey] = label;
option[labelKey] = label;
option.className = 'Select-create-option-placeholder';
return option;
};
var promptTextCreator = function promptTextCreator(label) {
return 'Create option "' + label + '"';
};
var shouldKeyDownEventCreateNewOption = function shouldKeyDownEventCreateNewOption(_ref6) {
var keyCode = _ref6.keyCode;
switch (keyCode) {
case 9: // TAB
case 13: // ENTER
case 188:
// COMMA
return true;
default:
return false;
}
};
// Default prop methods
CreatableSelect.isOptionUnique = isOptionUnique;
CreatableSelect.isValidNewOption = isValidNewOption;
CreatableSelect.newOptionCreator = newOptionCreator;
CreatableSelect.promptTextCreator = promptTextCreator;
CreatableSelect.shouldKeyDownEventCreateNewOption = shouldKeyDownEventCreateNewOption;
CreatableSelect.defaultProps = {
filterOptions: _defaultFilterOptions2.default,
isOptionUnique: isOptionUnique,
isValidNewOption: isValidNewOption,
menuRenderer: _defaultMenuRenderer2.default,
newOptionCreator: newOptionCreator,
promptTextCreator: promptTextCreator,
shouldKeyDownEventCreateNewOption: shouldKeyDownEventCreateNewOption
};
CreatableSelect.propTypes = {
// Child function responsible for creating the inner Select component
// This component can be used to compose HOCs (eg Creatable and Async)
// (props: Object): PropTypes.element
children: _propTypes2.default.func,
// See Select.propTypes.filterOptions
filterOptions: _propTypes2.default.any,
// Searches for any matching option within the set of options.
// This function prevents duplicate options from being created.
// ({ option: Object, options: Array, labelKey: string, valueKey: string }): boolean
isOptionUnique: _propTypes2.default.func,
// Determines if the current input text represents a valid option.
// ({ label: string }): boolean
isValidNewOption: _propTypes2.default.func,
// See Select.propTypes.menuRenderer
menuRenderer: _propTypes2.default.any,
// Factory to create new option.
// ({ label: string, labelKey: string, valueKey: string }): Object
newOptionCreator: _propTypes2.default.func,
// input change handler: function (inputValue) {}
onInputChange: _propTypes2.default.func,
// input keyDown handler: function (event) {}
onInputKeyDown: _propTypes2.default.func,
// new option click handler: function (option) {}
onNewOptionClick: _propTypes2.default.func,
// See Select.propTypes.options
options: _propTypes2.default.array,
// Creates prompt/placeholder option text.
// (filterText: string): string
promptTextCreator: _propTypes2.default.func,
ref: _propTypes2.default.func,
// Decides if a keyDown event (eg its `keyCode`) should result in the creation of a new option.
shouldKeyDownEventCreateNewOption: _propTypes2.default.func
};
exports.default = CreatableSelect;