SearchInput: Add keyboard shortcuts with platform detection
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user