Skip to content

Commit

Permalink
Merge pull request #66 from dmitry-smirnov/option-label-as-link
Browse files Browse the repository at this point in the history
Improvement: Option label as a link
  • Loading branch information
JedWatson committed Feb 20, 2015
2 parents 738a438 + f4c51e9 commit b3d2564
Show file tree
Hide file tree
Showing 13 changed files with 571 additions and 108 deletions.
17 changes: 14 additions & 3 deletions dist/default.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,25 @@
.Select-control:hover {
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06);
}
.is-searchable.is-open > .Select-control {
cursor: text;
}
.is-open > .Select-control {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
background: white;
border-color: #b3b3b3 #cccccc #d9d9d9;
cursor: text;
}
.is-open > .Select-control > .Select-arrow {
border-color: transparent transparent #999999;
border-width: 0 5px 5px;
}
.is-searchable.is-focused:not(.is-open) > .Select-control {
cursor: text;
}
.is-focused:not(.is-open) > .Select-control {
border-color: #0088cc #0099e6 #0099e6;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 5px -1px rgba(0, 136, 204, 0.5);
cursor: text;
}
.Select-placeholder {
color: #aaaaaa;
Expand All @@ -49,7 +53,7 @@
left: 0;
max-width: 100%;
overflow: hidden;
text-overflow: ellipses;
text-overflow: ellipsis;
white-space: nowrap;
}
.has-value > .Select-control > .Select-placeholder {
Expand All @@ -70,6 +74,9 @@
.is-focused .Select-input > input {
cursor: text;
}
.Select-control:not(.is-searchable) > .Select-input {
outline: none;
}
.Select-loading {
-webkit-animation: spin 400ms infinite linear;
-o-animation: spin 400ms infinite linear;
Expand Down Expand Up @@ -187,6 +194,10 @@
border-top-right-radius: 2px;
padding: 3px 5px;
}
.Select-item-label .Select-item-label__a {
color: #0088cc;
cursor: pointer;
}
.Select-item-icon {
cursor: pointer;
border-bottom-left-radius: 2px;
Expand Down
136 changes: 111 additions & 25 deletions dist/react-select.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,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
Expand All @@ -26,20 +27,31 @@ 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 <input /> tag
onChange: React.PropTypes.func, // onChange handler: function(newValue) {}
className: React.PropTypes.string, // className for the outer element
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 <a>label</a> tag.
*
* onOptionLabelClick handler: function (value, event) {}
* */
onOptionLabelClick: React.PropTypes.func
},

getDefaultProps: function () {
return {
value: undefined,
options: [],
disabled: false,
delimiter: ",",
asyncOptions: undefined,
autoload: true,
Expand All @@ -48,12 +60,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
};
},

Expand Down Expand Up @@ -106,7 +121,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);
}
Expand Down Expand Up @@ -205,6 +220,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);
Expand All @@ -213,10 +233,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) {
Expand All @@ -225,7 +246,7 @@ var Select = React.createClass({
});
} else {
this._openAfterFocus = true;
this.refs.input.focus();
this.getInputNode().focus();
}
},

Expand All @@ -248,6 +269,8 @@ var Select = React.createClass({
},

handleKeyDown: function (event) {
if (this.state.disabled) return;

switch (event.keyCode) {

case 8:
Expand Down Expand Up @@ -356,6 +379,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;
Expand Down Expand Up @@ -473,12 +500,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
});

Expand All @@ -488,13 +525,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" },
Expand All @@ -503,22 +542,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: "&times;" } }) : 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: "&times;" } }) : 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
Expand All @@ -533,29 +591,41 @@ module.exports = Select;

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./Value":3,"classnames":2}],2:[function(require,module,exports){
function classnames() {
var args = arguments, classes = [];
function classNames() {
var args = arguments;
var classes = [];

for (var i = 0; i < args.length; i++) {
if (args[i] && 'string' === typeof args[i]) {
classes.push(args[i]);
} else if ('object' === typeof args[i]) {
classes = classes.concat(Object.keys(args[i]).filter(function(cls) {
return args[i][cls];
}));
var arg = args[i];
if (!arg) {
continue;
}

if ('string' === typeof arg || 'number' === typeof arg) {
classes.push(arg);
} else if ('object' === typeof arg) {
for (var key in arg) {
if (!arg.hasOwnProperty(key) || !arg[key]) {
continue;
}
classes.push(key);
}
}
}
return classes.join(' ') || undefined;
return classes.join(' ');
}

module.exports = classnames;
// safely export classNames in case the script is included directly on a page
if (typeof module !== 'undefined' && module.exports) {
module.exports = classNames;
}

},{}],3:[function(require,module,exports){
(function (global){
"use strict";

var _ = (typeof window !== "undefined" ? window._ : typeof global !== "undefined" ? global._ : null),
React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null),
classes = require("classnames");
React = (typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null);

var Option = React.createClass({

Expand All @@ -570,18 +640,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
)
);
}
Expand All @@ -591,5 +677,5 @@ var Option = React.createClass({
module.exports = Option;

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"classnames":2}]},{},[1])(1)
},{}]},{},[1])(1)
});
Loading

0 comments on commit b3d2564

Please sign in to comment.