react-search-autocomplete
Version:
A search box for React
190 lines (189 loc) • 12.3 kB
JavaScript
var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
return cooked;
};
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MAX_RESULTS = exports.DEFAULT_INPUT_DEBOUNCE = void 0;
var jsx_runtime_1 = require("react/jsx-runtime");
var fuse_js_1 = __importDefault(require("fuse.js"));
var react_1 = __importStar(require("react"));
var styled_components_1 = __importStar(require("styled-components"));
var config_1 = require("../config/config");
var utils_1 = require("../utils/utils");
var Results_1 = __importDefault(require("./Results"));
var SearchInput_1 = __importDefault(require("./SearchInput"));
exports.DEFAULT_INPUT_DEBOUNCE = 200;
exports.MAX_RESULTS = 10;
function ReactSearchAutocomplete(_a) {
var _b = _a.items, items = _b === void 0 ? [] : _b, _c = _a.fuseOptions, fuseOptions = _c === void 0 ? config_1.defaultFuseOptions : _c, _d = _a.inputDebounce, inputDebounce = _d === void 0 ? exports.DEFAULT_INPUT_DEBOUNCE : _d, _e = _a.onSearch, onSearch = _e === void 0 ? function () { } : _e, _f = _a.onHover, onHover = _f === void 0 ? function () { } : _f, _g = _a.onSelect, onSelect = _g === void 0 ? function () { } : _g, _h = _a.onFocus, onFocus = _h === void 0 ? function () { } : _h, _j = _a.onClear, onClear = _j === void 0 ? function () { } : _j, _k = _a.showIcon, showIcon = _k === void 0 ? true : _k, _l = _a.showClear, showClear = _l === void 0 ? true : _l, _m = _a.maxResults, maxResults = _m === void 0 ? exports.MAX_RESULTS : _m, _o = _a.placeholder, placeholder = _o === void 0 ? '' : _o, _p = _a.autoFocus, autoFocus = _p === void 0 ? false : _p, _q = _a.styling, styling = _q === void 0 ? {} : _q, _r = _a.resultStringKeyName, resultStringKeyName = _r === void 0 ? 'name' : _r, _s = _a.inputSearchString, inputSearchString = _s === void 0 ? '' : _s, formatResult = _a.formatResult, _t = _a.showNoResults, showNoResults = _t === void 0 ? true : _t, _u = _a.showNoResultsText, showNoResultsText = _u === void 0 ? 'No results' : _u, _v = _a.showItemsOnFocus, showItemsOnFocus = _v === void 0 ? false : _v, _w = _a.maxLength, maxLength = _w === void 0 ? 0 : _w, className = _a.className;
var theme = __assign(__assign({}, config_1.defaultTheme), styling);
var options = __assign(__assign({}, config_1.defaultFuseOptions), fuseOptions);
var fuse = new fuse_js_1.default(items, options);
fuse.setCollection(items);
var _x = (0, react_1.useState)(inputSearchString), searchString = _x[0], setSearchString = _x[1];
var _y = (0, react_1.useState)([]), results = _y[0], setResults = _y[1];
var _z = (0, react_1.useState)(-1), highlightedItem = _z[0], setHighlightedItem = _z[1];
var _0 = (0, react_1.useState)(false), isSearchComplete = _0[0], setIsSearchComplete = _0[1];
var _1 = (0, react_1.useState)(false), isTyping = _1[0], setIsTyping = _1[1];
var _2 = (0, react_1.useState)(false), showNoResultsFlag = _2[0], setShowNoResultsFlag = _2[1];
var _3 = (0, react_1.useState)(false), hasFocus = _3[0], setHasFocus = _3[1];
(0, react_1.useEffect)(function () {
setSearchString(inputSearchString);
var timeoutId = setTimeout(function () { return setResults(fuseResults(inputSearchString)); }, 0);
return function () { return clearTimeout(timeoutId); };
}, [inputSearchString]);
(0, react_1.useEffect)(function () {
(searchString === null || searchString === void 0 ? void 0 : searchString.length) > 0 &&
results &&
(results === null || results === void 0 ? void 0 : results.length) > 0 &&
setResults(fuseResults(searchString));
}, [items]);
(0, react_1.useEffect)(function () {
if (showNoResults &&
searchString.length > 0 &&
!isTyping &&
results.length === 0 &&
!isSearchComplete) {
setShowNoResultsFlag(true);
}
else {
setShowNoResultsFlag(false);
}
}, [isTyping, showNoResults, isSearchComplete, searchString, results]);
(0, react_1.useEffect)(function () {
if (showItemsOnFocus && results.length === 0 && searchString.length === 0 && hasFocus) {
setResults(items.slice(0, maxResults));
}
}, [showItemsOnFocus, results, searchString, hasFocus]);
(0, react_1.useEffect)(function () {
var handleDocumentClick = function () {
eraseResults();
setHasFocus(false);
};
document.addEventListener('click', handleDocumentClick);
return function () { return document.removeEventListener('click', handleDocumentClick); };
}, []);
var handleOnFocus = function (event) {
onFocus(event);
setHasFocus(true);
};
var callOnSearch = function (keyword) {
var newResults = [];
(keyword === null || keyword === void 0 ? void 0 : keyword.length) > 0 && (newResults = fuseResults(keyword));
setResults(newResults);
onSearch(keyword, newResults);
setIsTyping(false);
};
var handleOnSearch = react_1.default.useCallback(inputDebounce > 0
? (0, utils_1.debounce)(function (keyword) { return callOnSearch(keyword); }, inputDebounce)
: function (keyword) { return callOnSearch(keyword); }, [items]);
var handleOnClick = function (result) {
eraseResults();
onSelect(result);
setSearchString(result[resultStringKeyName]);
setHighlightedItem(0);
};
var fuseResults = function (keyword) {
return fuse
.search(keyword, { limit: maxResults })
.map(function (result) { return (__assign({}, result.item)); })
.slice(0, maxResults);
};
var handleSetSearchString = function (_a) {
var target = _a.target;
var keyword = target.value;
setSearchString(keyword);
handleOnSearch(keyword);
setIsTyping(true);
if (isSearchComplete) {
setIsSearchComplete(false);
}
};
var eraseResults = function () {
setResults([]);
setIsSearchComplete(true);
};
var handleSetHighlightedItem = function (_a) {
var index = _a.index, event = _a.event;
var itemIndex = -1;
var setValues = function (index) {
setHighlightedItem(index);
(results === null || results === void 0 ? void 0 : results[index]) && onHover(results[index]);
};
if (index !== undefined) {
setHighlightedItem(index);
(results === null || results === void 0 ? void 0 : results[index]) && onHover(results[index]);
}
else if (event) {
switch (event.key) {
case 'Enter':
if (results.length > 0 && results[highlightedItem]) {
event.preventDefault();
onSelect(results[highlightedItem]);
setSearchString(results[highlightedItem][resultStringKeyName]);
onSearch(results[highlightedItem][resultStringKeyName], results);
}
else {
onSearch(searchString, results);
}
setHighlightedItem(-1);
eraseResults();
break;
case 'ArrowUp':
event.preventDefault();
itemIndex = highlightedItem > -1 ? highlightedItem - 1 : results.length - 1;
setValues(itemIndex);
break;
case 'ArrowDown':
event.preventDefault();
itemIndex = highlightedItem < results.length - 1 ? highlightedItem + 1 : -1;
setValues(itemIndex);
break;
default:
break;
}
}
};
return ((0, jsx_runtime_1.jsx)(styled_components_1.ThemeProvider, __assign({ theme: theme }, { children: (0, jsx_runtime_1.jsx)(StyledReactSearchAutocomplete, __assign({ className: className }, { children: (0, jsx_runtime_1.jsxs)("div", __assign({ className: "wrapper" }, { children: [(0, jsx_runtime_1.jsx)(SearchInput_1.default, { searchString: searchString, setSearchString: handleSetSearchString, eraseResults: eraseResults, autoFocus: autoFocus, onFocus: handleOnFocus, onClear: onClear, placeholder: placeholder, showIcon: showIcon, showClear: showClear, setHighlightedItem: handleSetHighlightedItem, maxLength: maxLength }), (0, jsx_runtime_1.jsx)(Results_1.default, { results: results, onClick: handleOnClick, setSearchString: setSearchString, showIcon: showIcon, maxResults: maxResults, resultStringKeyName: resultStringKeyName, formatResult: formatResult, highlightedItem: highlightedItem, setHighlightedItem: handleSetHighlightedItem, showNoResultsFlag: showNoResultsFlag, showNoResultsText: showNoResultsText })] })) })) })));
}
exports.default = ReactSearchAutocomplete;
var StyledReactSearchAutocomplete = styled_components_1.default.div(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n position: relative;\n\n height: ", ";\n\n .wrapper {\n position: absolute;\n display: flex;\n flex-direction: column;\n width: 100%;\n\n border: ", ";\n border-radius: ", ";\n\n background-color: ", ";\n color: ", ";\n\n font-size: ", ";\n font-family: ", ";\n\n z-index: ", ";\n\n &:hover {\n box-shadow: ", ";\n }\n &:active {\n box-shadow: ", ";\n }\n &:focus-within {\n box-shadow: ", ";\n }\n }\n"], ["\n position: relative;\n\n height: ", ";\n\n .wrapper {\n position: absolute;\n display: flex;\n flex-direction: column;\n width: 100%;\n\n border: ", ";\n border-radius: ", ";\n\n background-color: ", ";\n color: ", ";\n\n font-size: ", ";\n font-family: ", ";\n\n z-index: ", ";\n\n &:hover {\n box-shadow: ", ";\n }\n &:active {\n box-shadow: ", ";\n }\n &:focus-within {\n box-shadow: ", ";\n }\n }\n"])), function (props) { return parseInt(props.theme.height) + 2 + 'px'; }, function (props) { return props.theme.border; }, function (props) { return props.theme.borderRadius; }, function (props) { return props.theme.backgroundColor; }, function (props) { return props.theme.color; }, function (props) { return props.theme.fontSize; }, function (props) { return props.theme.fontFamily; }, function (props) { return props.theme.zIndex; }, function (props) { return props.theme.boxShadow; }, function (props) { return props.theme.boxShadow; }, function (props) { return props.theme.boxShadow; });
var templateObject_1;
;