@vtex/styleguide
Version:
> VTEX Styleguide React components ([Docs](https://vtex.github.io/styleguide))
346 lines (285 loc) • 10.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = undefined;
var _react = require("react");
var _react2 = _interopRequireDefault(_react);
var _propTypes = require("prop-types");
var _propTypes2 = _interopRequireDefault(_propTypes);
var _debounce = require("lodash/debounce");
var _debounce2 = _interopRequireDefault(_debounce);
var _Tag = require("./Tag");
var _Tag2 = _interopRequireDefault(_Tag);
var _DropdownList = require("./DropdownList");
var _DropdownList2 = _interopRequireDefault(_DropdownList);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
var MultiSelect =
/*#__PURE__*/
function (_Component) {
_inheritsLoose(MultiSelect, _Component);
function MultiSelect(props) {
var _this;
_this = _Component.call(this, props) || this;
_this.handleFilter = (0, _debounce2.default)(
/*#__PURE__*/
function () {
var _ref = _asyncToGenerator(
/*#__PURE__*/
regeneratorRuntime.mark(function _callee(term) {
var filteredOptions;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return _this.filter(term);
case 2:
filteredOptions = _context.sent;
_this.setState({
loading: false,
filteredOptions: filteredOptions
});
case 4:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return function (_x) {
return _ref.apply(this, arguments);
};
}(), 275, {
trailing: true
});
_this.handleFocus = function () {
_this.setState({
active: true
});
};
_this.handleBlur = function () {
if (!_this.state.hovering) {
_this.setState({
active: false
});
}
};
_this.handleKeyPress = function (event) {
if (_this.state.searchTerm !== '') {
if (event.key === 'Tab' || event.key === 'Enter') {
event.preventDefault();
_this.selectFocused();
} else if (event.key === 'ArrowUp') {
event.preventDefault();
_this.moveFocusUp();
} else if (event.key === 'ArrowDown') {
event.preventDefault();
_this.moveFocusDown();
}
} else if (event.key === 'Backspace') {
_this.unselectLast();
}
};
_this.handleSearch = function (event) {
var searchTerm = event.target.value;
_this.setState(function () {
return {
searchTerm: searchTerm,
focusedOption: 0,
filteredOptions: [],
loading: true
};
}, function () {
_this.handleFilter(searchTerm);
});
};
_this.handleSelect = function (index) {
_this.props.onChange([].concat(_this.props.selected, [_this.state.filteredOptions[index]]));
_this.setState({
hovering: false,
searchTerm: ''
}, function () {
_this.searchInput.current.focus();
});
};
_this.handleUnselect = function (index) {
_this.props.onChange(_this.props.selected.filter(function (opt, i) {
return index !== i;
}));
_this.searchInput.current.focus();
};
_this.filter = function (term) {
if (_this.props.filter) {
return _this.props.filter(term);
}
return _this.props.options.filter(function (opt) {
return opt.label.toLowerCase().includes(term.toLowerCase());
}).filter(function (opt) {
for (var i = 0; i < _this.props.selected.length; i++) {
if (_this.props.selected[i].value === opt.value) {
return false;
}
}
return true;
});
};
_this.moveFocusDown = function () {
var newFocus = _this.state.focusedOption + 1;
if (newFocus <= _this.state.filteredOptions.length - 1) {
_this.setState({
focusedOption: newFocus
});
}
};
_this.moveFocusUp = function () {
var newFocus = _this.state.focusedOption - 1;
if (newFocus >= 0) {
_this.setState({
focusedOption: newFocus
});
}
};
_this.selectFocused = function () {
var index = _this.state.focusedOption;
if (_this.state.filteredOptions.length > 0) {
_this.handleSelect(index);
}
};
_this.unselectLast = function () {
var length = _this.props.selected.length;
if (length > 0) {
_this.handleUnselect(length - 1);
}
};
_this.searchInput = _react2.default.createRef();
_this.state = {
active: false,
filteredOptions: [],
focusedOption: 0,
hovering: false,
loading: false,
searchTerm: ''
};
return _this;
}
var _proto = MultiSelect.prototype;
_proto.render = function render() {
var _this2 = this;
var _this$props = this.props,
disabled = _this$props.disabled,
error = _this$props.error,
errorMessage = _this$props.errorMessage,
label = _this$props.label,
loadingText = _this$props.loadingText,
placeholder = _this$props.placeholder,
selected = _this$props.selected;
var emptyState = this.props.emptyState("<span className=\"fw5\">" + this.state.searchTerm + "</span>");
var isDropdownVisible = this.state.active && this.state.searchTerm !== '';
var hasError = error || errorMessage;
var tags = selected.map(function (tag, index) {
return _react2.default.createElement("div", {
className: "mr2 mv1 flex",
key: index
}, _react2.default.createElement(_Tag2.default, {
disabled: disabled,
onClick: function onClick() {
_this2.handleUnselect(index);
}
}, tag.label));
});
var classes = disabled ? ' bg-muted-5 c-muted-2 ' : ' bg-base c-on-base ';
classes += isDropdownVisible ? ' br--top ' : '';
classes += hasError ? 'b--danger hover-b--danger ' : '';
classes += this.state.active ? ' b--muted-2 ' : !hasError ? ' b--muted-4 ' : '';
classes += !(this.state.active || disabled || hasError) ? ' hover-b--muted-3 ' : '';
return _react2.default.createElement("div", {
className: "relative"
}, _react2.default.createElement("label", null, label && _react2.default.createElement("span", {
className: "vtex-input__label db mb3 w-100 c-on-base"
}, label), _react2.default.createElement("div", {
className: "flex flex-wrap mt3 br2 b--solid bw1 pv2 ph5 " + classes
}, _react2.default.createElement("input", {
className: "t-small mv3 bn outline-0 flex-grow-1 order-last " + classes,
disabled: disabled,
onBlur: this.handleBlur,
onChange: this.handleSearch,
onFocus: this.handleFocus,
onKeyDown: this.handleKeyPress,
placeholder: placeholder,
ref: this.searchInput,
value: this.state.searchTerm,
style: {
WebkitAppearance: 'none'
}
}), tags)), _react2.default.createElement(_DropdownList2.default, {
emptyState: emptyState,
focused: this.state.focusedOption,
formatOption: function formatOption(opt) {
return opt.label.replace(new RegExp(_this2.state.searchTerm, 'i'), '<span className="fw5">$&</span>');
},
loading: this.state.loading,
loadingText: loadingText,
onFocus: function onFocus(opt) {
return _this2.setState({
focusedOption: opt
});
},
onMouseEnter: function onMouseEnter() {
return _this2.setState({
hovering: true
});
},
onMouseLeave: function onMouseLeave() {
return _this2.setState({
hovering: false
});
},
onSelect: this.handleSelect,
options: this.state.filteredOptions,
isVisible: isDropdownVisible
}), errorMessage && _react2.default.createElement("div", {
className: "pt3 t-small c-danger"
}, errorMessage));
};
return MultiSelect;
}(_react.Component);
exports.default = MultiSelect;
MultiSelect.defaultProps = {
disabled: false,
error: false,
errorMessage: '',
emptyState: function emptyState(term) {
return "No results found for \"" + term + "\".";
},
options: [],
placeholder: 'Search...',
selected: []
};
MultiSelect.propTypes = {
/** Error highlight */
error: _propTypes2.default.bool,
/** Error message */
errorMessage: _propTypes2.default.string,
/** True if the component should be disabled */
disabled: _propTypes2.default.bool,
/** Returns a string that will be shown if no results are found. Usage: emptyState(search term) */
emptyState: _propTypes2.default.func,
/** Returns an array of filtered results. Usage: filter(search term) */
filter: _propTypes2.default.func,
/** Label */
label: _propTypes2.default.string,
/** Text that shows during load */
loadingText: _propTypes2.default.string,
/** Called when selected options change. Usage: onChange(selected array) */
onChange: _propTypes2.default.func.isRequired,
/** List of selectable options. */
options: _propTypes2.default.array,
/** Search input placeholder */
placeholder: _propTypes2.default.string,
/** List of selected options, which will be shown as tags */
selected: _propTypes2.default.array
};