SearchInput: Add keyboard shortcuts with platform detection

This commit is contained in:
rektdeckard
2021-03-06 22:41:52 -05:00
parent 78ff8e4500
commit f598e3ab50
3 changed files with 54 additions and 7 deletions

View File

@@ -29,6 +29,7 @@
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-dropdown-select": "^4.4.2", "react-dropdown-select": "^4.4.2",
"react-ga": "^3.1.2", "react-ga": "^3.1.2",
"react-hotkeys-hook": "^3.2.1",
"react-scripts": "3.4.1", "react-scripts": "3.4.1",
"react-use": "^15.3.2", "react-use": "^15.3.2",
"recoil": "^0.1.2", "recoil": "^0.1.2",

View File

@@ -15,11 +15,15 @@
background-color: white !important; background-color: white !important;
} }
.search-bar:focus-within .keys {
display: none;
}
.search-bar input { .search-bar input {
height: 100%; height: 100%;
flex: 1; flex: 1;
border: none; border: none;
margin-left: 12px; margin: 0 12px;
padding: 0; padding: 0;
font-size: 16px; font-size: 16px;
color: white; color: white;
@@ -48,6 +52,13 @@
cursor: wait; cursor: wait;
} }
.keys {
display: inline-flex;
align-items: center;
min-width: 42px;
font-size: 0.8em;
}
@media screen and (max-width: 558px) { @media screen and (max-width: 558px) {
#search-icon { #search-icon {
display: none; display: none;
@@ -58,3 +69,9 @@
width: 60%; width: 60%;
} }
} }
@media screen and (min-width: 858px) and (max-width: 1100px) {
.search-bar {
flex-basis: 320px;
}
}

View File

@@ -1,7 +1,8 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useRef, MutableRefObject } from "react";
import { useRecoilState } from "recoil"; import { useRecoilState } from "recoil";
import { useDebounce } from "react-use"; 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 ReactGA from "react-ga";
import { searchQueryAtom } from "../../state/atoms"; import { searchQueryAtom } from "../../state/atoms";
@@ -12,6 +13,24 @@ type SearchInputProps = {};
const SearchInput: React.FC<SearchInputProps> = () => { const SearchInput: React.FC<SearchInputProps> = () => {
const [value, setValue] = useState<string>(""); const [value, setValue] = useState<string>("");
const [query, setQuery] = useRecoilState(searchQueryAtom); const [query, setQuery] = useRecoilState(searchQueryAtom);
const inputRef = useRef<HTMLInputElement>() as MutableRefObject<HTMLInputElement>;
const isMobile = useRef<boolean>(false) as MutableRefObject<boolean>;
const isApple = useRef<boolean>(false) as MutableRefObject<boolean>;
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 */ /* eslint-disable react-hooks/exhaustive-deps */
useEffect(() => { useEffect(() => {
@@ -28,11 +47,13 @@ const SearchInput: React.FC<SearchInputProps> = () => {
() => { () => {
if (value !== query) { if (value !== query) {
setQuery(value); setQuery(value);
!!value && ReactGA.event({ category: "Search", action: "Query", label: value }); !!value &&
ReactGA.event({ category: "Search", action: "Query", label: value });
} }
!!value && void document !!value &&
.getElementById("beacon") void document
?.scrollIntoView({ block: "start", behavior: "smooth" }); .getElementById("beacon")
?.scrollIntoView({ block: "start", behavior: "smooth" });
}, },
250, 250,
[value] [value]
@@ -49,6 +70,7 @@ const SearchInput: React.FC<SearchInputProps> = () => {
<div className="search-bar"> <div className="search-bar">
<MagnifyingGlass id="search-icon" size={24} /> <MagnifyingGlass id="search-icon" size={24} />
<input <input
ref={inputRef}
id="search-input" id="search-input"
title="Search for icon names, categories, or keywords" title="Search for icon names, categories, or keywords"
aria-label="Search for an icon" aria-label="Search for an icon"
@@ -62,6 +84,9 @@ const SearchInput: React.FC<SearchInputProps> = () => {
key === "Enter" && currentTarget.blur() key === "Enter" && currentTarget.blur()
} }
/> />
{!value && !isMobile.current && (
<Keys>{isApple.current ? <Command /> : "Ctrl"} + K</Keys>
)}
{value ? ( {value ? (
isReady() ? ( isReady() ? (
<X className="clear-icon" size={18} onClick={handleCancelSearch} /> <X className="clear-icon" size={18} onClick={handleCancelSearch} />
@@ -73,4 +98,8 @@ const SearchInput: React.FC<SearchInputProps> = () => {
); );
}; };
const Keys: React.FC<{}> = ({ children }) => (
<div className="keys">{children}</div>
);
export default SearchInput; export default SearchInput;