armstrong-react
Version:
Rocketmakers Armstrong library of React components
195 lines (194 loc) • 12.5 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var React = require("react");
var ReactDOM = require("react-dom");
var _ = require("underscore");
var icon_1 = require('./../display/icon');
var grid_1 = require('./../layout/grid');
var button_1 = require('./../interaction/button');
var DropdownSelect = (function (_super) {
__extends(DropdownSelect, _super);
function DropdownSelect(props) {
_super.call(this, props);
this.itemHeight = 52;
this.state = { filteredOptions: [], query: "", open: false, selectedValue: props.multiSelect ? [] : null, selectedIndex: 0, remoteSearching: false, offsetIndex: 0 };
}
DropdownSelect.prototype.filterRemote = function (query, immediate) {
var _this = this;
if (this.timer) {
window.clearTimeout(this.timer);
}
this.timer = window.setTimeout(function () {
_this.setState({ remoteSearching: true });
_this.props.remoteQuery(query).then(function (filteredOptions) {
_this.setState({ filteredOptions: filteredOptions, remoteSearching: false });
});
}, immediate ? 0 : this.props.remoteThrottle);
};
DropdownSelect.prototype.filter = function (query) {
var _this = this;
var q = query.toLowerCase();
if (this.props.remoteQuery) {
this.filterRemote(query);
}
else {
if (query.length < this.props.minimumLength) {
this.setState({ filteredOptions: this.props.options, query: query }, function () { return _this.constrainIndex(); });
}
else {
this.setState({ filteredOptions: _.reject(this.props.options, function (o) { return o.name.toLowerCase().indexOf(q) === -1; }), query: query }, function () { return _this.constrainIndex(); });
;
}
}
};
DropdownSelect.prototype.focusInput = function (e) {
var _this = this;
if (!this.state.open && !e.target.classList.contains("clear-selected")) {
this.setState({ open: true }, function () {
ReactDOM.findDOMNode(_this).querySelector("input").focus();
document.addEventListener("click", _this, false);
if (_this.props.remoteQueryOnOpen) {
_this.filterRemote("", true);
}
});
}
else {
}
};
DropdownSelect.prototype.handleEvent = function (e) {
if (ReactDOM.findDOMNode(this).contains(e.target) || e.target.classList.contains("multi-select-item-part")) {
return;
}
this.setState({ open: false, query: "", filteredOptions: this.props.options || [] });
document.removeEventListener("click", this, false);
};
DropdownSelect.prototype.componentWillMount = function () {
var selectedValue = this.props.multiSelect ? [] : null;
if (this.props.value) {
selectedValue = this.props.value;
}
this.setState({ filteredOptions: this.props.options || [], selectedValue: selectedValue });
};
DropdownSelect.prototype.componentWillReceiveProps = function (newProps) {
if (this.props.multiSelect) {
var newMultiValue = newProps.value;
var oldMultiValue = this.state.selectedValue;
if (oldMultiValue.length === 0 || !_.isEqual(newMultiValue.map(function (v) { return v.id; }), oldMultiValue.map(function (v) { return v.id; }))) {
this.setState({ selectedValue: newMultiValue });
}
}
else {
var newSingleValue = newProps.value;
var oldSingleValue = this.state.selectedValue;
if (!oldSingleValue || newSingleValue.id !== oldSingleValue.id) {
this.setState({ selectedValue: newSingleValue });
}
}
};
DropdownSelect.prototype.checkKey = function (e) {
var currentIndex = this.state.selectedIndex;
if (e.keyCode === 27) {
this.setState({ open: false, query: "", filteredOptions: this.props.options || [] });
}
if (e.keyCode === 40 && this.state.filteredOptions.length !== 0) {
var offsetIndex = Math.min((this.props.visibleItems || 3) - 1, this.state.offsetIndex + 1);
var selectedIndex = Math.min(this.state.selectedIndex + 1, this.state.filteredOptions.length - 1);
var listElement = ReactDOM.findDOMNode(this).querySelector(".dropdown-select-list");
this.setState({ offsetIndex: offsetIndex });
if (offsetIndex >= 2) {
listElement.scrollTop = (selectedIndex - 2) * this.itemHeight;
}
var selectedItem = this.state.filteredOptions[selectedIndex];
this.setState({ selectedIndex: selectedIndex, query: selectedItem.name });
e.preventDefault();
return false;
}
if (e.keyCode === 38 && this.state.filteredOptions.length !== 0) {
var offsetIndex = Math.max(this.state.offsetIndex - 1, 0);
var selectedIndex = Math.max(this.state.selectedIndex - 1, 0);
var listElement = ReactDOM.findDOMNode(this).querySelector(".dropdown-select-list");
this.setState({ offsetIndex: offsetIndex });
if (offsetIndex === 0) {
listElement.scrollTop = (selectedIndex) * this.itemHeight;
}
var selectedItem = this.state.filteredOptions[selectedIndex];
this.setState({ selectedIndex: selectedIndex, query: selectedItem.name });
e.preventDefault();
return false;
}
if (e.keyCode === 13 && this.state.filteredOptions.length !== 0) {
var selectedValue = this.state.filteredOptions[this.state.selectedIndex];
this.handleSelection(selectedValue);
e.preventDefault();
return false;
}
this.filter(e.target.value);
};
DropdownSelect.prototype.constrainIndex = function () {
if (this.state.selectedIndex > this.state.filteredOptions.length - 1) {
this.setState({ selectedIndex: Math.max(this.state.filteredOptions.length - 1, 0) });
}
};
DropdownSelect.prototype.handleSelection = function (options) {
if (this.props.multiSelect) {
var newOptions = options;
if (!_.isArray(options)) {
newOptions = [options];
}
var ddOptions = this.state.selectedValue;
newOptions.forEach(function (option) {
if (ddOptions.length !== 0 && _.some(ddOptions, function (ddo) { return ddo.id === option.id; })) {
ddOptions = _.reject(ddOptions, function (ddo) { return ddo.id === option.id; });
}
else {
ddOptions.push(option);
}
});
this.setState({ selectedValue: ddOptions });
if (this.props.onSelected) {
this.props.onSelected(ddOptions);
}
ReactDOM.findDOMNode(this).querySelector("input").focus();
}
else {
var option = options;
this.setState({ selectedValue: option, open: false, query: "", filteredOptions: this.props.options || [], offsetIndex: 0 });
if (this.props.onSelected) {
this.props.onSelected(option);
}
document.removeEventListener("click", this, false);
}
};
DropdownSelect.prototype.buttonClick = function () {
if (this.state.filteredOptions.length !== 0) {
var selectedValue = this.state.filteredOptions[this.state.selectedIndex];
if (selectedValue) {
this.handleSelection(selectedValue);
}
}
};
DropdownSelect.prototype.render = function () {
var _this = this;
return (React.createElement(grid_1.Grid, {onClick: function (e) { return _this.focusInput(e); }, className: "dropdown-select" + (this.props.className ? " " + this.props.className : '') + (this.props.disabled ? ' disabled' : '') + (this.props.hasGoButton && !this.props.multiSelect ? ' has-go-button' : '') + (this.props.multiSelect && this.state.selectedValue.length !== 0 ? ' has-multiple-options' : '')}, React.createElement(grid_1.Row, null, React.createElement(grid_1.Col, {className: "drop-down-controls"}, (!this.state.open || this.props.multiSelect) && React.createElement(grid_1.Grid, {className: "dropdown-value-display"}, React.createElement(grid_1.Row, null, React.createElement(grid_1.Col, null, this.state.selectedValue &&
React.createElement("div", {className: "selected-value-wrapper"}, this.props.multiSelect ? this.state.selectedValue.map(function (ddo) {
return React.createElement("div", {key: "multi-select-item-" + ddo.id, className: "multi-select-item multi-select-item-part", onClick: function () { return _this.handleSelection(ddo); }}, ddo.name, React.createElement(icon_1.Icon, {className: "multi-select-item-part", icon: icon_1.Icon.Icomoon.cross}));
}) : this.state.selectedValue.name), (this.props.multiSelect && this.state.selectedValue.length === 0) &&
React.createElement("div", {className: "placeholder"}, this.props.placeholder || "start typing to filter results..."), !this.props.multiSelect && this.state.selectedValue === null &&
React.createElement("div", {className: "placeholder"}, this.props.placeholder || "start typing to filter results...")), !this.props.multiSelect && this.state.selectedValue && this.props.canClear &&
React.createElement(grid_1.Col, {fixed: true, className: "clear-selected p-right-xsmall", onClick: function () { return _this.setState({ selectedValue: _this.props.multiSelect ? [] : null, open: false, query: "", filteredOptions: _this.props.options || [] }); }}, React.createElement(icon_1.Icon, {icon: icon_1.Icon.Icomoon.cross})), this.props.multiSelect && this.state.selectedValue.length !== 0 && this.props.canClear &&
React.createElement(grid_1.Col, {fixed: true, className: "clear-selected p-right-xsmall", onClick: function () { return _this.setState({ selectedValue: _this.props.multiSelect ? [] : null, open: false, query: "", filteredOptions: _this.props.options || [] }); }}, React.createElement(icon_1.Icon, {icon: icon_1.Icon.Icomoon.cross})))), this.state.open &&
React.createElement("div", {className: "dropdown-select-list-wrapper"}, React.createElement("input", {type: "text", value: this.state.query, onKeyUp: function (e) { return _this.checkKey(e); }, onChange: function (e) { return _this.setState({ query: e.target.value }); }, placeholder: this.props.placeholder || "start typing to filter results..."}), this.state.remoteSearching && React.createElement(icon_1.Icon, {className: "spinner fg-info", icon: icon_1.Icon.Icomoon.spinner2}), React.createElement("div", {"data-id": "dropdown-select-list", className: "dropdown-select-list", style: { maxHeight: (this.props.visibleItems || 3) * this.itemHeight + "px" }}, this.state.filteredOptions && this.state.filteredOptions.map(function (o, i) {
return React.createElement("div", {"data-index": i, key: "dd-item-" + i, className: "dd-list-item" + (i === _this.state.selectedIndex ? ' selected' : '') + ((_this.props.multiSelect && _.some(_this.state.selectedValue, function (ddo) { return ddo.id === o.id; })) ? ' in-selected-list' : ''), onClick: function () { return _this.handleSelection(o); }}, o.name);
}), this.state.filteredOptions.length === 0 && this.state.query && React.createElement("div", {className: "dd-list-item-no-select"}, this.props.noResultsMessage || "No results...")))), this.props.hasGoButton && !this.props.multiSelect && React.createElement(grid_1.Col, {fixed: true}, React.createElement(button_1.Button, {text: this.props.goButtonContent || "Go", className: "bg-positive", onClick: function () { return _this.buttonClick(); }})))));
};
DropdownSelect.defaultProps = {
remoteThrottle: 500,
minimumLength: 1
};
return DropdownSelect;
}(React.Component));
exports.DropdownSelect = DropdownSelect;