diff --git a/src/Card/index.tsx b/src/Card/index.tsx index 9dcdabf..bdb7f8f 100644 --- a/src/Card/index.tsx +++ b/src/Card/index.tsx @@ -3,7 +3,11 @@ import { IData } from '../types'; import './main.css'; -const Card = ({ data }: { data: IData }) => ( +type props = { + data: IData; +}; + +const Card: React.FC = ({ data }) => ( { - if (error) { - return error.toString(); - } - if (path === '/list' && searchQuery) { - let result = ''; - - if (searchQuery.class_num) { - result = result + searchQuery.class_num + ' класс'; - } - - if (searchQuery.predmet_type) { - result = result + ' ' + searchQuery.predmet_type; - } - - if (searchQuery.teacher) { - result = result + ' ' + searchQuery.teacher; - } - - if (searchQuery.search) { - result = result + ' поиск по "' + searchQuery.search + '"'; - } - - return result; - } - - if (path === '/') { - return 'Банк семинаров'; - } - - return ''; -}; - -const Header = ({ - query, - loading, - setSearchQuery -}: { +type props = { query: IFilterQuery; loading: ILoadingState; setSearchQuery: Dispatch>; -}) => { +}; + +const Header: React.FC = ({ query, loading, setSearchQuery }) => { /* * Hooks definitions */ - const history = useHistory(); - - useEffect(() => {}, [loading]); - - /* - * Animations - */ - const headerVariants = { - expanded: { - height: '100vh' - }, - collapsed: { - height: '10vh' - } - }; - - const transition: Transition = { - duration: 0.5, - ease: 'linear' - }; + const { pathname } = useLocation(); return ( -

{genName(query, history.location.pathname, loading.error)}

+

{genName(query, pathname, loading.error)}

{loading.fetching ? ( diff --git a/src/Header/utils.ts b/src/Header/utils.ts new file mode 100644 index 0000000..f35b2a7 --- /dev/null +++ b/src/Header/utils.ts @@ -0,0 +1,40 @@ +import { IFilterQuery } from '../types'; + +const genName = ( + searchQuery: IFilterQuery, + path: string, + error: string +): string => { + if (error) { + return error.toString(); + } + if (path === '/list' && searchQuery) { + let result = ''; + + if (searchQuery.class_num) { + result = result + searchQuery.class_num + ' класс'; + } + + if (searchQuery.predmet_type) { + result = result + ' ' + searchQuery.predmet_type; + } + + if (searchQuery.teacher) { + result = result + ' ' + searchQuery.teacher; + } + + if (searchQuery.search) { + result = result + ' поиск по "' + searchQuery.search + '"'; + } + + return result; + } + + if (path === '/') { + return 'Банк семинаров'; + } + + return ''; +}; + +export { genName }; diff --git a/src/Home/handlers.ts b/src/Home/handlers.ts new file mode 100644 index 0000000..1dea41a --- /dev/null +++ b/src/Home/handlers.ts @@ -0,0 +1,20 @@ +import { Dispatch, SetStateAction } from 'react'; +import { IFilterQuery } from '../types'; + +const handleShowMore = ( + predmet_type: string, + class_num: number, + setSearchQuery: Dispatch> +) => { + setSearchQuery( + (prev): IFilterQuery => { + return { + ...prev, + predmet_type, + class_num: class_num.toString() + }; + } + ); +}; + +export { handleShowMore }; diff --git a/src/Home/index.tsx b/src/Home/index.tsx index e4a51d1..eeb0854 100644 --- a/src/Home/index.tsx +++ b/src/Home/index.tsx @@ -4,15 +4,15 @@ import { Link } from 'react-router-dom'; import Card from '../Card'; import NothingFound from '../NothingFound'; import { IData, IFilterQuery, ILoadingState } from '../types'; +import { handleShowMore } from './handlers'; import './main.css'; -const Home = ({ - setLoading, - setSearchQuery -}: { +type props = { setLoading: Dispatch>; setSearchQuery: Dispatch>; -}) => { +}; + +const Home: React.FC = ({ setLoading, setSearchQuery }) => { const [data, setData] = useState([]); useEffect(() => { @@ -38,18 +38,6 @@ const Home = ({ ...Array.from(new Set(data.map((el) => el.predmet_type).sort())) ]; - const handleShowMore = (predmet_type: string, class_num: number) => { - setSearchQuery( - (prev): IFilterQuery => { - return { - ...prev, - predmet_type, - class_num: class_num.toString() - }; - } - ); - }; - return (
{classes.length ? ( @@ -87,7 +75,8 @@ const Home = ({ onClick={() => handleShowMore( subject, - class_num + class_num, + setSearchQuery ) } to={'/list'} diff --git a/src/Logotype/index.tsx b/src/Logotype/index.tsx index 18e7bfa..c1fa348 100644 --- a/src/Logotype/index.tsx +++ b/src/Logotype/index.tsx @@ -4,13 +4,13 @@ import { Link } from 'react-router-dom'; import { emptyQuery } from '../Navbar/utils'; import { IFilterQuery } from '../types'; import LogoImage from './logo.png'; -import './main.css' +import './main.css'; -const Logotype = ({ - setSearchQuery -}: { +type props = { setSearchQuery: Dispatch>; -}) => { +}; + +const Logotype: React.FC = ({ setSearchQuery }) => { return ( , + setSearchQuery: Dispatch>, + setFiltersCollapsed: Dispatch> +) => { + if (!filtersCollapsed) { + setSearchQuery((prev) => { + return { ...prev, ...localFilters }; + }); + } + setFiltersCollapsed((prev) => !prev); +}; + +const handleSelectChange = ( + element: HTMLSelectElement, + setLocalFilters: Dispatch>> +) => { + setLocalFilters((prev) => { + return { ...prev, [element.name]: element.value }; + }); +}; + +const handleSearchButton = ( + searchCollapsed: boolean, + setSearchCollapsed: Dispatch>, + setInputFocus: () => void, + searchInputRef: RefObject, + setSearchQuery: Dispatch> +) => { + if (searchCollapsed) { + setSearchCollapsed(false); + setInputFocus(); + } else if (searchInputRef && searchInputRef.current) { + const value = searchInputRef.current.value; + + setSearchQuery((prev) => { + return { ...prev, search: value }; + }); + setSearchCollapsed(true); + + searchInputRef.current.value = ''; + } +}; + +export { handleFiltersButton, handleSelectChange, handleSearchButton }; diff --git a/src/Navbar/index.tsx b/src/Navbar/index.tsx index 4e44cc4..fc0065e 100644 --- a/src/Navbar/index.tsx +++ b/src/Navbar/index.tsx @@ -1,5 +1,4 @@ import React, { - ChangeEvent, Dispatch, SetStateAction, useEffect, @@ -14,25 +13,31 @@ import SearchIcon from './search.svg'; import { IFilterQuery } from '../types'; import { useFocus } from '../utils'; import Logotype from '../Logotype'; +import { + filtersVariants, + navVariants, + searchVariants, + transition +} from './animations'; +import { + handleFiltersButton, + handleSearchButton, + handleSelectChange +} from './handlers'; -const Navbar = ({ - setSearchQuery, - query -}: { +type props = { setSearchQuery: Dispatch>; query: IFilterQuery; -}) => { - /* - * Hooks - */ +}; +const Navbar: React.FC = ({ setSearchQuery, query }) => { const [searchCollapsed, setSearchCollapsed] = useState(true); const [filtersCollapsed, setFiltersCollapsed] = useState(true); - const [localFilters, setLocalFilters] = useState>(); + const [localFilters, setLocalFilters] = useState>({}); - const searchInput = useRef(null); + const searchInputRef = useRef(null); - const setInputFocus = useFocus(searchInput); + const setInputFocus = useFocus(searchInputRef); const formRef = useRef(null); @@ -48,89 +53,6 @@ const Navbar = ({ } }, [query]); - /* - * Animations - */ - - const searchVariants = { - open: { - width: 'calc(100vw - 4vh)', - display: 'block' - }, - closed: { - width: '6vh', - transitionEnd: { - display: 'none' - } - } - }; - - const navVariants = { - open: { - height: '100vh', - borderTopLeftRadius: 0, - borderTopRightRadius: 0 - }, - closed: { - height: '10vh', - borderTopLeftRadius: '20px', - borderTopRightRadius: '20px' - } - }; - - const filtersVariants = { - open: { - height: '100vh', - padding: '2vh' - }, - closed: { - height: 0, - padding: 0 - } - }; - - const transition = { - ease: 'easeIn', - duration: 0.5 - }; - - /* - * Input handlers - */ - - const handleFiltersButton = () => { - if (!filtersCollapsed) { - setSearchQuery((prev) => { - return { ...prev, ...localFilters }; - }); - } - setFiltersCollapsed((prev) => !prev); - }; - - const handleSelectChange = ({ - target: element - }: ChangeEvent) => { - setLocalFilters((prev) => { - return { ...prev, [element.name]: element.value }; - }); - }; - - const handleSearchButton = () => { - if (searchCollapsed) { - setSearchCollapsed(false); - setInputFocus(); - } else if (searchInput && searchInput.current) { - const value = searchInput.current.value; - - setSearchQuery((prev) => { - return { ...prev, search: value }; - }); - setSearchCollapsed(true); - - searchInput.current.value = ''; - } - }; - return ( + handleFiltersButton( + filtersCollapsed, + localFilters, + setSearchQuery, + setFiltersCollapsed + ) + } > Фильтр @@ -154,7 +83,15 @@ const Navbar = ({ @@ -163,10 +100,16 @@ const Navbar = ({ variants={searchVariants} transition={transition} aria-label="Поиск" - ref={searchInput} + ref={searchInputRef} onKeyDown={(e) => { if (e.key === 'Enter') { - handleSearchButton(); + handleSearchButton( + searchCollapsed, + setSearchCollapsed, + setInputFocus, + searchInputRef, + setSearchQuery + ); } }} type="search" @@ -188,7 +131,9 @@ const Navbar = ({ + handleSelectChange(e.target, setLocalFilters) + } id="typeFilter" > @@ -228,7 +175,9 @@ const Navbar = ({ + handleSelectChange(e.target, setLocalFilters) + } id="classFilter" > diff --git a/src/NothingFound/index.tsx b/src/NothingFound/index.tsx index 331af00..8f50867 100644 --- a/src/NothingFound/index.tsx +++ b/src/NothingFound/index.tsx @@ -4,11 +4,11 @@ import './main.css'; import icon from './icon.svg'; import { ILoadingState } from '../types'; -const NothingFound = ({ - setLoading -}: { +type props = { setLoading?: Dispatch>; -}) => { +}; + +const NothingFound: React.FC = ({ setLoading }) => { useEffect(() => { if (!setLoading) return; diff --git a/src/SubjectList/index.tsx b/src/SubjectList/index.tsx index 8f0c891..9a49fa2 100644 --- a/src/SubjectList/index.tsx +++ b/src/SubjectList/index.tsx @@ -7,13 +7,12 @@ import Card from '../Card'; import './main.css'; import NothingFound from '../NothingFound'; -const SubjectList = ({ - setLoading, - searchQuery -}: { +type props = { setLoading: Dispatch>; searchQuery: IFilterQuery; -}) => { +}; + +const SubjectList: React.FC = ({ setLoading, searchQuery }) => { const [data, setData] = useState([]); const { push: historyPush } = useHistory();