From 6f87b7fc5b7dd4259f21067e913efbe69637871e Mon Sep 17 00:00:00 2001 From: Yasuaki Uechi Date: Fri, 6 Mar 2020 00:05:54 +0900 Subject: [PATCH] chore: add animation --- web/src/components/Form.tsx | 87 +++++++++++++++++++------------ web/src/components/Suggestion.tsx | 19 +++++-- web/src/util/css.ts | 11 ++++ 3 files changed, 80 insertions(+), 37 deletions(-) diff --git a/web/src/components/Form.tsx b/web/src/components/Form.tsx index 07df023..52677cb 100644 --- a/web/src/components/Form.tsx +++ b/web/src/components/Form.tsx @@ -4,24 +4,29 @@ import {useTranslation} from 'react-i18next'; import {Link, useHistory} from 'react-router-dom'; import {sanitize} from '../util/text'; import {sendQueryEvent} from '../util/analytics'; -import {useDeferredState} from '../util/hooks'; -import {mobile} from '../util/css'; +import {mobile, slideUp} from '../util/css'; import Suggestion from './Suggestion'; +import {useDeferredState} from '../util/hooks'; const Form: React.FC<{ initialValue?: string; }> = ({initialValue = ''}) => { const history = useHistory(); - const [query, setQuery] = useDeferredState(800, ''); const [inputValue, setInputValue] = useState(initialValue); + const [suggestionQuery, setSuggestionQuery] = useDeferredState(800, ''); + const [isFocused, setFocus] = useState(false); const [suggested, setSuggested] = useState(false); const inputRef = useRef(null); const {t} = useTranslation(); + function search(query: string) { + sendQueryEvent(sanitize(query)); + history.push(`/s/${query}`); + } + // set input value function onInputChange(e: React.FormEvent): void { - const value = e.currentTarget.value; - setInputValue(value); + setInputValue(e.currentTarget.value); } // clear input form and focus on it @@ -34,53 +39,56 @@ const Form: React.FC<{ // invoke when user clicked one of the suggested items function onSuggestionCompleted(name: string): void { setInputValue(name); + search(name); setSuggested(true); } - const queryGiven = query && query.length > 0; - - useEffect(() => { - function onQuery(query: string) { - if (!query || query === '') { - return; - } - sendQueryEvent(query); - history.push(`/s/${query}`); + function onSubmitQuery(e: React.FormEvent) { + e.preventDefault(); + if (!inputValue || inputValue === '') { + return; } - - if (query.length === 0) { - setSuggested(false); - } else { - onQuery(query); - } - }, [query, history]); + search(inputValue); + } useEffect(() => { const modifiedValue = sanitize(inputValue); - setQuery(modifiedValue); - }, [inputValue, setQuery]); + setSuggestionQuery(modifiedValue); + }, [inputValue, setSuggestionQuery]); + + const queryGiven = suggestionQuery && suggestionQuery !== ''; return ( - +
+ setFocus(true)} + onBlur={() => setFocus(false)} + value={inputValue} + ref={inputRef} + placeholder={t('placeholder')} + aria-label="search form" + /> + {isFocused && ( + + + Press Enter to search + + + )} + {queryGiven && !suggested ? ( - + ) : null}
); }; export default Form; - const InputContainer = styled.div` display: flex; flex-direction: column; @@ -120,7 +128,8 @@ const LogoImage = styled.img` `; const InputView = styled.input.attrs({ - type: 'text', + type: 'input', + name: 'search', autocomplete: 'off', autocorrect: 'off', autocapitalize: 'off', @@ -142,3 +151,15 @@ const InputView = styled.input.attrs({ color: #c8cdda; } `; + +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; + } +`; diff --git a/web/src/components/Suggestion.tsx b/web/src/components/Suggestion.tsx index 2676629..ece1145 100644 --- a/web/src/components/Suggestion.tsx +++ b/web/src/components/Suggestion.tsx @@ -7,7 +7,7 @@ import {motion} from 'framer-motion'; import {capitalize, stem, germanify, njoin, lower, upper} from '../util/text'; import {sampleFromArray, fillArray} from '../util/array'; -import {mobile} from '../util/css'; +import {mobile, slideUp} from '../util/css'; import {sanitize} from '../util/text'; import { sendShuffleSuggestionEvent, @@ -225,8 +225,11 @@ const Suggestion: React.FC<{ {bestWords && bestWords.map((name, i) => ( - applyQuery(name)}> - {name} + applyQuery(name)} + delay={i + 1}> + {name} ))} @@ -276,7 +279,7 @@ const Items = styled.div` } `; -const Item = styled.div` +const Item = styled.div<{delay: number}>` margin: 10px 10px 0; cursor: pointer; font-weight: bold; @@ -285,6 +288,14 @@ const Item = styled.div` line-height: 1em; border-bottom: 1px dashed black; color: black; + overflow: hidden; + + span { + display: block; + animation: ${slideUp} 0.6s ${(props) => `${props.delay * 0.1}s`} + cubic-bezier(0.19, 1, 0.22, 1); + animation-fill-mode: both; + } ${mobile} { margin: 10px 0 0; diff --git a/web/src/util/css.ts b/web/src/util/css.ts index 912061f..bc84c4e 100644 --- a/web/src/util/css.ts +++ b/web/src/util/css.ts @@ -1 +1,12 @@ +import {keyframes} from 'styled-components'; + export const mobile = '@media screen and (max-width: 800px)'; + +export const slideUp = keyframes` +from { + transform: translateY(100%); +} +to { + transform: translateY(0); +} +`;