diff --git a/package.json b/package.json index d9e333f..798de58 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "react-dom": "^17.0.1", "react-dropdown-select": "^4.4.2", "react-ga": "^3.1.2", + "react-hotkeys-hook": "^3.2.1", "react-scripts": "3.4.1", "react-use": "^15.3.2", "recoil": "^0.1.2", diff --git a/src/components/SearchInput/SearchInput.css b/src/components/SearchInput/SearchInput.css index e2d9280..5cd8189 100644 --- a/src/components/SearchInput/SearchInput.css +++ b/src/components/SearchInput/SearchInput.css @@ -15,11 +15,15 @@ background-color: white !important; } +.search-bar:focus-within .keys { + display: none; +} + .search-bar input { height: 100%; flex: 1; border: none; - margin-left: 12px; + margin: 0 12px; padding: 0; font-size: 16px; color: white; @@ -48,6 +52,13 @@ cursor: wait; } +.keys { + display: inline-flex; + align-items: center; + min-width: 42px; + font-size: 0.8em; +} + @media screen and (max-width: 558px) { #search-icon { display: none; @@ -58,3 +69,9 @@ width: 60%; } } + +@media screen and (min-width: 858px) and (max-width: 1100px) { + .search-bar { + flex-basis: 320px; + } +} diff --git a/src/components/SearchInput/SearchInput.tsx b/src/components/SearchInput/SearchInput.tsx index f407d25..f35f2a6 100644 --- a/src/components/SearchInput/SearchInput.tsx +++ b/src/components/SearchInput/SearchInput.tsx @@ -1,7 +1,8 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useRef, MutableRefObject } from "react"; import { useRecoilState } from "recoil"; import { useDebounce } from "react-use"; -import { MagnifyingGlass, X, HourglassHigh } from "phosphor-react"; +import { useHotkeys } from "react-hotkeys-hook"; +import { Command, MagnifyingGlass, X, HourglassHigh } from "phosphor-react"; import ReactGA from "react-ga"; import { searchQueryAtom } from "../../state/atoms"; @@ -12,6 +13,24 @@ type SearchInputProps = {}; const SearchInput: React.FC = () => { const [value, setValue] = useState(""); const [query, setQuery] = useRecoilState(searchQueryAtom); + const inputRef = useRef() as MutableRefObject; + const isMobile = useRef(false) as MutableRefObject; + const isApple = useRef(false) as MutableRefObject; + + useEffect(() => { + const apple = /iPhone|iPod|iPad|Macintosh|MacIntel|MacPPC/i; + const mobile = /Android|iPhone|iPod|iPad|Opera Mini|IEMobile/i; + isApple.current = apple.test(window.navigator.platform); + isMobile.current = mobile.test(window.navigator.userAgent); + }, [isApple, isMobile]); + + useHotkeys("ctrl+k,cmd+k", (e) => { + e.preventDefault(); + if (!e.repeat) { + inputRef.current?.focus(); + inputRef.current.select(); + } + }); /* eslint-disable react-hooks/exhaustive-deps */ useEffect(() => { @@ -28,11 +47,13 @@ const SearchInput: React.FC = () => { () => { if (value !== query) { setQuery(value); - !!value && ReactGA.event({ category: "Search", action: "Query", label: value }); + !!value && + ReactGA.event({ category: "Search", action: "Query", label: value }); } - !!value && void document - .getElementById("beacon") - ?.scrollIntoView({ block: "start", behavior: "smooth" }); + !!value && + void document + .getElementById("beacon") + ?.scrollIntoView({ block: "start", behavior: "smooth" }); }, 250, [value] @@ -49,6 +70,7 @@ const SearchInput: React.FC = () => {
= () => { key === "Enter" && currentTarget.blur() } /> + {!value && !isMobile.current && ( + {isApple.current ? : "Ctrl"} + K + )} {value ? ( isReady() ? ( @@ -73,4 +98,8 @@ const SearchInput: React.FC = () => { ); }; +const Keys: React.FC<{}> = ({ children }) => ( +
{children}
+); + export default SearchInput;