react-selector
Version:
A React component that allows to filter and move item between two lists.
201 lines (191 loc) • 6.52 kB
JavaScript
// Generated by CoffeeScript 1.9.1
(function() {
var ARROW_DOWN, ARROW_UP, BACKSPACE, ENTER, ESCAPE, React, ReactSelector;
React = require('react');
ARROW_UP = 38;
ARROW_DOWN = 40;
ENTER = 13;
BACKSPACE = 8;
ESCAPE = 27;
ReactSelector = React.createClass({
getInitialState: function() {
var query;
query = "";
return {
filtered_items_on_top: this.props.filtered_items_on_top || false,
show_filtered_items: false,
query: query,
active_item_index: 0,
filtered: this._calculateFiltered(this.props.universe, this.props.selected, query),
selected: [].concat(this.props.selected).sort(this.props.compare)
};
},
componentWillReceiveProps: function(nextProps) {
var active_item_index, filtered, query, selected;
query = "";
active_item_index = 0;
selected = nextProps.selected;
filtered = this._calculateFiltered(nextProps.universe, selected, query);
return this.setState({
active_item_index: active_item_index,
query: query,
filtered: filtered,
selected: selected.sort(this.props.compare)
});
},
componentDidUpdate: function() {
if (this.refs.active) {
return this.refs.active.getDOMNode().scrollIntoView(false);
}
},
_calculateFiltered: function(universe, selected, query) {
var filtered, item, j, len;
filtered = [];
for (j = 0, len = universe.length; j < len; j++) {
item = universe[j];
if (!this.__arrayContainsObject(selected, item)) {
if (item.name.toLowerCase().trim().indexOf(query.toLowerCase().trim()) !== -1) {
filtered.push(item);
}
}
}
filtered.sort(this.props.compare);
return filtered;
},
__arrayContainsObject: function(array, object) {
var j, len, o;
for (j = 0, len = array.length; j < len; j++) {
o = array[j];
if (object.id === o.id) {
return true;
}
}
return false;
},
_getItems: function(list, filtering) {
var i, is_active_item, item, item_class_name, item_component, items, j, len;
if (filtering == null) {
filtering = false;
}
items = [];
for (i = j = 0, len = list.length; j < len; i = ++j) {
item = list[i];
item_class_name = "item";
is_active_item = this.state.active_item_index === i;
if (filtering && is_active_item) {
item_class_name += " active";
}
item_component = React.createElement(item.renderer, item.props);
items.push(React.DOM.div({
ref: is_active_item ? "active" : void 0,
key: item.id,
onClick: this._onItemToggle.bind(null, item),
className: item_class_name
}, item_component));
}
return items;
},
_onItemToggle: function(item) {
var input;
item.onToggle(item.id);
clearTimeout(this._timeout);
input = this.refs.input.getDOMNode();
return input.focus();
},
_processActions: function(event) {
var active_item, active_item_index, filtered, key, selected;
key = event.keyCode;
active_item_index = this.state.active_item_index;
filtered = this.state.filtered;
selected = this.state.selected;
if (key === ARROW_DOWN) {
this._showFilteredItems();
if (active_item_index < filtered.length - 1) {
return this.setState({
active_item_index: active_item_index + 1
});
}
} else if (key === ARROW_UP) {
this._showFilteredItems();
if (active_item_index > 0) {
return this.setState({
active_item_index: active_item_index - 1
});
}
} else if (key === ENTER) {
active_item = filtered[active_item_index];
if (active_item) {
this._onItemToggle(active_item);
}
return event.preventDefault();
} else if (key === BACKSPACE && this.state.query === '' && selected.length > 0) {
return this._onItemToggle(selected[selected.length - 1]);
} else if (key === ESCAPE) {
return this._hideFilteredItems();
}
},
_processQuery: function(event) {
var active_item_index, filtered, input, query;
this._showFilteredItems();
input = event.target;
query = input.value;
if (query === this.state.query) {
return;
}
filtered = this._calculateFiltered(this.props.universe, this.state.selected, query);
active_item_index = this.state.active_item_index;
if (active_item_index >= filtered.length && filtered.length > 0) {
active_item_index = filtered.length - 1;
}
return this.setState({
query: query,
filtered: filtered,
active_item_index: active_item_index
});
},
_showFilteredItems: function() {
if (this.props.onFocus) {
this.props.onFocus();
}
return this.setState({
show_filtered_items: true
});
},
_hideFilteredItems: function() {
return this._timeout = setTimeout(((function(_this) {
return function() {
if (_this.props.onBlur) {
_this.props.onBlur();
}
return _this.setState({
show_filtered_items: false
});
};
})(this)), 0);
},
render: function() {
var filtered_items, filtered_items_section, selected_items;
filtered_items = this._getItems(this.state.filtered, true);
selected_items = this._getItems(this.state.selected);
filtered_items_section = React.DOM.div({
className: "universe"
}, React.DOM.div({
className: "container"
}, filtered_items));
return React.DOM.div({}, this.state.show_filtered_items && this.state.filtered_items_on_top ? filtered_items_section : void 0, React.DOM.div({
className: "selected"
}, React.DOM.div({
className: "container"
}, selected_items, React.DOM.input({
ref: "input",
value: this.state.query,
placeholder: this.props.placeholder,
onKeyDown: this._processActions,
onChange: this._processQuery,
onFocus: this._showFilteredItems,
onBlur: this._hideFilteredItems
}))), this.state.show_filtered_items && !this.state.filtered_items_on_top ? filtered_items_section : void 0);
}
});
module.exports = ReactSelector;
}).call(this);