diff --git a/src/features/Common/Input/index.tsx b/src/features/Common/Input/index.tsx index cf0823cb..5b782f24 100644 --- a/src/features/Common/Input/index.tsx +++ b/src/features/Common/Input/index.tsx @@ -14,6 +14,7 @@ import { } from './styled' type Props = { + autoComplete?: string, defaultValue?: string, id: string, inputWidth?: number, @@ -32,6 +33,7 @@ type Props = { } & WrapperProps export const Input = ({ + autoComplete = '', defaultValue, error, id, @@ -70,6 +72,7 @@ export const Input = ({ } ( + + + +) + +export const LoaderUI = { + Circles, + Loader, + Wrapper, +} diff --git a/src/features/Loader/styled.tsx b/src/features/Loader/styled.tsx new file mode 100644 index 00000000..2e94c367 --- /dev/null +++ b/src/features/Loader/styled.tsx @@ -0,0 +1,57 @@ +import styled, { css, keyframes } from 'styled-components' + +import { LoaderProps } from './types' + +const animation = keyframes` + 0%, + 100% { + transform: scale(0); + } + 50% { + transform: scale(1.0); + } +` + +export const Wrapper = styled.div` + position: relative; + margin: 0 auto; + + ${({ diameter = 25 }) => ( + diameter + ? css` + width: ${diameter}px; + height: ${diameter}px; + ` + : '' + )} + + :before, + :after { + content: ''; + position: absolute; + top: 0; + background-color: ${({ color }) => color}; + width: 100%; + height: 100%; + border-radius: 50%; + animation: ${animation} 1s infinite ease-in-out; + } + + :before { + left: -130%; + animation-delay: 0.2s; + } + + :after { + left: 130%; + animation-delay: -0.2s; + } +` + +export const Circles = styled.div` + background-color: ${({ color }) => color}; + width: 100%; + height: 100%; + border-radius: 50%; + animation: ${animation} 1s infinite ease-in-out; +` diff --git a/src/features/Loader/types.tsx b/src/features/Loader/types.tsx new file mode 100644 index 00000000..493866e6 --- /dev/null +++ b/src/features/Loader/types.tsx @@ -0,0 +1,6 @@ +export type LoaderProps = { + /** цвет кружков прелоадера */ + color?: string, + /** диаметр кружков прелоадера */ + diameter?: number, +} diff --git a/src/features/Search/hooks/index.tsx b/src/features/Search/hooks/index.tsx index cda7504c..882dd7cf 100644 --- a/src/features/Search/hooks/index.tsx +++ b/src/features/Search/hooks/index.tsx @@ -13,7 +13,10 @@ import size from 'lodash/size' import type { SearchItems } from 'requests' import { getSearchItems } from 'requests' -import { useToggle } from 'hooks' +import { + useRequest, + useToggle, +} from 'hooks' import { SEARCH_DELAY, MIN_CHARACTERS_LENGTH } from '../config' import { useNormalizedItems } from './useNormalizedItems' @@ -21,17 +24,22 @@ import { useNormalizedItems } from './useNormalizedItems' export const useSearch = () => { const [searchItems, setSearchItems] = useState({}) const abortControllerRef = useRef(null) + const { close, isOpen, open, } = useToggle() + const { + isFetching, + request: searchItemsRequest, + } = useRequest(getSearchItems) + const fetchSearchItems = useCallback(debounce((searchString: string) => { const abortController = new window.AbortController() abortControllerRef.current = abortController - - getSearchItems(searchString, abortController.signal).then((data) => { + searchItemsRequest(searchString, abortController.signal).then((data) => { setSearchItems(data) abortControllerRef.current = null }) @@ -76,6 +84,7 @@ export const useSearch = () => { return { close, + isFetching, normalizedItems: useNormalizedItems(searchItems), onChange, onFocus, diff --git a/src/features/Search/index.tsx b/src/features/Search/index.tsx index 7399bb73..0c5adf25 100644 --- a/src/features/Search/index.tsx +++ b/src/features/Search/index.tsx @@ -6,6 +6,7 @@ import { PAGES } from 'config' import { Input } from 'features/Common' import { ItemsList } from 'features/ItemsList' import { OutsideClick } from 'features/OutsideClick' +import { Loader } from 'features/Loader' import { useSearch } from './hooks' import { Header } from './components/Header' @@ -13,11 +14,13 @@ import { Wrapper, Form, Results, + LoaderWrapper, } from './styled' export const Search = () => { const { close, + isFetching, normalizedItems: { players, teams, @@ -28,17 +31,24 @@ export const Search = () => { onSubmit, showResults, } = useSearch() + const isMatch = useRouteMatch(`/:sportName${PAGES.match}/:pageId`)?.isExact || false return (
+ {isFetching && ( + + + + )}
{showResults && ( diff --git a/src/features/Search/styled.tsx b/src/features/Search/styled.tsx index d66da0be..a3949bf2 100644 --- a/src/features/Search/styled.tsx +++ b/src/features/Search/styled.tsx @@ -11,26 +11,52 @@ import { export const Wrapper = styled.div` position: relative; -} +` + +export const LoaderWrapper = styled.div` + position: absolute; + top: 0; + background-color: rgba(129, 129, 129, 0.5); + width: 100%; + height: 48px; + box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); + border-radius: 2px; + display: flex; + align-items: center; + justify-content: center; + + @media ${devices.mobile} { + top: 20px; + } + + @media ${devices.tablet} { + top: 26px; + height: 28px; + } + ` export const Form = styled.form<{isMatch: boolean}>` width: 288px; - + position: relative; ${InputWrapper} { margin: 0; padding-bottom: 13px; @media ${devices.desktop} { - padding-left: 12px; - } + padding-left: 12px; + } - @media ${devices.tablet} { - padding-left: 6px; - height: 12px; - margin-top: 25px; - background-color: transparent; - } + @media ${devices.tablet} { + padding-left: 6px; + height: 12px; + margin-top: 25px; + background-color: transparent; + } + + @media ${devices.mobile} { + margin-top: 20px; + } } @media ${devices.desktop} { @@ -51,6 +77,12 @@ export const Form = styled.form<{isMatch: boolean}>` left: -240px; } } + + @media ${devices.mobile} { + :focus-within { + top: -33px; + } + } ${InputStyled} { width: 100%; @@ -84,7 +116,7 @@ export const Form = styled.form<{isMatch: boolean}>` margin-right: 0; } @media ${devices.tablet} { - margin-bottom: 2px; + margin-right: 0; background-image: url(/images/search-mob.svg); } }