Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvement: Option label as a link #66

Merged
merged 5 commits into from
Feb 20, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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