1
0
mirror of https://github.com/uetchy/namae.git synced 2025-07-02 06:20:02 +09:00
namae/web/src/components/Form.tsx

164 lines
3.8 KiB
TypeScript
Raw Normal View History

2019-09-17 14:30:26 +09:00
import React, {useState, useRef, useEffect} from 'react';
import styled from 'styled-components';
import {useTranslation} from 'react-i18next';
2020-02-05 15:59:53 +09:00
import {Link, useHistory} from 'react-router-dom';
2020-02-05 17:28:22 +09:00
import {sanitize} from '../util/text';
2020-03-05 22:09:12 +09:00
import {sendQueryEvent} from '../util/analytics';
2020-03-06 00:05:54 +09:00
import {mobile, slideUp} from '../util/css';
2019-09-17 14:30:26 +09:00
import Suggestion from './Suggestion';
2020-03-06 00:05:54 +09:00
import {useDeferredState} from '../util/hooks';
2020-02-05 15:59:53 +09:00
const Form: React.FC<{
initialValue?: string;
}> = ({initialValue = ''}) => {
const history = useHistory();
const [inputValue, setInputValue] = useState(initialValue);
2020-03-06 00:05:54 +09:00
const [suggestionQuery, setSuggestionQuery] = useDeferredState(800, '');
const [isFocused, setFocus] = useState(false);
2019-09-17 14:30:26 +09:00
const [suggested, setSuggested] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const {t} = useTranslation();
2020-03-06 00:05:54 +09:00
function search(query: string) {
sendQueryEvent(sanitize(query));
history.push(`/s/${query}`);
}
// set input value
2019-12-24 01:57:07 +09:00
function onInputChange(e: React.FormEvent<HTMLInputElement>): void {
2020-03-06 00:05:54 +09:00
setInputValue(e.currentTarget.value);
}
// clear input form and focus on it
2019-12-24 01:57:07 +09:00
function onLogoClick(): void {
2019-09-17 14:30:26 +09:00
setInputValue('');
2020-02-05 15:59:53 +09:00
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
inputRef.current!.focus();
}
// invoke when user clicked one of the suggested items
2019-12-24 01:57:07 +09:00
function onSuggestionCompleted(name: string): void {
2019-09-17 14:30:26 +09:00
setInputValue(name);
2020-03-06 00:05:54 +09:00
search(name);
2019-09-17 14:30:26 +09:00
setSuggested(true);
}
2020-03-06 00:05:54 +09:00
function onSubmitQuery(e: React.FormEvent) {
e.preventDefault();
if (!inputValue || inputValue === '') {
return;
}
2020-03-06 00:05:54 +09:00
search(inputValue);
}
useEffect(() => {
2020-02-05 17:28:22 +09:00
const modifiedValue = sanitize(inputValue);
2020-03-06 00:05:54 +09:00
setSuggestionQuery(modifiedValue);
}, [inputValue, setSuggestionQuery]);
const queryGiven = suggestionQuery && suggestionQuery !== '';
return (
<InputContainer>
2020-02-06 17:21:22 +09:00
<Logo to="/" onClick={onLogoClick}>
<LogoImage src="/logo.svg" />
2020-02-05 15:59:53 +09:00
</Logo>
2020-03-06 00:05:54 +09:00
<form onSubmit={onSubmitQuery}>
<InputView
onChange={onInputChange}
onFocus={() => setFocus(true)}
onBlur={() => setFocus(false)}
value={inputValue}
ref={inputRef}
placeholder={t('placeholder')}
aria-label="search form"
/>
{isFocused && (
<Tips>
2020-03-06 23:46:19 +09:00
<span>{t('pressEnterToSearch')}</span>
2020-03-06 00:05:54 +09:00
</Tips>
)}
</form>
{queryGiven && !suggested ? (
2020-03-06 00:05:54 +09:00
<Suggestion onSubmit={onSuggestionCompleted} query={suggestionQuery} />
) : null}
</InputContainer>
2019-09-17 14:30:26 +09:00
);
};
2019-09-17 14:30:26 +09:00
export default Form;
const InputContainer = styled.div`
2020-02-06 17:21:22 +09:00
display: flex;
flex-direction: column;
align-items: center;
2020-02-06 13:16:30 +09:00
padding: 30px;
transform: translateY(40px);
2020-02-06 13:16:30 +09:00
border-radius: 50px;
box-shadow: 0 10px 50px 0 #858efb;
background: #ffffff;
${mobile} {
2020-02-06 13:16:30 +09:00
padding: 20px;
transform: translateY(20px);
2020-02-06 13:16:30 +09:00
border-radius: 30px;
}
2019-09-17 14:30:26 +09:00
`;
2020-02-06 17:21:22 +09:00
const Logo = styled(Link)`
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 12px;
margin-top: 5px;
cursor: pointer;
${mobile} {
font-size: 15px;
}
2020-02-06 17:21:22 +09:00
`;
2020-02-05 15:59:53 +09:00
2020-02-06 17:21:22 +09:00
const LogoImage = styled.img`
width: 140px;
2020-02-06 03:39:06 +09:00
2020-02-06 17:21:22 +09:00
${mobile} {
width: 90px;
2020-02-06 03:39:06 +09:00
}
2019-09-17 14:30:26 +09:00
`;
const InputView = styled.input.attrs({
2020-03-06 00:05:54 +09:00
type: 'input',
name: 'search',
autocomplete: 'off',
autocorrect: 'off',
autocapitalize: 'off',
spellcheck: 'false',
})`
width: 100%;
border: none;
outline: none;
text-align: center;
2020-02-06 03:39:06 +09:00
font-family: 'Montserrat', monospace;
font-weight: 600;
2020-02-06 13:16:30 +09:00
font-size: 6rem;
${mobile} {
font-size: 2rem;
}
2020-02-06 17:21:22 +09:00
::placeholder {
color: #c8cdda;
}
2019-09-17 14:30:26 +09:00
`;
2020-03-06 00:05:54 +09:00
const Tips = styled.div`
color: #ababab;
text-align: center;
overflow: hidden;
span {
display: block;
animation: ${slideUp} 0.6s cubic-bezier(0.19, 1, 0.22, 1);
animation-fill-mode: both;
}
`;