marquee todo...
This commit is contained in:
+2
-1
@@ -12,7 +12,8 @@
|
||||
"dependencies": {
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^6.23.1"
|
||||
"react-router-dom": "^6.23.1",
|
||||
"zustand": "^4.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.3.3",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.0001 12.0001L17.6571 6.34356M12.0001 12.0001L6.34325 6.34326M12.0001 12.0001L17.657 17.657M12.0001 12.0001L6.34314 17.6571" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 321 B |
+4
-4
@@ -1,5 +1,5 @@
|
||||
<svg width="10" height="16" viewBox="0 0 10 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.42193 3H10V9.56928H3.42193V3Z" fill="white"/>
|
||||
<path d="M10 9.56928H3.42193L0 12.9756H6.33444L10 9.56928Z" fill="white"/>
|
||||
<path d="M3.42193 9.56928V3L0 6.6496V12.9756L3.42193 9.56928Z" fill="white"/>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.8">
|
||||
<path d="M18.1371 6H10.1532L6 10.4296V18.1075H13.6882L18.1371 13.9732V6Z" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 314 B After Width: | Height: | Size: 215 B |
@@ -31,7 +31,7 @@ export function Header() {
|
||||
для обучения сотрудников
|
||||
</span>
|
||||
</h1>
|
||||
<h2 className="text-[32px] max-w-[768px] desktop:block mobile:max-desktop:hidden">
|
||||
<h2 className="text-[32px] max-w-[768px] desktop:block mobile:max-desktop:hidden desktop:leading-8">
|
||||
Помогаем сократить затраты на обучение, повысить безопасность и
|
||||
производительность
|
||||
</h2>
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { useState } from 'react';
|
||||
import { NavLink } from '../../ui/NavLink';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Lang, useLang } from '../../store/language';
|
||||
|
||||
export function Navbar() {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const { value: lang } = useLang();
|
||||
|
||||
return (
|
||||
<nav className="flex items-stretch justify-between text-[#ffffff] desktop:text-lg tablet:text-base font-semibold border-b border-[#3D425C] desktop:pl-10 t mobile:pl-4">
|
||||
<div className="flex w-[104px] justify-between items-center mobile:py-3">
|
||||
<>
|
||||
<nav className="flex items-stretch justify-between border-b border-[#3D425C] desktop:pl-10 mobile:pl-4 desktop:min-h-[72px] mobile:min-h-16">
|
||||
<div className="flex w-[104px] justify-between items-center">
|
||||
<img src="src/assets/logo.svg" alt="" />
|
||||
<img
|
||||
src="src/assets/text_logo.svg"
|
||||
@@ -11,22 +18,103 @@ export function Navbar() {
|
||||
className="desktop:block mobile:hidden"
|
||||
/>
|
||||
</div>
|
||||
<div className="items-stretch flex">
|
||||
<div className="flex">
|
||||
<NavLink text="Типы тренажеров" route="/" />
|
||||
<NavLink text="Варианты комплектации" route="/" />
|
||||
<NavLink text="Проекты" route="/" />
|
||||
<NavLink text="События" route="/" />
|
||||
<button className="text-lg bg-gradient-to-r from-[#798FFF] to-[#D375FF] border-x border-[#3D425C] tablet:block mobile:hidden py-[30px] px-10">
|
||||
<button className="desktop:text-lg tablet:text-base font-semibold text-[#ffffff] bg-gradient-to-r from-[#798FFF] to-[#D375FF] border-x border-[#3D425C] tablet:block mobile:hidden px-10">
|
||||
Оставить заявку
|
||||
</button>
|
||||
<button className="p-6 gap-x-1 items-center desktop:flex mobile:hidden">
|
||||
<span>RU</span>
|
||||
<img src="src/assets/arrow_down.svg" alt="" />
|
||||
</button>
|
||||
<button className="px-6 py-5 min-[1350px]:hidden mobile:block border-l border-[#3D425C]">
|
||||
<img src="src/assets/burger.svg" alt="" />
|
||||
{!menuOpen && <LangToggler lang={lang} />}
|
||||
<button
|
||||
onClick={() => setMenuOpen(prev => !prev)}
|
||||
className="px-6 py-5 min-[1350px]:hidden mobile:block border-[#3D425C]"
|
||||
>
|
||||
<img
|
||||
src={`src/assets/${menuOpen ? 'cross' : 'burger'}.svg`}
|
||||
alt=""
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
{menuOpen && (
|
||||
<div
|
||||
onClick={() => setMenuOpen(false)}
|
||||
className="absolute w-full min-[1350px]:hidden tablet:max-[1350px]:max-w-[340px] right-0 border-x border-b border-[#3D425C] animate-[smooth-appear_1s_ease_forwards] duration-1000"
|
||||
>
|
||||
<BurgerLink route="/" text="Типы тренажеров" />
|
||||
<BurgerLink route="/" text="Варианты комплектации" />
|
||||
<BurgerLink route="/" text="Проекты" />
|
||||
<BurgerLink route="/" text="События" />
|
||||
<div className="grid mobile:max-tablet:grid-cols-[216px_1fr_1fr] tablet:grid-cols-2">
|
||||
<button className="text-[#ffffff] desktop:text-lg tablet:text-base tablet:hidden font-semibold bg-gradient-to-r from-[#798FFF] to-[#D375FF] border-x border-[#3D425C] py-[30px] px-10">
|
||||
Оставить заявку
|
||||
</button>
|
||||
<ChooseLang lang="RU" />
|
||||
<ChooseLang lang="EN" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BurgerLink({ text, route }: { text: string; route: string }) {
|
||||
return (
|
||||
<Link
|
||||
to={route}
|
||||
className="flex px-10 py-6 gap-1 text-[#ffffff] mobile:text-base desktop:text-lg bg-[#14161F] w-full font-semibold border-[#3D425C] border-b hover:bg-[#3D425C] active:bg-[#14161F]"
|
||||
>
|
||||
<img src="src/assets/cube.svg" alt="" />
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function ChooseLang({ lang }: { lang: 'RU' | 'EN' }) {
|
||||
const { updateLang, value } = useLang();
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'p-px min-h-[72px] ' +
|
||||
(value !== lang
|
||||
? 'bg-[#3D425C]'
|
||||
: 'bg-gradient-to-r from-[#798FFF] to-[#D375FF] box-border')
|
||||
}
|
||||
>
|
||||
<button
|
||||
onClick={() => updateLang(lang)}
|
||||
className={
|
||||
'text-[#ffffff] w-full h-full desktop:text-lg tablet:text-base font-semibold bg-[#14161F] hover:bg-[#3D425C] active:bg-[#14161F]'
|
||||
}
|
||||
>
|
||||
{lang}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function LangToggler({ lang }: { lang: Lang }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<div className="min-w-[101px] mobile:max-[1350px]:hidden box-border border-r border-[#3D425C]">
|
||||
<button
|
||||
onClick={() => setOpen(prev => !prev)}
|
||||
className="mx-6 h-full gap-x-1 items-center flex text-[#ffffff] font-semibold box-border"
|
||||
>
|
||||
{lang}
|
||||
<img src="src/assets/arrow_down.svg" alt="" />
|
||||
</button>
|
||||
{open && (
|
||||
<div
|
||||
className="absolute grid grid-cols-2 min-w-[101px] box-border"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
<ChooseLang lang={'RU'} />
|
||||
<ChooseLang lang={'EN'} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export function Marquee() {
|
||||
return (
|
||||
<div className="flex items-stretch overflow-clip">
|
||||
<div className="flex items-stretch overflow-clip relative">
|
||||
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C] py-[30px]">
|
||||
<img src="src/assets/rosatom.svg" className="m-auto" alt="" />
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useState } from 'react';
|
||||
import { MiniTitle } from '../../ui/MiniTitle';
|
||||
|
||||
export function Products() {
|
||||
const [curTab, setCurTab] = useState(0);
|
||||
|
||||
return (
|
||||
<div className="desktop:py-[70px] tablet:max-desktop:pt-14 tablet:max-desktop:pb-8 mobile:max-tablet:py-14 desktop:px-10 tablet:max-desktop:px-6 mobile:max-tablet:px-4 text-[#ffffff]">
|
||||
<h1 className="text-[64px] font-medium leading-[58px] mb-14 -tracking-[.02em] desktop:block mobile:hidden">
|
||||
@@ -21,18 +24,40 @@ export function Products() {
|
||||
<MiniTitle text="Продукты" />
|
||||
</div>
|
||||
<div className="flex gax-y-4 bg-[#3D425C4D] bg-opacity-3 rounded-xl p-1 mb-2 w-fit max-w-full overflow-auto tablet:max-desktop:mt-[13px] mobile:mt-6 mobile:max-[912px]:[-webkit-mask-image:_linear-gradient(to_left,rgba(32,35,50,0)_0%,rgba(32,35,50,1)_20%)]">
|
||||
<TabButton text="Промышленные тренажеры" />
|
||||
<TabButton text="Симуляторы управления техникой" />
|
||||
<TabButton text="Тренажеры для учебных заведений" />
|
||||
<TabButton
|
||||
className={curTab === 0 ? ' bg-[#798FFF]' : ''}
|
||||
onClick={() => setCurTab(0)}
|
||||
text="Промышленные тренажеры"
|
||||
/>
|
||||
<TabButton
|
||||
className={curTab === 1 ? ' bg-[#798FFF]' : ''}
|
||||
onClick={() => setCurTab(1)}
|
||||
text="Симуляторы управления техникой"
|
||||
/>
|
||||
<TabButton
|
||||
className={curTab === 2 ? ' bg-[#798FFF]' : ''}
|
||||
onClick={() => setCurTab(2)}
|
||||
text="Тренажеры для учебных заведений"
|
||||
/>
|
||||
</div>
|
||||
<TeachingItems />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TabButton({ text }: { text: string }) {
|
||||
function TabButton({
|
||||
text,
|
||||
className,
|
||||
onClick,
|
||||
}: { text: string } & React.HTMLProps<HTMLButtonElement>) {
|
||||
return (
|
||||
<button className="active:bg-[#798FFF] rounded-lg px-5 py-[13.5px] desktop:text-lg font-semibold desktop:leading-[18px] mobile:text-base tablet:leading-4 text-nowrap text-clip bg-text-gradient">
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={
|
||||
'active:bg-[#798FFF] rounded-lg px-5 py-[13.5px] desktop:text-lg font-semibold desktop:leading-[18px] mobile:text-base tablet:leading-4 text-nowrap text-clip bg-text-gradient' +
|
||||
className
|
||||
}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
@@ -74,7 +99,7 @@ function TeachingItems() {
|
||||
<div className="bg-[#3D425C4D] rounded-xl desktop:bg-[url('src/assets/mask_group.png')] 2xl:bg-contain bg-right-bottom desktop:max-2xl:bg-[length:60%] desktop:p-10 tablet:max-desktop:p-7 mobile:max-tablet:p-5 bg-no-repeat">
|
||||
<div className="desktop:max-w-[455px]">
|
||||
<div className="tablet:max-desktop:border-b border-[#3D425C] pb-5 tablet:max-desktop:bg-[url('src/assets/mask_group.png')] bg-no-repeat bg-contain bg-right-bottom tablet:max-tablet-figma:bg-[length:40%]">
|
||||
<div className="tablet:max-desktop:max-w-[326px] mobile:max-tablet:border-b">
|
||||
<div className="tablet:max-desktop:max-w-[326px] mobile:max-tablet:border-b border-[#3D425C]">
|
||||
<h1 className="text-[#ffffff] desktop:text-[32px] tablet:text-2xl mobile:text-xl font-medium desktop:leading-[29px] tablet:leading-6 mobile:leading-6 tablet:mb-8">
|
||||
Промышленные тренажеры виртуальной реальности
|
||||
</h1>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { create } from 'zustand';
|
||||
import { devtools, persist } from 'zustand/middleware';
|
||||
|
||||
export type Lang = 'RU' | 'EN';
|
||||
|
||||
export const useLang = create<{
|
||||
value: Lang;
|
||||
updateLang: (lang: Lang) => void;
|
||||
}>()(
|
||||
devtools(
|
||||
persist(
|
||||
set => ({
|
||||
value: JSON.parse(localStorage.getItem('lang') ?? '{}').state ?? 'RU',
|
||||
updateLang: (lang: Lang) => {
|
||||
localStorage.setItem(
|
||||
'lang',
|
||||
JSON.stringify({ state: { value: lang } }),
|
||||
);
|
||||
set({ value: lang });
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'lang',
|
||||
partialize: state => ({ value: state.value }),
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -2,7 +2,7 @@ export function MiniTitle({ text }: { text: string }) {
|
||||
return (
|
||||
<h2
|
||||
className={
|
||||
'flex gap-1 items-start text-[#ffffff] uppercase opacity-80 font-medium desktop:text-sm mobile:text-xs'
|
||||
'flex gap-1 items-center self-start text-[#ffffff] uppercase opacity-80 font-medium desktop:text-sm mobile:text-xs'
|
||||
}
|
||||
>
|
||||
<img src="src/assets/cube.svg" alt="" />
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ export function NavLink({ text, route }: { text: string; route: string }) {
|
||||
return (
|
||||
<Link
|
||||
className={
|
||||
'border-l border-[#3D425C] mobile:hidden py-[30px] px-10 min-[1350px]:block'
|
||||
'text-[#ffffff] desktop:text-lg tablet:text-base desktop:leading-[18px] tablet:leading-4 font-semibold border-l border-[#3D425C] mobile:hidden py-[30px] px-10 min-[1350px]:block hover:bg-[#3D425C] active:bg-[#14161F]'
|
||||
}
|
||||
to={route}
|
||||
>
|
||||
|
||||
@@ -2093,6 +2093,11 @@ uri-js@^4.2.2:
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
use-sync-external-store@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
||||
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
||||
|
||||
util-deprecate@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
@@ -2158,3 +2163,10 @@ yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
zustand@^4.5.4:
|
||||
version "4.5.4"
|
||||
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.4.tgz#63abdd81edfb190bc61e0bbae045cc4d52158a05"
|
||||
integrity sha512-/BPMyLKJPtFEvVL0E9E9BTUM63MNyhPGlvxk1XjrfWTUlV+BR8jufjsovHzrtR6YNcBEcL7cMHovL1n9xHawEg==
|
||||
dependencies:
|
||||
use-sync-external-store "1.2.0"
|
||||
|
||||
Reference in New Issue
Block a user