diff --git a/examples/src/CustomOption.js b/examples/src/CustomOption.js
new file mode 100644
index 0000000000..704f17421e
--- /dev/null
+++ b/examples/src/CustomOption.js
@@ -0,0 +1,33 @@
+var React = require('react');
+
+var Gravatar = require('react-gravatar');
+
+var Option = React.createClass({
+ propTypes: {
+ addLabelText: React.PropTypes.string,
+ className: React.PropTypes.string,
+ mouseDown: React.PropTypes.func,
+ mouseEnter: React.PropTypes.func,
+ mouseLeave: React.PropTypes.func,
+ option: React.PropTypes.object.isRequired,
+ renderFunc: React.PropTypes.func
+ },
+
+ render: function() {
+ var obj = this.props.option;
+ var size = 15;
+
+ return (
+
+
diff --git a/examples/src/data/users.js b/examples/src/data/users.js
new file mode 100644
index 0000000000..d767cae07c
--- /dev/null
+++ b/examples/src/data/users.js
@@ -0,0 +1,5 @@
+exports.users = [
+ { value: 'John Smith', label: 'John Smith', email: 'john@smith.com' },
+ { value: 'Merry Jane', label: 'Merry Jane', email: 'merry@jane.com' },
+ { value: 'Stan Hoper', label: 'Stan Hoper', email: 'stan@hoper.com' }
+];
diff --git a/package.json b/package.json
index 61942e07f0..558fa05b9f 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"mocha": "^2.2.5",
"mocha-jsdom": "^1.0.0",
"react": ">=0.13.3",
+ "react-gravatar": "2.0.0",
"react-component-gulp-tasks": "^0.7.0",
"sinon": "^1.15.4",
"sinon-chai": "^2.8.0",
diff --git a/src/Option.js b/src/Option.js
new file mode 100644
index 0000000000..fe03b78203
--- /dev/null
+++ b/src/Option.js
@@ -0,0 +1,32 @@
+var React = require('react');
+
+var Option = React.createClass({
+ propTypes: {
+ addLabelText: React.PropTypes.string, // string rendered in case of allowCreate option passed to ReactSelect
+ className: React.PropTypes.string, // className (based on mouse position)
+ mouseDown: React.PropTypes.func, // method to handle click on option element
+ mouseEnter: React.PropTypes.func, // method to handle mouseEnter on option element
+ mouseLeave: React.PropTypes.func, // method to handle mouseLeave on option element
+ option: React.PropTypes.object.isRequired, // object that is base for that option
+ renderFunc: React.PropTypes.func // method passed to ReactSelect component to render label text
+ },
+
+ render: function() {
+ var obj = this.props.option;
+ var renderedLabel = this.props.renderFunc(obj);
+
+ return obj.disabled ? (
+
{renderedLabel}
+ ) : (
+
+ { obj.create ? this.props.addLabelText.replace('{label}', obj.label) : renderedLabel }
+
+ );
+ }
+});
+
+module.exports = Option;
diff --git a/src/Select.js b/src/Select.js
index 927268b34f..099e79f609 100644
--- a/src/Select.js
+++ b/src/Select.js
@@ -6,6 +6,8 @@ var React = require('react');
var Input = require('react-input-autosize');
var classes = require('classnames');
var Value = require('./Value');
+var SingleValue = require('./SingleValue');
+var Option = require('./Option');
var requestId = 0;
@@ -44,7 +46,11 @@ var Select = React.createClass({
searchable: React.PropTypes.bool, // whether to enable searching feature or not
searchPromptText: React.PropTypes.string, // label to prompt for search input
value: React.PropTypes.any, // initial field value
- valueRenderer: React.PropTypes.func // valueRenderer: function(option) {}
+ valueRenderer: React.PropTypes.func, // valueRenderer: function(option) {}
+ optionComponent: React.PropTypes.func, // option component to render in dropdown
+ valueComponent: React.PropTypes.func, // value component to render in multiple mode
+ singleValueComponent: React.PropTypes.func,// single value component when multiple is set to false
+ newOptionCreator: React.PropTypes.func // factory to create new options when allowCreate set
},
getDefaultProps: function() {
@@ -72,7 +78,11 @@ var Select = React.createClass({
placeholder: 'Select...',
searchable: true,
searchPromptText: 'Type to search',
- value: undefined
+ value: undefined,
+ optionComponent: Option,
+ valueComponent: Value,
+ singleValueComponent: SingleValue,
+ newOptionCreator: undefined
};
},
@@ -659,11 +669,12 @@ var Select = React.createClass({
if (this.props.allowCreate && this.state.inputValue.trim()) {
var inputValue = this.state.inputValue;
options = options.slice();
- options.unshift({
+ var newOption = this.props.newOptionCreator ? this.props.newOptionCreator(inputValue) : {
value: inputValue,
label: inputValue,
create: true
- });
+ };
+ options.unshift(newOption);
}
var ops = Object.keys(options).map(function(key) {
@@ -683,13 +694,21 @@ var Select = React.createClass({
var mouseEnter = this.focusOption.bind(this, op);
var mouseLeave = this.unfocusOption.bind(this, op);
var mouseDown = this.selectValue.bind(this, op);
- var renderedLabel = renderLabel(op);
- return op.disabled ? (
-
{renderedLabel}
- ) : (
-
{ op.create ? this.props.addLabelText.replace('{label}', op.label) : renderedLabel}
- );
+ var optionResult = React.createElement(this.props.optionComponent, {
+ key: 'option-' + op.value,
+ className: optionClass,
+ renderFunc: renderLabel,
+ mouseEnter: mouseEnter,
+ mouseLeave: mouseLeave,
+ mouseDown: mouseDown,
+ click: mouseDown,
+ addLabelText: this.props.addLabelText,
+ option: op,
+ ref: ref
+ });
+
+ return optionResult;
}, this);
return ops.length ? ops : (
@@ -720,29 +739,39 @@ var Select = React.createClass({
if (this.props.multi) {
this.state.values.forEach(function(val) {
- value.push(
);
+ var onOptionLabelClick = this.handleOptionLabelClick.bind(this, val);
+ var onRemove = this.removeValue.bind(this, val);
+
+ var valueComponent = React.createElement(this.props.valueComponent, {
+ key: val.value,
+ option: val,
+ renderer: this.props.valueRenderer,
+ optionLabelClick: !!this.props.onOptionLabelClick,
+ onOptionLabelClick: onOptionLabelClick,
+ onRemove: onRemove,
+ disabled: this.props.disabled
+ });
+
+ value.push(valueComponent);
}, this);
}
if(!this.state.inputValue && (!this.props.multi || !value.length)) {
+ var val = this.state.values[0] || null;
if(this.props.valueRenderer && !!this.state.values.length) {
- var val = this.state.values[0] || null;
value.push(
);
} else {
- value.push(
{this.state.placeholder}
);
+ var singleValueComponent = React.createElement(this.props.singleValueComponent, {
+ key: 'placeholder',
+ value: val,
+ placeholder: this.state.placeholder
+ });
+ value.push(singleValueComponent);
}
-
}
var loading = this.state.isLoading ?
: null;
diff --git a/src/SingleValue.js b/src/SingleValue.js
new file mode 100644
index 0000000000..7dc7106a32
--- /dev/null
+++ b/src/SingleValue.js
@@ -0,0 +1,16 @@
+var React = require('react');
+
+var SingleValue = React.createClass({
+ propTypes: {
+ placeholder: React.PropTypes.string, // this is default value provided by React-Select based component
+ value: React.PropTypes.object // selected option
+ },
+
+ render: function() {
+ return (
+
{this.props.placeholder}
+ );
+ }
+});
+
+module.exports = SingleValue;
diff --git a/src/Value.js b/src/Value.js
index f6ead70ad3..0226dc26b3 100644
--- a/src/Value.js
+++ b/src/Value.js
@@ -5,12 +5,12 @@ var Value = React.createClass({
displayName: 'Value',
propTypes: {
- disabled: React.PropTypes.bool,
- onOptionLabelClick: React.PropTypes.func,
- onRemove: React.PropTypes.func,
- option: React.PropTypes.object.isRequired,
- optionLabelClick: React.PropTypes.bool,
- renderer: React.PropTypes.func
+ disabled: React.PropTypes.bool, // disabled prop passed to ReactSelect
+ onOptionLabelClick: React.PropTypes.func, // method to handle click on value label
+ onRemove: React.PropTypes.func, // method to handle remove of that value
+ option: React.PropTypes.object.isRequired, // option passed to component
+ optionLabelClick: React.PropTypes.bool, // indicates if onOptionLabelClick should be handled
+ renderer: React.PropTypes.func // method to render option label passed to ReactSelect
},
blockEvent: function(event) {