fix: CMD+F not working on screens with keyboard shortcut guide (#2066)

This commit is contained in:
Tom Moor 2021-04-23 12:10:02 -07:00 committed by GitHub
parent a818c7a924
commit eb6acdae20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 143 additions and 117 deletions

View File

@ -106,8 +106,9 @@ export type Props = {|
onChange?: (
ev: SyntheticInputEvent<HTMLInputElement | HTMLTextAreaElement>
) => mixed,
onFocus?: (ev: SyntheticEvent<>) => void,
onBlur?: (ev: SyntheticEvent<>) => void,
onKeyDown?: (ev: SyntheticKeyboardEvent<HTMLInputElement>) => mixed,
onFocus?: (ev: SyntheticEvent<>) => mixed,
onBlur?: (ev: SyntheticEvent<>) => mixed,
|};
@observer

View File

@ -1,98 +1,47 @@
// @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 { useTheme } from "styled-components";
import Input from "./Input";
import { type Theme } from "types";
import { meta } from "utils/keyboard";
import { searchUrl } from "utils/routeHelpers";
type Props = {
history: RouterHistory,
theme: Theme,
source: string,
type Props = {|
placeholder?: string,
label?: string,
labelHidden?: boolean,
collectionId?: string,
redirectDisabled?: boolean,
maxWidth?: string,
value: string,
value?: string,
onChange: (event: SyntheticInputEvent<>) => mixed,
onKeyDown: (event: SyntheticKeyboardEvent<>) => mixed,
t: TFunction,
};
onKeyDown: (event: SyntheticKeyboardEvent<HTMLInputElement>) => mixed,
|};
@observer
class InputSearch extends React.Component<Props> {
input: ?Input;
@observable focused: boolean = false;
export default function InputSearch(props: Props) {
const { t } = useTranslation();
const theme = useTheme();
const [isFocused, setIsFocused] = React.useState(false);
@keydown(`${meta}+f`)
focus(ev: SyntheticEvent<>) {
ev.preventDefault();
const handleFocus = React.useCallback(() => {
setIsFocused(true);
}, []);
if (this.input) {
this.input.focus();
}
}
const handleBlur = React.useCallback(() => {
setIsFocused(false);
}, []);
handleSearchInput = (ev: SyntheticInputEvent<>) => {
ev.preventDefault();
this.props.history.push(
searchUrl(ev.target.value, {
collectionId: this.props.collectionId,
ref: this.props.source,
})
);
};
const { placeholder = `${t("Search")}`, onKeyDown, ...rest } = props;
handleFocus = () => {
this.focused = true;
};
handleBlur = () => {
this.focused = false;
};
render() {
const { t, redirectDisabled, value, onChange, onKeyDown } = this.props;
const { theme, placeholder = `${t("Search")}` } = this.props;
return (
<InputMaxWidth
ref={(ref) => (this.input = ref)}
type="search"
placeholder={placeholder}
onInput={redirectDisabled ? undefined : this.handleSearchInput}
value={value}
onChange={onChange}
onKeyDown={onKeyDown}
icon={
<SearchIcon
color={this.focused ? theme.inputBorderFocused : theme.inputBorder}
/>
}
label={this.props.label}
labelHidden={this.props.labelHidden}
maxWidth={this.props.maxWidth}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
margin={0}
/>
);
}
return (
<Input
type="search"
placeholder={placeholder}
icon={
<SearchIcon
color={isFocused ? theme.inputBorderFocused : theme.inputBorder}
/>
}
onKeyDown={onKeyDown}
onFocus={handleFocus}
onBlur={handleBlur}
margin={0}
labelHidden
{...rest}
/>
);
}
const InputMaxWidth = styled(Input)`
max-width: ${(props) => props.maxWidth};
`;
export default withTranslation()<InputSearch>(
withTheme(withRouter(InputSearch))
);

View File

@ -0,0 +1,95 @@
// @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 Input from "./Input";
import { type Theme } from "types";
import { meta } from "utils/keyboard";
import { searchUrl } from "utils/routeHelpers";
type Props = {
history: RouterHistory,
theme: Theme,
source: string,
placeholder?: string,
label?: string,
labelHidden?: boolean,
collectionId?: string,
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;
@keydown(`${meta}+f`)
focus(ev: SyntheticEvent<>) {
ev.preventDefault();
if (this.input) {
this.input.focus();
}
}
handleSearchInput = (ev: SyntheticInputEvent<>) => {
ev.preventDefault();
this.props.history.push(
searchUrl(ev.target.value, {
collectionId: this.props.collectionId,
ref: this.props.source,
})
);
};
handleFocus = () => {
this.focused = true;
};
handleBlur = () => {
this.focused = false;
};
render() {
const { t, value, onChange, onKeyDown } = this.props;
const { theme, placeholder = `${t("Search")}` } = this.props;
return (
<InputMaxWidth
ref={(ref) => (this.input = ref)}
type="search"
placeholder={placeholder}
onInput={this.handleSearchInput}
value={value}
onChange={onChange}
onKeyDown={onKeyDown}
icon={
<SearchIcon
color={this.focused ? theme.inputBorderFocused : theme.inputBorder}
/>
}
label={this.props.label}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
margin={0}
labelHidden
/>
);
}
}
const InputMaxWidth = styled(Input)`
max-width: 30vw;
`;
export default withTranslation()<InputSearchPage>(
withTheme(withRouter(InputSearchPage))
);

View File

@ -18,7 +18,7 @@ import DocumentList from "components/DocumentList";
import Flex from "components/Flex";
import Heading from "components/Heading";
import HelpText from "components/HelpText";
import InputSearch from "components/InputSearch";
import InputSearchPage from "components/InputSearchPage";
import LoadingIndicator from "components/LoadingIndicator";
import { ListPlaceholder } from "components/LoadingPlaceholder";
import Mask from "components/Mask";
@ -120,13 +120,11 @@ function CollectionScene() {
actions={
<>
<Action>
<InputSearch
<InputSearchPage
source="collection"
placeholder={`${t("Search in collection")}`}
label={`${t("Search in collection")}`}
labelHidden
collectionId={collectionId}
maxWidth="30vw"
/>
</Action>
{can.update && (

View File

@ -14,7 +14,7 @@ import { Action } from "components/Actions";
import Empty from "components/Empty";
import Flex from "components/Flex";
import Heading from "components/Heading";
import InputSearch from "components/InputSearch";
import InputSearchPage from "components/InputSearchPage";
import PaginatedDocumentList from "components/PaginatedDocumentList";
import Scene from "components/Scene";
import Subheading from "components/Subheading";
@ -83,12 +83,7 @@ class Drafts extends React.Component<Props> {
actions={
<>
<Action>
<InputSearch
source="drafts"
label={t("Search documents")}
maxWidth="30vw"
labelHidden
/>
<InputSearchPage source="drafts" label={t("Search documents")} />
</Action>
<Action>
<NewDocumentMenu />

View File

@ -6,7 +6,7 @@ import { useTranslation } from "react-i18next";
import { Switch, Route } from "react-router-dom";
import { Action } from "components/Actions";
import Heading from "components/Heading";
import InputSearch from "components/InputSearch";
import InputSearchPage from "components/InputSearchPage";
import LanguagePrompt from "components/LanguagePrompt";
import Scene from "components/Scene";
import Tab from "components/Tab";
@ -29,12 +29,7 @@ function Home() {
actions={
<>
<Action>
<InputSearch
source="dashboard"
label={t("Search documents")}
maxWidth="30vw"
labelHidden
/>
<InputSearchPage source="dashboard" label={t("Search documents")} />
</Action>
<Action>
<NewDocumentMenu />

View File

@ -3,7 +3,7 @@ import * as React from "react";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import Flex from "components/Flex";
import Input from "components/InputSearch";
import InputSearch from "components/InputSearch";
import Key from "components/Key";
import { metaDisplay } from "utils/keyboard";
@ -351,7 +351,7 @@ function KeyboardShortcuts() {
}, []);
const handleKeyDown = React.useCallback((event) => {
if (event.target.value && event.key === "Escape") {
if (event.currentTarget.value && event.key === "Escape") {
event.preventDefault();
event.stopPropagation();
setSearchTerm("");
@ -360,12 +360,10 @@ function KeyboardShortcuts() {
return (
<Flex column>
<Input
type="search"
<InputSearch
onChange={handleChange}
onKeyDown={handleKeyDown}
value={searchTerm}
redirectDisabled
/>
{categories.map((category, x) => {
const filtered = searchTerm

View File

@ -7,7 +7,7 @@ import { type Match } from "react-router-dom";
import { Action } from "components/Actions";
import Empty from "components/Empty";
import Heading from "components/Heading";
import InputSearch from "components/InputSearch";
import InputSearchPage from "components/InputSearchPage";
import PaginatedDocumentList from "components/PaginatedDocumentList";
import Scene from "components/Scene";
import Tab from "components/Tab";
@ -32,12 +32,7 @@ function Starred(props: Props) {
actions={
<>
<Action>
<InputSearch
source="starred"
label={t("Search documents")}
maxWidth="30vw"
labelHidden
/>
<InputSearchPage source="starred" label={t("Search documents")} />
</Action>
<Action>
<NewDocumentMenu />