fixes
This commit is contained in:
+2
-2
@@ -1,14 +1,14 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { Motivation } from './components/Layout/Motivation';
|
||||
import { Header } from './components/Layout/Header';
|
||||
import { Navbar } from './components/Layout/Navbar';
|
||||
import { Footer } from './components/Layout/Footer';
|
||||
import ModalContainer from './components/Main/ModalContainer';
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<div className="bg-[#14161F]">
|
||||
<Navbar />
|
||||
<Header />
|
||||
<Motivation />
|
||||
<main>
|
||||
<Outlet />
|
||||
</main>
|
||||
|
||||
@@ -1,42 +1,156 @@
|
||||
import { Marquee } from '../Main/Marquee';
|
||||
import { motion } from 'framer-motion';
|
||||
import { PropsWithChildren, useRef, useState } from 'react';
|
||||
import { Anchor } from '../../ui/NavLink';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Lang, useLang } from '../../store/languageStore';
|
||||
import { HashLink } from 'react-router-hash-link';
|
||||
import { useOnClickOutside } from 'usehooks-ts';
|
||||
import useModalStore from '../../store/modalStore';
|
||||
import ModalWithForm from '../Main/ModalWithForm';
|
||||
import Button from '../../ui/Button';
|
||||
|
||||
export function Header() {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const { value: lang } = useLang();
|
||||
const setModal = useModalStore(state => state.setModal);
|
||||
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const menuBtnRef = useRef<HTMLButtonElement>(null);
|
||||
useOnClickOutside<HTMLDivElement | HTMLButtonElement>(
|
||||
[menuRef, menuBtnRef],
|
||||
() => setMenuOpen(false),
|
||||
);
|
||||
|
||||
return (
|
||||
<header className="text-[#ffffff]">
|
||||
<div className="lg:px-10 sm:px-6 mobile:px-4 lg:py-28 sm:py-12 mobile:py-14">
|
||||
<h1 className="desktop-figma:mb-[38px] pb-8 font-medium lg:block mobile:max-lg:hidden h1">
|
||||
Создаем{' '}
|
||||
<span
|
||||
className="bg-text-gradient bg-gradient-to-r from-[#798FFF] to-[#D375FF]"
|
||||
style={{
|
||||
backgroundClip: 'text',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
}}
|
||||
<>
|
||||
<nav className="flex items-stretch justify-between border-b border-[#3D425C] lg:pl-10 mobile:pl-4 lg:min-h-[72px] mobile:min-h-16">
|
||||
<Link to={'/'} className="flex w-[104px] justify-between items-center">
|
||||
<img src="src/assets/logo.svg" alt="" />
|
||||
<img
|
||||
src="src/assets/text_logo.svg"
|
||||
alt=""
|
||||
className="lg:block mobile:hidden"
|
||||
/>
|
||||
</Link>
|
||||
<div className="flex">
|
||||
<Anchor route="#products">Типы тренажеров</Anchor>
|
||||
<Anchor route="#trainings">Варианты комплектации</Anchor>
|
||||
<Anchor route="#projects">Проекты</Anchor>
|
||||
<Anchor route="#events">События</Anchor>
|
||||
<Button
|
||||
onClick={() => setModal(<ModalWithForm />)}
|
||||
className="rounded-none btn-text font-semibold sm:block mobile:hidden px-10"
|
||||
>
|
||||
интерактивные тренажеры
|
||||
</span>{' '}
|
||||
для промышленности и образования
|
||||
</h1>
|
||||
<h1 className="font-medium lg:hidden mobile:max-lg:block h1">
|
||||
Интерактивные тренажеры{' '}
|
||||
<span
|
||||
className="bg-text-gradient bg-gradient-to-r from-[#798FFF] to-[#D375FF]"
|
||||
style={{
|
||||
backgroundClip: 'text',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
}}
|
||||
Оставить заявку
|
||||
</Button>
|
||||
<LangToggler lang={lang} />
|
||||
<button
|
||||
ref={menuBtnRef}
|
||||
onClick={() => setMenuOpen(prev => !prev)}
|
||||
className="px-6 py-5 min-[1350px]:hidden mobile:block border-[#3D425C] mobile:max-sm:border-l"
|
||||
>
|
||||
для обучения сотрудников
|
||||
</span>
|
||||
</h1>
|
||||
<h3 className="max-w-[768px] lg:block mobile:max-lg:hidden h3">
|
||||
Помогаем сократить затраты на обучение, повысить безопасность и
|
||||
производительность
|
||||
</h3>
|
||||
</div>
|
||||
<Marquee />
|
||||
</header>
|
||||
<img
|
||||
src={`src/assets/${menuOpen ? 'cross' : 'burger'}.svg`}
|
||||
alt=""
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: +menuOpen }}
|
||||
transition={{ duration: 0.2 }}
|
||||
ref={menuRef}
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className={
|
||||
'absolute z-50 w-full min-[1350px]:hidden sm:max-[1350px]:max-w-[340px] right-0' +
|
||||
(menuOpen ? ' shadow-[0_0_0_9999px_rgba(0,0,0,.4)]' : '')
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<BurgerAnchor route="#products">Типы тренажеров</BurgerAnchor>
|
||||
<BurgerAnchor route="#trainings">Варианты комплектации</BurgerAnchor>
|
||||
<BurgerAnchor route="#projects">Проекты</BurgerAnchor>
|
||||
<BurgerAnchor route="#events">События</BurgerAnchor>
|
||||
</div>
|
||||
<div className="grid mobile:max-sm:grid-cols-[216px_1fr_1fr] sm:grid-cols-2 ">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setMenuOpen(false);
|
||||
setModal(<ModalWithForm />);
|
||||
}}
|
||||
width="full"
|
||||
className="sm:hidden font-semibold btn-text rounded-none"
|
||||
>
|
||||
Оставить заявку
|
||||
</Button>
|
||||
<ChooseLang lang="RU" />
|
||||
<ChooseLang lang="EN" />
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BurgerAnchor({
|
||||
children,
|
||||
route,
|
||||
}: PropsWithChildren<{ route: string }>) {
|
||||
return (
|
||||
<HashLink
|
||||
to={route}
|
||||
className="flex items-center px-10 py-6 gap-1 text-[#ffffff] btn-text bg-[#14161F] w-full last:border-b-0 [&:not(:last-child)]:border-b sm:border-l font-semibold border-[#3D425C] hover:bg-[#3D425C] active:bg-[#14161F]"
|
||||
>
|
||||
<img src="src/assets/cube.svg" alt="" />
|
||||
{children}
|
||||
</HashLink>
|
||||
);
|
||||
}
|
||||
|
||||
function ChooseLang({ lang }: { lang: 'RU' | 'EN' }) {
|
||||
const { updateLang, value } = useLang();
|
||||
return (
|
||||
<button
|
||||
onClick={() => updateLang(lang)}
|
||||
className={
|
||||
'text-[#ffffff] min-h-[72px] w-full h-full btn-text font-semibold bg-[#14161F] hover:bg-[#3D425C] border active:bg-[#14161F] ' +
|
||||
(value === lang
|
||||
? '[border-image:linear-gradient(to_right,#798FFF,#D375FF)_3]'
|
||||
: 'border-[#3D425C]')
|
||||
}
|
||||
>
|
||||
{lang}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function LangToggler({ lang }: { lang: Lang }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const langTogglerRef = useRef<HTMLDivElement>(null);
|
||||
useOnClickOutside(langTogglerRef, () => setOpen(false));
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={langTogglerRef}
|
||||
className="min-w-[101px] mobile:max-[1349px]:hidden hover:bg-[#3D425C] active:bg-[#14161F]"
|
||||
>
|
||||
<button
|
||||
onClick={() => setOpen(prev => !prev)}
|
||||
className="mx-6 h-full gap-x-1 items-center flex text-[#ffffff] font-semibold btn-text"
|
||||
>
|
||||
{lang}
|
||||
<img src="src/assets/arrow_down.svg" alt="" />
|
||||
</button>
|
||||
<motion.div
|
||||
className="absolute grid grid-cols-2 min-w-[101px]"
|
||||
onClick={() => setOpen(false)}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: +open }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<ChooseLang lang={'RU'} />
|
||||
<ChooseLang lang={'EN'} />
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Marquee } from '../Main/Marquee';
|
||||
|
||||
export function Motivation() {
|
||||
return (
|
||||
<header className="text-[#ffffff]">
|
||||
<div className="lg:px-10 sm:px-6 mobile:px-4 lg:py-28 sm:py-12 mobile:py-14">
|
||||
<h1 className="desktop-figma:mb-[38px] pb-8 font-medium lg:block mobile:max-lg:hidden h1">
|
||||
Создаем{' '}
|
||||
<span
|
||||
className="bg-text-gradient bg-gradient-to-r from-[#798FFF] to-[#D375FF]"
|
||||
style={{
|
||||
backgroundClip: 'text',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
интерактивные тренажеры
|
||||
</span>{' '}
|
||||
для промышленности и образования
|
||||
</h1>
|
||||
<h1 className="font-medium lg:hidden mobile:max-lg:block h1">
|
||||
Интерактивные тренажеры{' '}
|
||||
<span
|
||||
className="bg-text-gradient bg-gradient-to-r from-[#798FFF] to-[#D375FF]"
|
||||
style={{
|
||||
backgroundClip: 'text',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
для обучения сотрудников
|
||||
</span>
|
||||
</h1>
|
||||
<h3 className="max-w-[768px] lg:block mobile:max-lg:hidden h3">
|
||||
Помогаем сократить затраты на обучение, повысить безопасность и
|
||||
производительность
|
||||
</h3>
|
||||
</div>
|
||||
<Marquee />
|
||||
</header>
|
||||
);
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
import { motion } from 'framer-motion';
|
||||
import { PropsWithChildren, useRef, useState } from 'react';
|
||||
import { NavLink } from '../../ui/NavLink';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Lang, useLang } from '../../store/language';
|
||||
import { HashLink } from 'react-router-hash-link';
|
||||
import { useOnClickOutside } from 'usehooks-ts';
|
||||
import useModalStore from '../../store/modal';
|
||||
import ModalWithForm from '../Main/ModalWithForm';
|
||||
import Button from '../../ui/Button';
|
||||
|
||||
export function Navbar() {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const { value: lang } = useLang();
|
||||
const setModal = useModalStore(state => state.setModal);
|
||||
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
const menuBtnRef = useRef<HTMLButtonElement>(null);
|
||||
useOnClickOutside<HTMLDivElement | HTMLButtonElement>(
|
||||
[menuRef, menuBtnRef],
|
||||
() => setMenuOpen(false),
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className="flex items-stretch justify-between border-b border-[#3D425C] lg:pl-10 mobile:pl-4 lg:min-h-[72px] mobile:min-h-16">
|
||||
<Link to={'/'} className="flex w-[104px] justify-between items-center">
|
||||
<img src="src/assets/logo.svg" alt="" />
|
||||
<img
|
||||
src="src/assets/text_logo.svg"
|
||||
alt=""
|
||||
className="lg:block mobile:hidden"
|
||||
/>
|
||||
</Link>
|
||||
<div className="flex">
|
||||
<NavLink route="#products">Типы тренажеров</NavLink>
|
||||
<NavLink route="#trainings">Варианты комплектации</NavLink>
|
||||
<NavLink route="#projects">Проекты</NavLink>
|
||||
<NavLink route="#events">События</NavLink>
|
||||
<Button
|
||||
onClick={() => setModal(<ModalWithForm />)}
|
||||
className="rounded-none btn-text font-semibold sm:block mobile:hidden px-10"
|
||||
>
|
||||
Оставить заявку
|
||||
</Button>
|
||||
<LangToggler lang={lang} />
|
||||
<button
|
||||
ref={menuBtnRef}
|
||||
onClick={() => setMenuOpen(prev => !prev)}
|
||||
className="px-6 py-5 min-[1350px]:hidden mobile:block border-[#3D425C] mobile:max-sm:border-l"
|
||||
>
|
||||
<img
|
||||
src={`src/assets/${menuOpen ? 'cross' : 'burger'}.svg`}
|
||||
alt=""
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: +menuOpen }}
|
||||
transition={{ duration: 0.2 }}
|
||||
ref={menuRef}
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className={
|
||||
'absolute z-50 w-full min-[1350px]:hidden sm:max-[1350px]:max-w-[340px] right-0 sm:border-l border-b border-[#3D425C]' +
|
||||
(menuOpen ? ' shadow-[0_0_0_9999px_rgba(0,0,0,.4)]' : '')
|
||||
}
|
||||
>
|
||||
<BurgerLink route="#products">Типы тренажеров</BurgerLink>
|
||||
<BurgerLink route="#trainings">Варианты комплектации</BurgerLink>
|
||||
<BurgerLink route="#projects">Проекты</BurgerLink>
|
||||
<BurgerLink route="#events">События</BurgerLink>
|
||||
<div className="grid mobile:max-sm:grid-cols-[216px_1fr_1fr] sm:grid-cols-2">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setMenuOpen(false);
|
||||
setModal(<ModalWithForm />);
|
||||
}}
|
||||
width="full"
|
||||
className="sm:hidden font-semibold btn-text rounded-none"
|
||||
>
|
||||
Оставить заявку
|
||||
</Button>
|
||||
<ChooseLang lang="RU" />
|
||||
<ChooseLang lang="EN" />
|
||||
</div>
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BurgerLink({ children, route }: PropsWithChildren<{ route: string }>) {
|
||||
return (
|
||||
<HashLink
|
||||
to={route}
|
||||
className="flex items-center px-10 py-6 gap-1 text-[#ffffff] btn-text bg-[#14161F] w-full font-semibold border-[#3D425C] border-b hover:bg-[#3D425C] active:bg-[#14161F]"
|
||||
>
|
||||
<img src="src/assets/cube.svg" alt="" />
|
||||
{children}
|
||||
</HashLink>
|
||||
);
|
||||
}
|
||||
|
||||
function ChooseLang({
|
||||
lang,
|
||||
isBordered = false,
|
||||
}: {
|
||||
lang: 'RU' | 'EN';
|
||||
isBordered?: boolean;
|
||||
}) {
|
||||
const { updateLang, value } = useLang();
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'min-h-[72px] ' +
|
||||
(value !== lang
|
||||
? 'bg-[#3D425C]'
|
||||
: 'bg-gradient-to-r from-[#798FFF] to-[#D375FF] p-px')
|
||||
}
|
||||
>
|
||||
<button
|
||||
onClick={() => updateLang(lang)}
|
||||
className={
|
||||
'text-[#ffffff] w-full h-full btn-text font-semibold bg-[#14161F] hover:bg-[#3D425C] active:bg-[#14161F] ' +
|
||||
(isBordered ? 'border border-[#3D425C]' : '')
|
||||
}
|
||||
>
|
||||
{lang}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LangToggler({ lang }: { lang: Lang }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const langTogglerRef = useRef<HTMLDivElement>(null);
|
||||
useOnClickOutside(langTogglerRef, () => setOpen(false));
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={langTogglerRef}
|
||||
className="min-w-[101px] mobile:max-[1349px]:hidden box-border border-r border-[#3D425C] hover:bg-[#3D425C] active:bg-[#14161F]"
|
||||
>
|
||||
<button
|
||||
onClick={() => setOpen(prev => !prev)}
|
||||
className="mx-6 h-full gap-x-1 items-center flex text-[#ffffff] font-semibold box-border btn-text"
|
||||
>
|
||||
{lang}
|
||||
<img src="src/assets/arrow_down.svg" alt="" />
|
||||
</button>
|
||||
<motion.div
|
||||
className="absolute grid grid-cols-2 min-w-[101px] box-border"
|
||||
onClick={() => setOpen(false)}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: +open }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<ChooseLang isBordered lang={'RU'} />
|
||||
<ChooseLang isBordered lang={'EN'} />
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import useModalStore from '../../store/modal';
|
||||
import useModalStore from '../../store/modalStore';
|
||||
|
||||
export default function ModalContainer() {
|
||||
const modal = useModalStore(state => state.modal);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Close2Icon } from '../icons/Close2Icon';
|
||||
import useModalStore from '../../store/modal';
|
||||
import useModalStore from '../../store/modalStore';
|
||||
import ContactsForm from './ContactsForm';
|
||||
|
||||
function ModalWithForm() {
|
||||
|
||||
@@ -116,8 +116,6 @@ function Slider({
|
||||
setSliderOffset(-baseOffset);
|
||||
}, [order, baseOffset, slide]);
|
||||
|
||||
useEffect(() => console.log(sliderOffset), [sliderOffset]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col lg:mt-4 sm:mt-3 mobile:mt-2">
|
||||
<div {...handlers}>
|
||||
|
||||
@@ -78,7 +78,14 @@ function TrainingsFeature({
|
||||
</div>
|
||||
<div className="mobile:max-sm:flex sm:hidden justify-between items-end">
|
||||
<p className="text-[#52587A] m-text mb-5">{order}</p>
|
||||
<img src={src} alt="" className="w-[50vw]" />
|
||||
<div className="flex flex-col items-center">
|
||||
<img src={src} alt="" className="relative z-30 w-[50vw]" />
|
||||
<img
|
||||
src="src/assets/vr_backlight.svg"
|
||||
className="absolute w-[36vw]"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="md:flex mobile:max-md:hidden">
|
||||
<motion.div
|
||||
@@ -87,7 +94,7 @@ function TrainingsFeature({
|
||||
transition={{
|
||||
duration: 0.4,
|
||||
}}
|
||||
className="-my-10 mobile:max-lg:hidden flex items-center justify-center "
|
||||
className="-my-10 mobile:max-lg:hidden flex items-center justify-center"
|
||||
>
|
||||
<img
|
||||
src={src}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { HashLink } from 'react-router-hash-link';
|
||||
|
||||
export function NavLink({
|
||||
export function Anchor({
|
||||
children,
|
||||
route,
|
||||
className = '',
|
||||
|
||||
Reference in New Issue
Block a user