Last active
August 29, 2015 14:17
-
-
Save bobpace/a5c28a1da4f0b4308780 to your computer and use it in GitHub Desktop.
React MultiSelect with item filter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var React = require('react'); | |
var Input = require('react-bootstrap/src/Input'); | |
var DropdownButton = require('react-bootstrap/src/DropdownButton'); | |
var _ = require('lodash'); | |
var escape = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g; | |
function escapeRegExp(str) { | |
return str.replace(escape, "\\$&"); | |
}; | |
var ListItemCheckbox = React.createClass({ | |
getDefaultProps() { | |
return { | |
text: '', | |
onChange: _.noop | |
}; | |
}, | |
render() { | |
var checkboxProps = {}; | |
if (this.props.checked !== undefined) { | |
checkboxProps.checked = this.props.checked | |
} | |
return ( | |
<li className="checkbox"> | |
<label> | |
<input type="checkbox" onChange={this.props.onChange} {...checkboxProps} /> | |
<span>{this.props.text}</span> | |
</label> | |
</li> | |
); | |
} | |
}); | |
var MultiSelect = React.createClass({ | |
getDefaultProps() { | |
return { | |
items: [], | |
placeholder: 'Enter some filter text', | |
onChange: _.noop, | |
onItemSelected: _.noop, | |
onItemDeselected: _.noop | |
}; | |
}, | |
getInitialState() { | |
return { | |
selections: [], | |
filter: '' | |
}; | |
}, | |
handleItemClick(item) { | |
this.setSelected(item, !_.contains(this.state.selections, item.id)); | |
}, | |
handleFilterChange(event) { | |
// Keep track of every change to the filter input | |
this.setState({ filter: event.target.value }); | |
}, | |
createItem(item) { | |
var regex = new RegExp(escapeRegExp(this.state.filter), 'i'); | |
var text = item.text || item.name || item.id; | |
return regex.test(text) ? ( | |
<ListItemCheckbox | |
key={item.id} | |
text={text} | |
onChange={this.handleItemClick.bind(this, item)} | |
checked={_.contains(this.state.selections, item.id)} | |
/> | |
) : null; | |
}, | |
setSelected(items, selected) { | |
// Accept an array or a single item | |
if (!(items instanceof Array)) { | |
items = [items]; | |
} | |
var selections = this.state.selections; | |
var update = selected ? function(x) { | |
selections.push(x.id); | |
} : function(x) { | |
_.pull(selections, x.id); | |
}; | |
items.forEach(update); | |
this.setState({selections: selections}); | |
this.props.onChange(selections); | |
}, | |
toggleSelectAll(event) { | |
this.setSelected(this.props.items, event.target.checked); | |
}, | |
render() { | |
var totalSelected = this.state.selections.length; | |
var title = totalSelected ? totalSelected + " selected" : "--"; | |
var selectAll = _.isEmpty(this.state.filter) ? ( | |
<ListItemCheckbox text="Select All" onChange={this.toggleSelectAll} /> | |
) : null; | |
return ( | |
<div className="multiselect"> | |
<DropdownButton title={title}> | |
<li> | |
<input | |
type="text" | |
onChange={this.handleFilterChange} | |
value={this.state.filter} | |
placeholder={this.props.placeholder} | |
/> | |
</li> | |
{selectAll} | |
<ul>{this.props.items.map(this.createItem)}</ul> | |
</DropdownButton> | |
</div> | |
) | |
} | |
}) | |
module.exports = MultiSelect; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment