236 lines
8.8 KiB
TypeScript
236 lines
8.8 KiB
TypeScript
'use client';
|
||
|
||
import { api } from '@/api';
|
||
import { ClassNameWrapper } from '@/hocs/ClassNameWrapper';
|
||
import { useMediaQueries } from '@/hooks/useMediaQueries';
|
||
import { useScroll } from '@/hooks/useScroll';
|
||
import { useCheckAuthQuery } from '@/queries/checkAuth';
|
||
import { useModalStore } from '@/stores/useModalStore';
|
||
import { Button } from '@/ui/Button';
|
||
import { HeaderLink } from '@/ui/HeaderLink';
|
||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||
import { AnimatePresence, motion } from 'framer-motion';
|
||
import Image from 'next/image';
|
||
import Link from 'next/link';
|
||
import { usePathname } from 'next/navigation';
|
||
import { useRef, useState } from 'react';
|
||
import { useOnClickOutside } from 'usehooks-ts';
|
||
import { BurgerIcon } from '../icons/BurgerIcon';
|
||
import { CloseIcon } from '../icons/CloseIcon';
|
||
import { LogoIcon } from '../icons/LogoIcon';
|
||
import { LogoWithTextIcon } from '../icons/LogoWithTextIcon';
|
||
import { SkolkovoIcon } from '../icons/SkolkovoIcon';
|
||
import { ModalWithFeedbackForm } from '../modals/ModalWithFeedbackForm';
|
||
import { Products } from './Products';
|
||
|
||
export function Header() {
|
||
const { setModal } = useModalStore();
|
||
|
||
const queryClient = useQueryClient();
|
||
|
||
const { data: auth } = useCheckAuthQuery();
|
||
|
||
const { mutate: logout } = useMutation({
|
||
mutationKey: ['checkAuth'],
|
||
mutationFn: async () =>
|
||
await api.get('auth/logout').json<{ success: boolean }>(),
|
||
onSuccess() {
|
||
queryClient.invalidateQueries({ queryKey: ['checkAuth'] });
|
||
},
|
||
});
|
||
|
||
const [burgerOpened, setBurgerOpened] = useState(false);
|
||
const [productsOpened, setProductsOpened] = useState(false);
|
||
|
||
const { isLg, isXs, isSm, isMd } = useMediaQueries();
|
||
|
||
const pathname = usePathname();
|
||
|
||
const burgerRef = useRef<HTMLDivElement>(null);
|
||
const burgerBtnRef = useRef<HTMLDivElement>(null);
|
||
const productsRef = useRef<HTMLDivElement>(null);
|
||
const productsBtnRef = useRef<HTMLDivElement>(null);
|
||
const logoRef = useRef<HTMLAnchorElement>(null);
|
||
|
||
useOnClickOutside([productsRef, productsBtnRef], () => {
|
||
setProductsOpened(false);
|
||
});
|
||
|
||
useOnClickOutside([burgerRef, burgerBtnRef], () => {
|
||
setBurgerOpened(false);
|
||
});
|
||
|
||
const scroll = useScroll(logoRef);
|
||
|
||
return (
|
||
<header className="lg:mt-5 relative flex items-center px-5">
|
||
<Link href={'/'} ref={logoRef}>
|
||
<ClassNameWrapper className="max-lg:hidden">
|
||
<LogoWithTextIcon />
|
||
</ClassNameWrapper>
|
||
</Link>
|
||
<div className="relative flex justify-center flex-1 m-auto">
|
||
<AnimatePresence>
|
||
<motion.nav
|
||
animate={{
|
||
width:
|
||
burgerOpened && (isXs || isSm)
|
||
? 340
|
||
: productsOpened && !isXs && !isSm
|
||
? isLg
|
||
? 'calc(992 / 1440 * 100vw)'
|
||
: 'calc(100vw - 32px)'
|
||
: 'auto',
|
||
}}
|
||
className="fixed lg:top-5 top-4 p-1 rounded-[20px] bg-[#37393B99] backdrop-blur-2xl flex gap-1 z-[12]"
|
||
>
|
||
{((isLg && scroll < -logoRef.current?.clientHeight!) || !isLg) && (
|
||
<Link
|
||
href={'/'}
|
||
className="aspect-square p-4 alg:hidden hover:bg-[#37393B99] rounded-xl content-center"
|
||
>
|
||
<ClassNameWrapper className={'w-4 h-4'}>
|
||
<LogoIcon />
|
||
</ClassNameWrapper>
|
||
</Link>
|
||
)}
|
||
<div ref={productsBtnRef} className="max-md:hidden">
|
||
<button
|
||
className="px-6 py-4 font-medium btnm hover:bg-[#37393B99] rounded-2xl active:bg-white active:text-black"
|
||
onClick={() => {
|
||
setProductsOpened((prev) => !prev);
|
||
}}
|
||
>
|
||
Продукты
|
||
</button>
|
||
</div>
|
||
<HeaderLink
|
||
className="max-md:hidden btnm"
|
||
href={'/about'}
|
||
text={'О нас'}
|
||
/>
|
||
<HeaderLink
|
||
className="max-md:hidden btnm"
|
||
href={'/blog'}
|
||
text={'Блог'}
|
||
/>
|
||
<HeaderLink
|
||
className="max-md:hidden btnm"
|
||
href={'/projects'}
|
||
text={'Проекты'}
|
||
/>
|
||
<div className="md:justify-end flex justify-center flex-1">
|
||
<Button
|
||
className="btnm font-medium py-[17px] text-nowrap"
|
||
rounded="2xl"
|
||
onClick={() => setModal(<ModalWithFeedbackForm />, 'form')}
|
||
>
|
||
Оставить заявку
|
||
</Button>
|
||
</div>
|
||
<div className="md:hidden md:justify-end flex" ref={burgerBtnRef}>
|
||
<button
|
||
className="!border-none p-[18px] hover:bg-[#232425] rounded-2xl active:opacity-50 outline-none"
|
||
onClick={() => setBurgerOpened((prev) => !prev)}
|
||
>
|
||
<ClassNameWrapper className="w-4 h-4 text-white">
|
||
{burgerOpened ? <CloseIcon /> : <BurgerIcon />}
|
||
</ClassNameWrapper>
|
||
</button>
|
||
</div>
|
||
{auth && (
|
||
<div className="sm:max-md:absolute -right-1/4 sm:max-md:bg-[#37393B99] rounded-[20px] self-center">
|
||
<button
|
||
className="rounded-2xl btnm font-medium p-3 hover:bg-[#FF4517] transition-colors h-full hover:text-black outline-none"
|
||
onClick={() => logout()}
|
||
>
|
||
Выйти
|
||
</button>
|
||
</div>
|
||
)}
|
||
<AnimatePresence>
|
||
{burgerOpened && (isXs || isSm) && (
|
||
<>
|
||
<motion.div
|
||
initial={{ opacity: 0 }}
|
||
animate={{ opacity: 100 }}
|
||
exit={{ opacity: 0 }}
|
||
className="absolute w-full p-4 pt-2 top-16 rounded-2xl bg-[#232425] md:hidden space-y-6"
|
||
>
|
||
<div className="px-2 -mx-4 space-y-1">
|
||
<HeaderLink
|
||
href={'/about'}
|
||
text={'О нас'}
|
||
className="accent"
|
||
/>
|
||
<HeaderLink href={'/'} text={'Блог'} className="accent" />
|
||
<HeaderLink
|
||
href={'/projects'}
|
||
text={'Проекты'}
|
||
className="accent"
|
||
/>
|
||
</div>
|
||
<hr className="border-[#37393B]" />
|
||
<div className="space-y-[10px]">
|
||
<p className="btnm opacity-60">Продукты</p>
|
||
<Products />
|
||
</div>
|
||
<div>
|
||
<p className="btnm opacity-60 mb-1">Контакты:</p>
|
||
<Link
|
||
href={'tel:88007700067'}
|
||
className="accent font-medium outline-none"
|
||
>
|
||
8 800 770 00 67
|
||
</Link>
|
||
</div>
|
||
</motion.div>
|
||
<div className="fixed w-screen h-screen bg-[#0F101199] backdrop-blur-lg -top-4 -left-[calc((100vw-100%)/2)] -z-[1]" />
|
||
</>
|
||
)}
|
||
</AnimatePresence>
|
||
</motion.nav>
|
||
</AnimatePresence>
|
||
<AnimatePresence>
|
||
{productsOpened && (isMd || isLg) && (
|
||
<>
|
||
<motion.div
|
||
onClick={() => setProductsOpened(false)}
|
||
ref={productsRef}
|
||
animate={{
|
||
width: isLg
|
||
? 'calc(992 / 1440 * 100vw)'
|
||
: 'calc(100vw - 32px)',
|
||
}}
|
||
className="fixed max-md:hidden top-20 rounded-2xl bg-[#37393B99] backdrop-blur-2xl p-1 z-[13]"
|
||
>
|
||
<Products />
|
||
</motion.div>
|
||
<div className="fixed w-screen h-screen bg-[#0F101199] backdrop-blur-lg top-0 left-0 z-[11]" />
|
||
</>
|
||
)}
|
||
</AnimatePresence>
|
||
</div>
|
||
{pathname === '/projects' ? (
|
||
<Link
|
||
href={'https://dprofile.ru/graff.estate'}
|
||
className="max-xl:hidden rounded-[20px] bg-[#37393B99] backdrop-blur-[20px] hover:bg-[#232425] py-5 pl-[63px] pr-[33px] right-5 fixed z-[2] top-5 overflow-clip bg-[url(/img/components/header/dp.png)] bg-no-repeat bg-right-bottom"
|
||
>
|
||
<Image
|
||
src={'/img/components/header/show_case.png'}
|
||
width={53.77}
|
||
height={104.48}
|
||
alt="кейс dprofile"
|
||
className="absolute bottom-0 left-0 rotate-[2.56deg]"
|
||
/>
|
||
<p className="btnm font-medium">Смотреть кейс</p>
|
||
</Link>
|
||
) : (
|
||
<ClassNameWrapper className="max-lg:hidden">
|
||
<SkolkovoIcon />
|
||
</ClassNameWrapper>
|
||
)}
|
||
</header>
|
||
);
|
||
}
|