chore: Remove react-keydown (#2713)

* First steps of remove react-keydown, replace with hook

* RegisterKeyDown component to aid transition away from react-keydown
This commit is contained in:
Tom Moor
2021-11-01 19:52:04 -07:00
committed by GitHub
parent 5f00e1394d
commit 57fa1305a6
11 changed files with 220 additions and 565 deletions

View File

@@ -1,20 +1,17 @@
// @flow
import { observable } from "mobx";
import { observer } from "mobx-react";
import { SearchIcon } from "outline-icons";
import * as React from "react";
import { withTranslation, type TFunction } from "react-i18next";
import keydown from "react-keydown";
import { withRouter, type RouterHistory } from "react-router-dom";
import styled, { withTheme } from "styled-components";
import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
import styled, { useTheme } from "styled-components";
import Input from "./Input";
import { type Theme } from "types";
import { meta } from "utils/keyboard";
import useBoolean from "hooks/useBoolean";
import useKeyDown from "hooks/useKeyDown";
import { isModKey } from "utils/keyboard";
import { searchUrl } from "utils/routeHelpers";
type Props = {
history: RouterHistory,
theme: Theme,
type Props = {|
source: string,
placeholder?: string,
label?: string,
@@ -23,78 +20,77 @@ type Props = {
value: string,
onChange: (event: SyntheticInputEvent<>) => mixed,
onKeyDown: (event: SyntheticKeyboardEvent<HTMLInputElement>) => mixed,
t: TFunction,
};
|};
@observer
class InputSearchPage extends React.Component<Props> {
input: ?Input;
@observable focused: boolean = false;
function InputSearchPage({
onKeyDown,
value,
onChange,
placeholder,
label,
collectionId,
source,
}: Props) {
const inputRef = React.useRef();
const theme = useTheme();
const history = useHistory();
const { t } = useTranslation();
const [isFocused, setFocused, setUnfocused] = useBoolean(false);
@keydown(`${meta}+f`)
focus(ev: SyntheticEvent<>) {
ev.preventDefault();
const focus = React.useCallback(() => {
inputRef.current?.focus();
}, []);
if (this.input) {
this.input.focus();
}
}
handleKeyDown = (ev: SyntheticKeyboardEvent<HTMLInputElement>) => {
if (ev.key === "Enter") {
useKeyDown("f", (ev: KeyboardEvent) => {
if (isModKey(ev)) {
ev.preventDefault();
this.props.history.push(
searchUrl(ev.currentTarget.value, {
collectionId: this.props.collectionId,
ref: this.props.source,
})
);
focus();
}
});
if (this.props.onKeyDown) {
this.props.onKeyDown(ev);
}
};
const handleKeyDown = React.useCallback(
(ev: SyntheticKeyboardEvent<HTMLInputElement>) => {
if (ev.key === "Enter") {
ev.preventDefault();
history.push(
searchUrl(ev.currentTarget.value, {
collectionId,
ref: source,
})
);
}
handleFocus = () => {
this.focused = true;
};
if (onKeyDown) {
onKeyDown(ev);
}
},
[history, collectionId, source, onKeyDown]
);
handleBlur = () => {
this.focused = false;
};
render() {
const { t, value, onChange } = this.props;
const { theme, placeholder = `${t("Search")}` } = this.props;
return (
<InputMaxWidth
ref={(ref) => (this.input = ref)}
type="search"
placeholder={placeholder}
value={value}
onChange={onChange}
onKeyDown={this.handleKeyDown}
icon={
<SearchIcon
color={this.focused ? theme.inputBorderFocused : theme.inputBorder}
/>
}
label={this.props.label}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
margin={0}
labelHidden
/>
);
}
return (
<InputMaxWidth
ref={inputRef}
type="search"
placeholder={placeholder || `${t("Search")}`}
value={value}
onChange={onChange}
onKeyDown={handleKeyDown}
icon={
<SearchIcon
color={isFocused ? theme.inputBorderFocused : theme.inputBorder}
/>
}
label={label}
onFocus={setFocused}
onBlur={setUnfocused}
margin={0}
labelHidden
/>
);
}
const InputMaxWidth = styled(Input)`
max-width: 30vw;
`;
export default withTranslation()<InputSearchPage>(
withTheme(withRouter(InputSearchPage))
);
export default observer(InputSearchPage);

View File

@@ -5,7 +5,6 @@ import { MenuIcon } from "outline-icons";
import * as React from "react";
import { Helmet } from "react-helmet";
import { withTranslation } from "react-i18next";
import keydown from "react-keydown";
import {
Switch,
Route,
@@ -22,11 +21,12 @@ import ErrorSuspended from "scenes/ErrorSuspended";
import Button from "components/Button";
import Flex from "components/Flex";
import { LoadingIndicatorBar } from "components/LoadingIndicator";
import RegisterKeyDown from "components/RegisterKeyDown";
import Sidebar from "components/Sidebar";
import SettingsSidebar from "components/Sidebar/Settings";
import SkipNavContent from "components/SkipNavContent";
import SkipNavLink from "components/SkipNavLink";
import { meta } from "utils/keyboard";
import { isModKey } from "utils/keyboard";
import {
searchUrl,
matchDocumentSlug as slug,
@@ -64,20 +64,13 @@ class Layout extends React.Component<Props> {
scrollable: ?HTMLDivElement;
@observable keyboardShortcutsOpen: boolean = false;
@keydown(`${meta}+.`)
handleToggleSidebar() {
this.props.ui.toggleCollapsedSidebar();
}
@keydown(["t", "/"])
goToSearch(ev: SyntheticEvent<>) {
goToSearch = (ev: KeyboardEvent) => {
ev.preventDefault();
ev.stopPropagation();
this.props.history.push(searchUrl());
}
};
@keydown("n")
goToNewDocument() {
goToNewDocument = () => {
const { activeCollectionId } = this.props.ui;
if (!activeCollectionId) return;
@@ -85,7 +78,7 @@ class Layout extends React.Component<Props> {
if (!can.update) return;
this.props.history.push(newDocumentPath(activeCollectionId));
}
};
render() {
const { auth, ui } = this.props;
@@ -97,6 +90,17 @@ class Layout extends React.Component<Props> {
return (
<Container column auto>
<RegisterKeyDown trigger="n" handler={this.goToNewDocument} />
<RegisterKeyDown trigger="t" handler={this.goToSearch} />
<RegisterKeyDown trigger="/" handler={this.goToSearch} />
<RegisterKeyDown
trigger="."
handler={(event) => {
if (isModKey(event)) {
ui.toggleCollapsedSidebar();
}
}}
/>
<Helmet>
<title>{team && team.name ? team.name : "Outline"}</title>
<meta

View File

@@ -0,0 +1,17 @@
// @flow
import useKeyDown, { type KeyFilter } from "hooks/useKeyDown";
type Props = {
trigger: KeyFilter,
handler: (event: KeyboardEvent) => void,
};
/**
* This method is a wrapper around the useKeyDown hook to allow easier use in
* class components that have not yet been converted to functions. Do not use
* this method in functional components.
*/
export default function RegisterKeyDown({ trigger, handler }: Props) {
useKeyDown(trigger, handler);
return null;
}