mirror of
https://github.com/uetchy/namae.git
synced 2025-08-20 09:58:13 +09:00
dev: initial attempt
This commit is contained in:
150
components/Form.tsx
Normal file
150
components/Form.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from '@emotion/styled';
|
||||
import { useStoreActions } from '../store';
|
||||
import { sendQueryEvent } from '../src/util/analytics';
|
||||
import { mobile } from '../src/util/css';
|
||||
import { useDeferredState } from '../src/util/hooks';
|
||||
import { sanitize } from '../src/util/text';
|
||||
import Suggestion from './Suggestion';
|
||||
|
||||
const Form: React.FC<{
|
||||
initialValue?: string;
|
||||
useSuggestion?: boolean;
|
||||
}> = ({ initialValue = '', useSuggestion = true }) => {
|
||||
const router = useRouter();
|
||||
const reset = useStoreActions((actions) => actions.stats.reset);
|
||||
const [inputValue, setInputValue] = useState(initialValue);
|
||||
const [suggestionQuery, setSuggestionQuery] = useDeferredState(800, '');
|
||||
const [suggested, setSuggested] = useState(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
function search(query: string) {
|
||||
reset();
|
||||
sendQueryEvent(sanitize(query));
|
||||
router.push(`/s/${query}`);
|
||||
}
|
||||
|
||||
// set input value
|
||||
function onInputChange(e: React.FormEvent<HTMLInputElement>): void {
|
||||
setInputValue(e.currentTarget.value);
|
||||
}
|
||||
|
||||
// invoke when user clicked one of the suggested items
|
||||
function onSuggestionCompleted(name: string): void {
|
||||
setInputValue(name);
|
||||
search(name);
|
||||
setSuggested(true);
|
||||
}
|
||||
|
||||
function onSubmitQuery(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
inputRef.current!.blur();
|
||||
if (!inputValue || inputValue === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
search(inputValue);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const modifiedValue = sanitize(inputValue);
|
||||
setSuggestionQuery(modifiedValue);
|
||||
}, [inputValue, setSuggestionQuery]);
|
||||
|
||||
return (
|
||||
<InputContainer>
|
||||
<InputHeader>
|
||||
<Logo href="/">
|
||||
<LogoImage src="/logo.svg" />
|
||||
</Logo>
|
||||
</InputHeader>
|
||||
<form onSubmit={onSubmitQuery} action="/s" role="search">
|
||||
<InputView
|
||||
onChange={onInputChange}
|
||||
value={inputValue}
|
||||
ref={inputRef}
|
||||
placeholder={t('placeholder')}
|
||||
aria-label="Search"
|
||||
/>
|
||||
</form>
|
||||
{useSuggestion && !suggested ? (
|
||||
<Suggestion onSubmit={onSuggestionCompleted} query={suggestionQuery} />
|
||||
) : null}
|
||||
</InputContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Form;
|
||||
|
||||
const InputContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 30px;
|
||||
transform: translateY(40px);
|
||||
border-radius: 50px;
|
||||
box-shadow: 0 10px 50px 0 #858efb;
|
||||
background: #ffffff;
|
||||
|
||||
${mobile} {
|
||||
padding: 20px;
|
||||
transform: translateY(20px);
|
||||
border-radius: 30px;
|
||||
}
|
||||
`;
|
||||
|
||||
const InputHeader = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 12px 0 5px 0;
|
||||
`;
|
||||
|
||||
const Logo = styled(Link)`
|
||||
cursor: pointer;
|
||||
|
||||
${mobile} {
|
||||
font-size: 15px;
|
||||
}
|
||||
`;
|
||||
|
||||
const LogoImage = styled.img`
|
||||
width: 140px;
|
||||
|
||||
${mobile} {
|
||||
width: 90px;
|
||||
}
|
||||
`;
|
||||
|
||||
const InputView = styled.input`
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
text-align: center;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 6rem;
|
||||
appearance: none;
|
||||
|
||||
${mobile} {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #c8cdda;
|
||||
}
|
||||
`;
|
||||
InputView.defaultProps = {
|
||||
type: 'search',
|
||||
enterKeyHint: 'search',
|
||||
autoComplete: 'off',
|
||||
autoCorrect: 'off',
|
||||
autoCapitalize: 'off',
|
||||
spellCheck: 'false',
|
||||
autoFocus: true,
|
||||
};
|
Reference in New Issue
Block a user