+
diff --git a/less/select-multi.less b/less/select-multi.less
index 7bd063bba4..f179e885b7 100644
--- a/less/select-multi.less
+++ b/less/select-multi.less
@@ -51,6 +51,11 @@
cursor: default;
.border-right-radius( @select-item-border-radius );
padding: @select-item-padding-vertical @select-item-padding-horizontal;
+
+ .Select-item-label__a {
+ color: @select-item-color;
+ cursor: pointer;
+ }
}
// icon
diff --git a/lib/Select.js b/lib/Select.js
index ff8665a100..44b1c7b38f 100644
--- a/lib/Select.js
+++ b/lib/Select.js
@@ -15,6 +15,7 @@ var Select = React.createClass({
propTypes: {
value: React.PropTypes.any, // initial field value
multi: React.PropTypes.bool, // multi-value input
+ disabled: React.PropTypes.bool, // whether the Select is disabled or not
options: React.PropTypes.array, // array of options
delimiter: React.PropTypes.string, // delimiter to use to join multiple values
asyncOptions: React.PropTypes.func, // function to call to get options
@@ -24,6 +25,7 @@ var Select = React.createClass({
clearable: React.PropTypes.bool, // should it be possible to reset value
clearValueText: React.PropTypes.string, // title for the "clear" control
clearAllText: React.PropTypes.string, // title for the "clear" control when multi: true
+ searchable: React.PropTypes.bool, // whether to enable searching feature or not
searchPromptText: React.PropTypes.string, // label to prompt for search input
name: React.PropTypes.string, // field name, for hidden
tag
onChange: React.PropTypes.func, // onChange handler: function(newValue) {}
@@ -31,13 +33,23 @@ var Select = React.createClass({
filterOption: React.PropTypes.func, // method to filter a single option: function(option, filterString)
filterOptions: React.PropTypes.func, // method to filter the options array: function([options], filterString, [values])
matchPos: React.PropTypes.string, // (any|start) match the start or entire string when filtering
- matchProp: React.PropTypes.string // (any|label|value) which option property to filter on
+ matchProp: React.PropTypes.string, // (any|label|value) which option property to filter on
+
+ /*
+
+ * Allow user to make option label clickable. When this handler is defined we should
+ * wrap label into
label tag.
+ *
+ * onOptionLabelClick handler: function (value, event) {}
+ * */
+ onOptionLabelClick: React.PropTypes.func
},
getDefaultProps: function () {
return {
value: undefined,
options: [],
+ disabled: false,
delimiter: ",",
asyncOptions: undefined,
autoload: true,
@@ -46,12 +58,15 @@ var Select = React.createClass({
clearable: true,
clearValueText: "Clear value",
clearAllText: "Clear all",
+ searchable: true,
searchPromptText: "Type to search",
name: undefined,
onChange: undefined,
className: undefined,
matchPos: "any",
- matchProp: "any"
+ matchProp: "any",
+
+ onOptionLabelClick: undefined
};
},
@@ -104,7 +119,7 @@ var Select = React.createClass({
if (this._focusAfterUpdate) {
clearTimeout(this._blurTimeout);
this._focusTimeout = setTimeout((function () {
- this.refs.input.focus();
+ this.getInputNode().focus();
this._focusAfterUpdate = false;
}).bind(this), 50);
}
@@ -203,6 +218,11 @@ var Select = React.createClass({
this.setValue(this.state.value);
},
+ getInputNode: function () {
+ var input = this.refs.input;
+ return this.props.searchable ? input : input.getDOMNode();
+ },
+
fireChangeEvent: function (newState) {
if (newState.value !== this.state.value && this.props.onChange) {
this.props.onChange(newState.value, newState.values);
@@ -211,10 +231,11 @@ var Select = React.createClass({
handleMouseDown: function (event) {
// if the event was triggered by a mousedown and not the primary
- // button, ignore it.
- if (event.type == "mousedown" && event.button !== 0) {
+ // button, or if the component is disabled, ignore it.
+ if (this.props.disabled || event.type == "mousedown" && event.button !== 0) {
return;
}
+
event.stopPropagation();
event.preventDefault();
if (this.state.isFocused) {
@@ -223,7 +244,7 @@ var Select = React.createClass({
});
} else {
this._openAfterFocus = true;
- this.refs.input.focus();
+ this.getInputNode().focus();
}
},
@@ -246,6 +267,8 @@ var Select = React.createClass({
},
handleKeyDown: function (event) {
+ if (this.state.disabled) return;
+
switch (event.keyCode) {
case 8:
@@ -354,6 +377,10 @@ var Select = React.createClass({
},
filterOptions: function (options, values) {
+ if (!this.props.searchable) {
+ return options;
+ }
+
var filterValue = this._optionsFilterString;
var exclude = (values || this.state.values).map(function (i) {
return i.value;
@@ -471,12 +498,22 @@ var Select = React.createClass({
);
},
+ handleOptionLabelClick: function (value, event) {
+ var handler = this.props.onOptionLabelClick;
+
+ if (handler) {
+ handler(value, event);
+ }
+ },
+
render: function () {
var selectClass = classes("Select", this.props.className, {
"is-multi": this.props.multi,
+ "is-searchable": this.props.searchable,
"is-open": this.state.isOpen,
"is-focused": this.state.isFocused,
"is-loading": this.state.isLoading,
+ "is-disabled": this.props.disabled,
"has-value": this.state.value
});
@@ -486,13 +523,15 @@ var Select = React.createClass({
this.state.values.forEach(function (val) {
var props = _.extend({
key: val.value,
+ optionLabelClick: !!this.props.onOptionLabelClick,
+ onOptionLabelClick: this.handleOptionLabelClick.bind(this, val),
onRemove: this.removeValue.bind(this, val)
}, val);
value.push(React.createElement(Value, props));
}, this);
}
- if (!this.state.inputValue && (!this.props.multi || !value.length)) {
+ if (this.props.disabled || !this.state.inputValue && (!this.props.multi || !value.length)) {
value.push(React.createElement(
"div",
{ className: "Select-placeholder", key: "placeholder" },
@@ -501,22 +540,41 @@ var Select = React.createClass({
}
var loading = this.state.isLoading ? React.createElement("span", { className: "Select-loading", "aria-hidden": "true" }) : null;
- var clear = this.props.clearable && this.state.value ? React.createElement("span", { className: "Select-clear", title: this.props.multi ? this.props.clearAllText : this.props.clearValueText, "aria-label": this.props.multi ? this.props.clearAllText : this.props.clearValueText, onMouseDown: this.clearValue, onClick: this.clearValue, dangerouslySetInnerHTML: { __html: "×" } }) : null;
+ var clear = this.props.clearable && this.state.value && !this.props.disabled ? React.createElement("span", { className: "Select-clear", title: this.props.multi ? this.props.clearAllText : this.props.clearValueText, "aria-label": this.props.multi ? this.props.clearAllText : this.props.clearValueText, onMouseDown: this.clearValue, onClick: this.clearValue, dangerouslySetInnerHTML: { __html: "×" } }) : null;
var menu = this.state.isOpen ? React.createElement(
"div",
{ ref: "menu", onMouseDown: this.handleMouseDown, className: "Select-menu" },
this.buildMenu()
) : null;
+ var commonProps = {
+ ref: "input",
+ className: "Select-input",
+ tabIndex: this.props.tabIndex || 0,
+ onFocus: this.handleInputFocus,
+ onBlur: this.handleInputBlur
+ };
+ var input;
+
+ if (this.props.searchable && !this.props.disabled) {
+ input = React.createElement(Input, React.__spread({ value: this.state.inputValue, onChange: this.handleInputChange, minWidth: "5" }, commonProps));
+ } else {
+ input = React.createElement(
+ "div",
+ commonProps,
+ " "
+ );
+ }
+
return React.createElement(
"div",
{ ref: "wrapper", className: selectClass },
- React.createElement("input", { type: "hidden", ref: "value", name: this.props.name, value: this.state.value }),
+ React.createElement("input", { type: "hidden", ref: "value", name: this.props.name, value: this.state.value, disabled: this.props.disabled }),
React.createElement(
"div",
{ className: "Select-control", ref: "control", onKeyDown: this.handleKeyDown, onMouseDown: this.handleMouseDown, onTouchEnd: this.handleMouseDown },
value,
- React.createElement(Input, { className: "Select-input", tabIndex: this.props.tabIndex, ref: "input", value: this.state.inputValue, onFocus: this.handleInputFocus, onBlur: this.handleInputBlur, onChange: this.handleInputChange, minWidth: "5" }),
+ input,
React.createElement("span", { className: "Select-arrow" }),
loading,
clear
diff --git a/lib/Value.js b/lib/Value.js
index dd654ad4c1..b75277ec50 100644
--- a/lib/Value.js
+++ b/lib/Value.js
@@ -1,8 +1,7 @@
"use strict";
var _ = require("underscore"),
- React = require("react"),
- classes = require("classnames");
+ React = require("react");
var Option = React.createClass({
@@ -17,18 +16,34 @@ var Option = React.createClass({
},
render: function () {
+ var label = this.props.label;
+
+ if (this.props.optionLabelClick) {
+ label = React.createElement(
+ "a",
+ { className: "Select-item-label__a",
+ onMouseDown: this.blockEvent,
+ onTouchEnd: this.props.onOptionLabelClick,
+ onClick: this.props.onOptionLabelClick },
+ label
+ );
+ }
+
return React.createElement(
"div",
{ className: "Select-item" },
React.createElement(
"span",
- { className: "Select-item-icon", onMouseDown: this.blockEvent, onClick: this.props.onRemove, onTouchEnd: this.props.onRemove },
+ { className: "Select-item-icon",
+ onMouseDown: this.blockEvent,
+ onClick: this.props.onRemove,
+ onTouchEnd: this.props.onRemove },
"×"
),
React.createElement(
"span",
{ className: "Select-item-label" },
- this.props.label
+ label
)
);
}
diff --git a/src/Select.js b/src/Select.js
index 8c97c39085..1f15321939 100644
--- a/src/Select.js
+++ b/src/Select.js
@@ -31,7 +31,16 @@ var Select = React.createClass({
filterOption: React.PropTypes.func, // method to filter a single option: function(option, filterString)
filterOptions: React.PropTypes.func, // method to filter the options array: function([options], filterString, [values])
matchPos: React.PropTypes.string, // (any|start) match the start or entire string when filtering
- matchProp: React.PropTypes.string // (any|label|value) which option property to filter on
+ matchProp: React.PropTypes.string, // (any|label|value) which option property to filter on
+
+ /*
+
+ * Allow user to make option label clickable. When this handler is defined we should
+ * wrap label into
label tag.
+ *
+ * onOptionLabelClick handler: function (value, event) {}
+ * */
+ onOptionLabelClick: React.PropTypes.func
},
getDefaultProps: function() {
@@ -53,7 +62,9 @@ var Select = React.createClass({
onChange: undefined,
className: undefined,
matchPos: 'any',
- matchProp: 'any'
+ matchProp: 'any',
+
+ onOptionLabelClick: undefined
};
},
@@ -194,7 +205,7 @@ var Select = React.createClass({
removeValue: function(value) {
this.setValue(_.without(this.state.values, value));
},
-
+
clearValue: function(event) {
// if the event was triggered by a mousedown and not the primary
// button, ignore it.
@@ -225,6 +236,7 @@ var Select = React.createClass({
if (this.props.disabled || (event.type == 'mousedown' && event.button !== 0)) {
return;
}
+
event.stopPropagation();
event.preventDefault();
if (this.state.isFocused) {
@@ -495,6 +507,14 @@ var Select = React.createClass({
},
+ handleOptionLabelClick: function (value, event) {
+ var handler = this.props.onOptionLabelClick;
+
+ if (handler) {
+ handler(value, event);
+ }
+ },
+
render: function() {
var selectClass = classes('Select', this.props.className, {
@@ -513,8 +533,10 @@ var Select = React.createClass({
this.state.values.forEach(function(val) {
var props = _.extend({
key: val.value,
+ optionLabelClick: !!this.props.onOptionLabelClick,
+ onOptionLabelClick: this.handleOptionLabelClick.bind(this, val),
onRemove: this.removeValue.bind(this, val)
- }, val);
+ }, val);
value.push(
);
}, this);
}
@@ -532,7 +554,7 @@ var Select = React.createClass({
className: 'Select-input',
tabIndex: this.props.tabIndex || 0,
onFocus: this.handleInputFocus,
- onBlur: this.handleInputBlur,
+ onBlur: this.handleInputBlur
};
var input;
diff --git a/src/Value.js b/src/Value.js
index 3c365b2ca3..5301d1f539 100644
--- a/src/Value.js
+++ b/src/Value.js
@@ -1,6 +1,5 @@
var _ = require('underscore'),
- React = require('react'),
- classes = require('classnames');
+ React = require('react');
var Option = React.createClass({
@@ -11,14 +10,32 @@ var Option = React.createClass({
},
blockEvent: function(event) {
- event.stopPropagation();
+ event.stopPropagation();
},
render: function() {
+ var label = this.props.label;
+
+ if (this.props.optionLabelClick) {
+ label = (
+
+
+ {label}
+
+ );
+ }
+
return (
- ×
- {this.props.label}
+ ×
+
+ {label}
);
}