diff --git a/src/components/search.jsx b/src/components/search.jsx new file mode 100644 index 00000000..a53ab59d --- /dev/null +++ b/src/components/search.jsx @@ -0,0 +1,93 @@ +import { useTranslation } from "react-i18next"; +import { useEffect, useState, useRef } from "react"; +import classNames from "classnames"; + +export default function Search({bookmarks, services, searchString, setSearchString, isOpen, close}) { + const { t, i18n } = useTranslation(); + const all = [...bookmarks.map(bg => bg.bookmarks).flat(), ...services.map(sg => sg.services).flat()]; + + const searchField = useRef(); + + const [results, setResults] = useState([]); + const [currentItemIndex, setCurrentItemIndex] = useState(null); + + function handleSearchChange(event) { + setSearchString(event.target.value.toLowerCase()) + } + + function handleSearchKeyDown(event) { + if (event.key === "Escape") { + setSearchString(""); + close(false); + } else if (event.key === "Enter" && results.length) { + setSearchString(""); + close(false); + const result = results[currentItemIndex]; + console.log("go to", result); + window.open(result.href, '_blank'); + } else if (event.key == "ArrowDown" && results[currentItemIndex + 1]) { + setCurrentItemIndex(currentItemIndex + 1); + event.preventDefault(); + } else if (event.key == "ArrowUp" && currentItemIndex > 0) { + setCurrentItemIndex(currentItemIndex - 1); + event.preventDefault(); + } + } + + useEffect(() => { + if (searchString.length === 0) setResults([]); + else { + const newResults = all.filter(r => r.name.toLowerCase().includes(searchString)); + setResults(newResults); + if (newResults.length) { + setCurrentItemIndex(0); + } + } + }, [searchString]) + + + const [hidden, setHidden] = useState(true); + useEffect(() => { + if (isOpen) { + searchField.current.focus(); + setHidden(false); + } else { + setTimeout(() => { + setHidden(true); + }, 300); // disable on close + } + }, [isOpen]) + + return ( +