Skip to content
Closed
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
2 changes: 2 additions & 0 deletions examples/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import GithubUsers from './components/GithubUsers';
import CustomComponents from './components/CustomComponents';
import CustomRender from './components/CustomRender';
import Multiselect from './components/Multiselect';
import SearchField from './components/SearchField';
import NumericSelect from './components/NumericSelect';
import Virtualized from './components/Virtualized';
import States from './components/States';
Expand All @@ -23,6 +24,7 @@ ReactDOM.render(
<NumericSelect label="Numeric Values" />
<CustomRender label="Custom Render Methods"/>
<CustomComponents label="Custom Placeholder, Option and Value Components" />
<SearchField label="Searchfield with suggestions" />
{/*
<SelectedValuesField label="Option Creation (tags mode)" options={FLAVOURS} allowCreate hint="Enter a value that's NOT in the list, then hit return" />
*/}
Expand Down
68 changes: 68 additions & 0 deletions examples/src/components/SearchField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import Select from 'react-select';

const FLAVOURS = [
{ label: 'Chocolate', value: 'chocolate' },
{ label: 'Vanilla', value: 'vanilla' },
{ label: 'Strawberry', value: 'strawberry' },
{ label: 'Caramel', value: 'caramel' },
{ label: 'Cookies and Cream', value: 'cookiescream' },
{ label: 'Peppermint', value: 'peppermint' },
];

const WHY_WOULD_YOU = [
{ label: 'Chocolate (are you crazy?)', value: 'chocolate', disabled: true },
].concat(FLAVOURS.slice(1));

var SearchField = React.createClass({
displayName: 'SearchField',
propTypes: {
label: React.PropTypes.string,
},
getInitialState () {
return {
disabled: false,
crazy: false,
options: FLAVOURS,
value: [],
};
},
handleSelectChange (value) {
console.log('You\'ve selected:', value);
this.setState({ value });
},
toggleDisabled (e) {
this.setState({ disabled: e.target.checked });
},
toggleChocolate (e) {
let crazy = e.target.checked;
this.setState({
crazy: crazy,
options: crazy ? WHY_WOULD_YOU : FLAVOURS,
});
},
handleValueSubmit () {
console.log('Submitted value: ', this.state.value, '. Do something interesting with it ...');
},
render () {
return (
<div className="section">
<h3 className="section-heading">{this.props.label}</h3>
<Select multi simpleValue disabled={this.state.disabled} value={this.state.value} placeholder="Select your favourite(s)" options={this.state.options} onChange={this.handleSelectChange} minCharsToOpen={1} showArrow={false} onValueSubmit={this.handleValueSubmit} />

<div className="checkbox-list">
<label className="checkbox">
<input type="checkbox" className="checkbox-control" checked={this.state.disabled} onChange={this.toggleDisabled} />
<span className="checkbox-label">Disable the control</span>
</label>
<label className="checkbox">
<input type="checkbox" className="checkbox-control" checked={this.state.crazy} onChange={this.toggleChocolate} />
<span className="checkbox-label">I do not like Chocolate (disabled the option)</span>
</label>
</div>
</div>
);
}
});

module.exports = SearchField;
2 changes: 1 addition & 1 deletion less/select.less
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
@select-clear-size: floor((@select-input-height / 2));
@select-clear-color: #999;
@select-clear-hover-color: #D0021B; // red
@select-clear-width: (@select-input-internal-height / 2);
@select-clear-width: 25px;

// arrow indicator
@select-arrow-color: #999;
Expand Down
40 changes: 29 additions & 11 deletions src/Select.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const Select = React.createClass({
menuContainerStyle: React.PropTypes.object, // optional style to apply to the menu container
menuRenderer: React.PropTypes.func, // renders a custom menu with options
menuStyle: React.PropTypes.object, // optional style to apply to the menu
minCharsToOpen: React.PropTypes.number,
multi: React.PropTypes.bool, // multi-value input
name: React.PropTypes.string, // generates a hidden <input /> tag with this field name for html forms
newOptionCreator: React.PropTypes.func, // factory to create new options when allowCreate set
Expand All @@ -68,6 +69,7 @@ const Select = React.createClass({
onMenuScrollToBottom: React.PropTypes.func, // fires when the menu is scrolled to the bottom; can be used to paginate options
onOpen: React.PropTypes.func, // fires when the menu is opened
onValueClick: React.PropTypes.func, // onClick handler for value labels: function (value, event) {}
onValueSubmit: React.PropTypes.func, // handler for submitting value on enter key press: function () {}
openAfterFocus: React.PropTypes.bool, // boolean to enable opening dropdown when focused
openOnFocus: React.PropTypes.bool, // always open options menu on focus
optionClassName: React.PropTypes.string, // additional class(es) to apply to the <Option /> elements
Expand All @@ -79,9 +81,10 @@ const Select = React.createClass({
resetValue: React.PropTypes.any, // value to use when you clear the control
scrollMenuIntoView: React.PropTypes.bool, // boolean to enable the viewport to shift so that the full menu fully visible when engaged
searchable: React.PropTypes.bool, // whether to enable searching feature or not
showArrow: React.PropTypes.bool, // whether to display the drop down arrow or not
simpleValue: React.PropTypes.bool, // pass the value to onChange as a simple value (legacy pre 1.0 mode), defaults to false
style: React.PropTypes.object, // optional style to apply to the control
tabIndex: React.PropTypes.string, // optional tab index of the control
tabIndex: React.PropTypes.string, // optional tab index of the control
tabSelectsValue: React.PropTypes.bool, // whether to treat tabbing out while focused to be value selection
value: React.PropTypes.any, // initial field value
valueComponent: React.PropTypes.func, // value component to render
Expand Down Expand Up @@ -114,6 +117,7 @@ const Select = React.createClass({
matchPos: 'any',
matchProp: 'any',
menuBuffer: 0,
minCharsToOpen: 0,
multi: false,
noResultsText: 'No results found',
onBlurResetsInput: true,
Expand All @@ -124,6 +128,7 @@ const Select = React.createClass({
resetValue: null,
scrollMenuIntoView: true,
searchable: true,
showArrow: true,
simpleValue: false,
tabSelectsValue: true,
valueComponent: Value,
Expand Down Expand Up @@ -210,8 +215,7 @@ const Select = React.createClass({
focus () {
if (!this.refs.input) return;
this.refs.input.focus();

if (this.props.openAfterFocus) {
if (this.props.openAfterFocus && this.isMinCharsToOpenReached()) {
this.setState({
isOpen: true,
});
Expand Down Expand Up @@ -269,6 +273,7 @@ const Select = React.createClass({
// for the non-searchable select, toggle the menu
if (!this.props.searchable) {
this.focus();
// TODO: Look more into the searchable = false && minCharsToOpen > 0 flow
return this.setState({
isOpen: !this.state.isOpen,
});
Expand All @@ -280,17 +285,17 @@ const Select = React.createClass({
// Call focus() again here to be safe.
this.focus();

// clears value so that the cursor will be a the end of input then the component re-renders
// clears value so that the cursor will be at the end of input then the component re-renders
this.refs.input.getInput().value = '';

// if the input is focused, ensure the menu is open
// if the input is focused, ensure the menu is open
this.setState({
isOpen: true,
isOpen: this.isMinCharsToOpenReached(),
isPseudoFocused: false,
});
} else {
// otherwise, focus the input and open the menu
this._openAfterFocus = true;
this._openAfterFocus = this.isMinCharsToOpenReached();
this.focus();
}
},
Expand Down Expand Up @@ -335,6 +340,7 @@ const Select = React.createClass({
},

handleInputFocus (event) {
// TODO look more into the inputOnFocus = true && minCharsToOpen > 0 flow
var isOpen = this.state.isOpen || this._openAfterFocus || this.props.openOnFocus;
if (this.props.onFocus) {
this.props.onFocus(event);
Expand Down Expand Up @@ -376,7 +382,7 @@ const Select = React.createClass({
}
}
this.setState({
isOpen: true,
isOpen: event.target.value.length >= this.props.minCharsToOpen,
isPseudoFocused: false,
inputValue: newInputValue
});
Expand All @@ -398,7 +404,12 @@ const Select = React.createClass({
this.selectFocusedOption();
return;
case 13: // enter
if (!this.state.isOpen) return;
if (!this.state.isOpen) {
if (this.state.inputValue === '' && this.props.value.length > 0 && this.props.onValueSubmit) {
this.props.onValueSubmit();
}
return;
};
event.stopPropagation();
this.selectFocusedOption();
break;
Expand Down Expand Up @@ -446,6 +457,10 @@ const Select = React.createClass({
if (!value) return true;
return (multi ? value.length === 0 : Object.keys(value).length === 0);
},

isMinCharsToOpenReached () {
return this.state.inputValue.length >= this.props.minCharsToOpen;
},

getOptionLabel (op) {
return op[this.props.labelKey];
Expand Down Expand Up @@ -493,6 +508,8 @@ const Select = React.createClass({
if (this.props.multi) {
this.addValue(value);
this.setState({
// <= 0 just in case somebody passes in a negative number
isOpen: this.props.minCharsToOpen <= 0,
inputValue: '',
});
} else {
Expand Down Expand Up @@ -557,7 +574,7 @@ const Select = React.createClass({
this._scrollToFocusedOptionOnUpdate = true;
if (!this.state.isOpen) {
this.setState({
isOpen: true,
isOpen: this.isMinCharsToOpenReached(),
inputValue: '',
focusedOption: this._focusedOption || options[dir === 'next' ? 0 : options.length - 1]
});
Expand Down Expand Up @@ -704,6 +721,7 @@ const Select = React.createClass({
},

renderArrow () {
if (!this.props.showArrow) return;
return (
<span className="Select-arrow-zone" onMouseDown={this.handleMouseDownOnArrow}>
<span className="Select-arrow" onMouseDown={this.handleMouseDownOnArrow} />
Expand Down