NextJS_DOC/pages/Components/AutoCompleteSearch.js

168 lines
5.4 KiB
JavaScript
Raw Permalink Normal View History

2023-03-24 10:11:04 +00:00
import React, { useState, useEffect } from 'react'
import { Card } from 'react-bootstrap';
import { getSearchResults } from './TabulatorData';
import '../jqueryloader'
const Search = () => {
const [searchTerm, updateSearchTerm] = useState('');
const [filteredResults, updateFilteredResults] = useState([]);
const [searchResults, updateSearchResults] = useState([]);
const [displayResults, updateDisplayResults] = useState(false);
const [focusIndex, updateFocusIndex] = useState(-1);
const linkRefs = [];
const keys = {
ENTER: 13,
UP: 38,
DOWN: 40
};
useEffect(() => {
const getSearchResult = () => {
// ⚠️ This is where you should pull data in from your server
const searchResultsResponse = getSearchResults();
updateSearchResults(searchResultsResponse);
};
getSearchResult();
}, []);
const updateSearch = e => {
var input = e.target?.value;
updateSearchTerm(e.target.value);
updateFilteredResults(searchResults.filter(result =>{
if(input?.length >= 3){
return result.title.match(new RegExp(e.target.value, 'gi'))
}
}))
};
const hideAutoSuggest = e => {
e.persist();
if (e.relatedTarget && e.relatedTarget.className === 'autosuggest-link') {
return false;
}
updateDisplayResults(true);
updateFocusIndex(-1);
};
const showAutoSuggest = () => {
updateDisplayResults(false);
};
const handleInput = (e) =>{
var input = e.target?.value;
$('.search-suggestions').css('display', 'block');
if(input?.length === 0){
$('.search-suggestions').css('display', 'none');
}
}
const handleNavigation = e => {
switch (e.keyCode) {
case keys.ENTER:
if (focusIndex !== -1) {
window.open(linkRefs[focusIndex].href);
}
hideAutoSuggest(e);
break;
case keys.UP:
if (focusIndex > -1) {
updateFocusIndex(focusIndex - 1);
}
break;
case keys.DOWN:
if (focusIndex < filteredResults.length - 1) {
updateFocusIndex(focusIndex + 1);
}
break;
}
};
const SearchResults = () => {
const Message = ({ text }) => (
<div className="search-results-message">
<h2>{text}</h2>
<hr />
</div>
);
if (!displayResults) {
return null;
}
if (!searchResults.length) {
return <Message text="Loading search results" />
}
if (!searchTerm) {
return <Message text="Try to search for something..." />
}
if (!filteredResults.length) {
return <Message text="We couldn't find anything for your search term." />
}
return (
<ul className="search-results">
{filteredResults.map((article, index) => (
<li key={index}>
{/* ⚠️ You may want to outsource this part to make the component less heavy */}
<Card model={article} />
</li>
))}
</ul>
);
}
return (
<section className="search">
<h5>Search {searchTerm.length ? `results for: ${searchTerm}` : null}</h5>
<input type="text"
placeholder="Search for tutorials..."
onKeyUp={updateSearch}
onKeyDown={handleNavigation}
onBlur={hideAutoSuggest}
onFocus={showAutoSuggest}
onChange={handleInput}
className='inputclear'/>
<ul className="search-suggestions">
{(!displayResults && searchTerm) && <li key="-1" className={focusIndex === -1 ? 'active' : null}>{`Search for ${searchTerm}`}</li>}
{!displayResults && filteredResults.map((article, index) => (
<li key={index} className={focusIndex === index ? 'active' : null}>
<div className="autosuggest-link"
ref={link => {linkRefs[index] = link}}>
<Highlight tags={searchTerm}>{article.title}</Highlight>
</div>
</li>
))}
</ul>
<SearchResults />
</section>
);
}
function Highlight({ children: text = "", tags }) {
if (!tags?.length) return text;
const matches = [...text.matchAll(new RegExp(tags, "ig"))];
const startText = text.slice(0, matches[0]?.index);
return (
<span>
{startText}
{matches.map((match, i) => {
const startIndex = match.index;
const currentText = match[0];
const endIndex = startIndex + currentText.length;
const nextIndex = matches[i + 1]?.index;
const untilNextText = text.slice(endIndex, nextIndex);
return (
<React.Fragment key={i}>
<p><span className='text-primary'>{currentText}</span>{untilNextText}</p>
</React.Fragment>
);
})}
</span>
);
}
export default Search;