diff --git a/package.json b/package.json index 39a6091..93fe901 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.23.1", + "react-swipeable": "^7.0.1", "zustand": "^4.5.4" }, "devDependencies": { diff --git a/src/components/Layout/Footer.tsx b/src/components/Layout/Footer.tsx index 34632fa..9d00ffd 100644 --- a/src/components/Layout/Footer.tsx +++ b/src/components/Layout/Footer.tsx @@ -9,10 +9,10 @@ export function Footer() {
-

+

Политика конфиденциальности graff.tech

-

+

© 2024 GRAFF interactive. Все права защищены

@@ -22,16 +22,16 @@ export function Footer() { info@graff.tech +7 800 770 00 67 -
+
RU
sam@graff.tech - +971 058 506 0097 + +971 58 506 0097
-
+
UAE
@@ -43,14 +43,5 @@ function Contact({ children, className = '', }: PropsWithChildren<{ className?: string }>) { - return ( -

- {children} -

- ); + return

{children}

; } diff --git a/src/components/Layout/Header.tsx b/src/components/Layout/Header.tsx index b2f0230..db885a4 100644 --- a/src/components/Layout/Header.tsx +++ b/src/components/Layout/Header.tsx @@ -4,7 +4,7 @@ export function Header() { return (
-

+

Создаем{' '} {' '} для промышленности и образования

-

+

Интерактивные тренажеры{' '}

-

+

Помогаем сократить затраты на обучение, повысить безопасность и производительность -

+
diff --git a/src/components/Layout/Navbar.tsx b/src/components/Layout/Navbar.tsx index 7c81e07..c64247a 100644 --- a/src/components/Layout/Navbar.tsx +++ b/src/components/Layout/Navbar.tsx @@ -23,7 +23,7 @@ export function Navbar() { - @@ -51,7 +51,7 @@ export function Navbar() {
- @@ -67,7 +67,7 @@ function BurgerLink({ text, route }: { text: string; route: string }) { return ( {text} @@ -95,7 +95,7 @@ function ChooseLang({
-

+

В одном цифровом пространстве{' '} могут работать сотрудники, находящиеся в разных помещениях, зданиях или городах -

+

@@ -52,9 +52,7 @@ function MultiUserFeature({ text, img }: { text: string; img: string }) { return (
-

- {text} -

+

{text}

); } diff --git a/src/components/Main/Contacts.tsx b/src/components/Main/Contacts.tsx index d3e1fc4..717e42e 100644 --- a/src/components/Main/Contacts.tsx +++ b/src/components/Main/Contacts.tsx @@ -18,30 +18,26 @@ export function Contacts() { Давайте обсудим детали. -
-

- Свяжитесь с нами -

+

Свяжитесь с нами

- -
-

- Социальные сети -

+

Социальные сети

@@ -55,9 +55,7 @@ function Figure({ }} >
-
- {title} -
+
{title}

{percents} diff --git a/src/components/Main/Events.tsx b/src/components/Main/Events.tsx index b5ade81..1a0b215 100644 --- a/src/components/Main/Events.tsx +++ b/src/components/Main/Events.tsx @@ -49,14 +49,7 @@ function EventTitle({ children, }: PropsWithChildren<{ className?: string }>) { return ( -

- {children} -

+

{children}

); } @@ -64,7 +57,7 @@ function LinkButton({ href }: { href: string }) { return ( как это было diff --git a/src/components/Main/Products.tsx b/src/components/Main/Products.tsx index 56f0ca6..97b2d34 100644 --- a/src/components/Main/Products.tsx +++ b/src/components/Main/Products.tsx @@ -1,11 +1,14 @@ -import { useEffect, useReducer, useState } from 'react'; +import { useEffect, useReducer, useRef, useState } from 'react'; import { MiniTitle } from '../../ui/MiniTitle'; import { Title } from '../../ui/Title'; import { useWindowWidth } from '../../hooks/useWindowWidth'; +import { useSwipeable } from 'react-swipeable'; export function Products() { const [curTab, setCurTab] = useState(0); + const ref = useRef(null); + return (
@@ -23,20 +26,41 @@ export function Products() { </span> -
+
setCurTab(0)} + onClick={e => { + ref.current?.scrollTo({ + left: e.currentTarget.offsetLeft - 16, + behavior: 'smooth', + }); + setCurTab(0); + }} text="Промышленные тренажеры" /> setCurTab(1)} + onClick={e => { + ref.current?.scrollTo({ + left: e.currentTarget.offsetLeft - 16, + behavior: 'smooth', + }); + setCurTab(1); + }} text="Симуляторы управления техникой" /> setCurTab(2)} + onClick={e => { + ref.current?.scrollTo({ + left: e.currentTarget.offsetLeft - 16, + behavior: 'smooth', + }); + setCurTab(2); + }} text="Тренажеры для учебных заведений" />
@@ -60,7 +84,7 @@ function TabButton({
); @@ -111,7 +133,7 @@ function TrainingsTab() {

Промышленные тренажеры виртуальной реальности

-

+

Может быть еще какой-нибудь небольшой текст, а то мне не хватает для балланса. Ну если не будет, то как-нибудь переживем

@@ -147,7 +169,6 @@ function TrainingsTab() { function SimulatorsTab() { const width = useWindowWidth(); const [slide, setSlide] = useState(0); - const [touchStart, setTouchStart] = useState(0); const [sliderOffset, setSliderOffset] = useState(-width + 80); const [order, dispatch] = useReducer( @@ -178,13 +199,24 @@ function SimulatorsTab() { setSliderOffset(-width + 80); }, [order, slide, width]); + const handlers = useSwipeable({ + onSwipedLeft: () => { + dispatch('next'); + setSlide(prev => (prev === order.length - 3 ? 0 : prev + 1)); + }, + onSwipedRight: () => { + dispatch('prev'); + setSlide(prev => (prev === 0 ? order.length - 3 : prev - 1)); + }, + }); + return ( -
+
-

+

Интерактивные симуляторы управления техникой -

+

    @@ -194,20 +226,20 @@ function SimulatorsTab() {
-

+

В основу симуляторов заложена математическая модель, полностью соответствующая работе настоящего оборудования -

-

+ +

модель позволяет производить расчеты характеристик работы, отслеживать безопасность работы устройств и симулировать внештатные ситуации.

-
+
setTouchStart(e.targetTouches[0].clientX)} - onTouchEnd={e => { - if (e.nativeEvent.changedTouches[0].clientX - touchStart > 100) { - dispatch('prev'); - setSlide(prev => (prev === 0 ? order.length - 3 : prev - 1)); - } else if ( - e.nativeEvent.changedTouches[0].clientX - touchStart < - -100 - ) { - dispatch('next'); - setSlide(prev => (prev === order.length - 3 ? 0 : prev + 1)); - } - }} + {...handlers} > {width < 640 ? ( order.map((src, index) => ( @@ -269,7 +289,7 @@ function SimulatorsTab() { )}
-

+

модель позволяет производить расчеты характеристик работы, отслеживать безопасность работы устройств и симулировать внештатные ситуации. @@ -282,7 +302,7 @@ function SimulatorsTab() { function SimulatorsItem({ text }: { text: string }) { return ( -

  • +
  • {text}
  • ); @@ -292,9 +312,9 @@ function ForTeachingTab() { return (
    -

    +

    Интерактивные тренажеры для учебных заведений -

    + -

    +

    cоздание обучающих VR систем -

    +
    -

    +

    Проведение виртуальных практических работ, создание учебных мастерских и стендов

    @@ -338,21 +358,21 @@ function ForTeachingTab() { className="desktop:hidden" alt="" /> -

    +

    cоздание VR лабораторий -

    +
    -

    +

    Тренажер для проведения лабораториных работ позволит избежать поломки оборудования, а также экономить на расходных средствах

    -

    +

    Оснащение учебных классов и центров всем необходимым для современного обучения под «ключ» -

    +

    ); } diff --git a/src/components/Main/Projects.tsx b/src/components/Main/Projects.tsx index 5fe50c4..f95366c 100644 --- a/src/components/Main/Projects.tsx +++ b/src/components/Main/Projects.tsx @@ -2,6 +2,7 @@ import { useEffect, useMemo, useReducer, useState } from 'react'; import { MiniTitle } from '../../ui/MiniTitle'; import { useWindowWidth } from '../../hooks/useWindowWidth'; import { Title } from '../../ui/Title'; +import { useSwipeable } from 'react-swipeable'; export function Projects() { return ( @@ -53,20 +54,18 @@ function Project({ tags: string[]; }) { return ( -
    +
    -

    - {title} -

    +

    {title}

    {tags.map(tag => (

    {tag}

    @@ -106,7 +105,16 @@ function Slider({ [projects[projects.length - 1], ...projects, projects[0]], ); - const [touchStart, setTouchStart] = useState(0); + const handlers = useSwipeable({ + onSwipedLeft: () => { + setSlide(prev => (prev === order.length - 3 ? 0 : prev + 1)); + dispatch('next'); + }, + onSwipedRight: () => { + setSlide(prev => (prev === 0 ? order.length - 3 : prev - 1)); + dispatch('prev'); + }, + }); useEffect(() => { setSliderOffset(-baseOffset); @@ -120,23 +128,7 @@ function Slider({ transition: `${sliderOffset === 0 || sliderOffset === -baseOffset * 2 ? 0 : 0.4}s`, transform: `translateX(${sliderOffset}px)`, }} - onTouchStart={e => { - setTouchStart(e.targetTouches[0].clientX); - }} - onTouchEnd={e => { - if (e.nativeEvent.changedTouches[0].clientX - touchStart > 100) { - dispatch('prev'); - setSlide(prev => (prev === 0 ? order.length - 3 : prev - 1)); - return; - } else if ( - e.nativeEvent.changedTouches[0].clientX - touchStart < - -100 - ) { - dispatch('next'); - setSlide(prev => (prev === order.length - 3 ? 0 : prev + 1)); - return; - } - }} + {...handlers} > {order.map((project, index) => ( diff --git a/src/components/Main/Teaching.tsx b/src/components/Main/Teaching.tsx index cb29063..cedd9dd 100644 --- a/src/components/Main/Teaching.tsx +++ b/src/components/Main/Teaching.tsx @@ -190,14 +190,14 @@ function TeachingFeatureTitle({ className?: string; }>) { return ( -

    {children} -

    + ); } @@ -207,30 +207,5 @@ function TeachingFeatureDescription({ }: PropsWithChildren<{ className?: string; }>) { - return ( -

    - {children} -

    - ); + return

    {children}

    ; } - -// function TeachingFeature({ -// className = '', -// children, -// }: PropsWithChildren<{ className?: string }>) { -// return ( -//
    -// {children} -//
    -// ); -// } diff --git a/src/components/Main/Trainings.tsx b/src/components/Main/Trainings.tsx index f940c70..ba7448b 100644 --- a/src/components/Main/Trainings.tsx +++ b/src/components/Main/Trainings.tsx @@ -55,29 +55,25 @@ function TrainingsFeature({ return (
    -

    +

    {title} -

    -

    - {text} -

    + +

    {text}

    -

    {order}

    +

    {order}

    -

    - {order} -

    +

    {order}

    -

    +

    {order}

    diff --git a/src/index.css b/src/index.css index d675208..b4ca326 100644 --- a/src/index.css +++ b/src/index.css @@ -7,3 +7,40 @@ body { font-family: 'TTHovesPro'; } + +@layer components { + .h1 { + @apply leading-[90%] desktop-figma:text-[clamp(96px,6vw,112px)] desktop:max-desktop-figma:text-[clamp(76px,76px+(100vw-1024px)/576*20,96px)] tablet-figma:max-desktop:text-[clamp(64px,64px+(100vw-768px)/256*16,80px)] tablet:max-tablet-figma:text-[clamp(56px,56px+(100vw-640px)/128*8,64px)] mobile:max-tablet:text-[clamp(40px,40px+(100vw-360px)/280*4,44px)]; + } + .h2 { + @apply desktop:leading-[90%] mobile:max-desktop:leading-[100%] desktop-figma:text-[clamp(64px,4vw,72px)] desktop:max-desktop-figma:text-[clamp(56px,56px+(100vw-1024px)/576*8,64px)] tablet-figma:max-desktop:text-[clamp(40px,40px+(100vw-768px)/256*12,52px)] tablet:max-tablet-figma:text-[clamp(32px,32px+(1000vw-640px)/128*8,40px)] mobile:max-tablet:text-[clamp(28px,28px+(100vw-360px)/280*4,32px)]; + } + + .h3 { + @apply leading-[100%] desktop-figma:text-[clamp(32px,2vw,40px)] desktop:max-desktop-figma:text-[clamp(28px,28px+(100vw-1024px)/576*4,32px)] tablet-figma:max-desktop:text-[clamp(24px,24px+(100vw-768px)/256*4,28px)] tablet:max-tablet-figma:text-[clamp(20px,20px+(100vw-640px)/128*4,24px)] mobile:max-tablet:text-[clamp(20px,20px+(100vw-360px)/280*4,24px)]; + } + + .h4 { + @apply leading-[120%] desktop-figma:text-[clamp(14px,0.875vw,16px)] desktop:max-desktop-figma:text-sm tablet-figma:max-desktop:text-[clamp(16px,16px+(100vw-768px)/256*2,18px)] tablet:max-tablet-figma:text-[clamp(14px,14px+(100vw-640px)/128*2,16px)] mobile:max-tablet:text-[clamp(14px,14px+(100vw-360px)/280*2,16px,18px)]; + } + + .l-text { + @apply leading-[135%] desktop-figma:text-[clamp(18px,1.125vw,20px)] desktop:max-desktop-figma:text-[clamp(16px,16px+(100vw-1024px)/576*2,18px)] tablet-figma:max-desktop:text-[clamp(16px,16px+(100vw-768px)/256*2,18px)] tablet:max-tablet-figma:text-[clamp(14px,14px+(100vw-640px)/128*2,16px)] mobile:max-tablet:text-[clamp(14px,14px+(100vw-360px)/280*2,16px)]; + } + + .m-text { + @apply leading-[140%] desktop-figma:text-[clamp(14px,0.875vw,16px)] desktop:max-desktop-figma:text-sm tablet-figma:max-desktop:text-[clamp(12px,12px+(100vw-768px)/576*2,14px)] tablet:max-tablet-figma:text-xs mobile:max-tablet:text-[clamp(12px,12px+(100vw-360px)/280*2,14px)]; + } + + .btn-text { + @apply leading-[100%] desktop-figma:text-[clamp(18px,1.125vw,20px)] desktop:max-desktop-figma:text-[clamp(16px,16px+(100vw-1024px)/576*2,18px)] tablet-figma:max-desktop:text-[clamp(16px,16px+(100vw-768px)/256*2,18px)] tablet:max-tablet-figma:text-[clamp(14px,14px+(100vw-640px)/128*2,16px)] mobile:max-tablet:text-[clamp(14px,14px+(100vw-360px)/280*2,16px)]; + } + + .link { + @apply leading-[120%] desktop-figma:text-[clamp(14px,0.875vw,16px)] desktop:max-desktop-figma:text-[clamp(12px,12px+(100vw-1024px)/576*2,14px)] tablet-figma:max-desktop:text-[clamp(12px,12px+(100vw-768px)/256*2,14px)] tablet:max-tablet-figma:text-xs mobile:max-tablet:text-[clamp(12px,12px+(100vw-360px)/280*2,14px)]; + } + + .descriptor { + @apply leading-[120%] desktop-figma:text-[clamp(14px,0.875vw,16px)] desktop:max-desktop-figma:text-[clamp(12px,12px+(100vw-1024px)/576*2,14px)] tablet-figma:max-desktop:text-[clamp(12px,12px+(100vw-640px)/256*2,14px)] tablet:max-tablet-figma:text-xs mobile:max-tablet:text-[clamp(12px,12px+(100vw-360px)/280*2,14px)]; + } +} diff --git a/src/ui/MiniTitle.tsx b/src/ui/MiniTitle.tsx index 3580542..28ad7e1 100644 --- a/src/ui/MiniTitle.tsx +++ b/src/ui/MiniTitle.tsx @@ -8,7 +8,7 @@ export function MiniTitle({ return (

    diff --git a/src/ui/NavLink.tsx b/src/ui/NavLink.tsx index daafdc5..e9169cc 100644 --- a/src/ui/NavLink.tsx +++ b/src/ui/NavLink.tsx @@ -4,7 +4,7 @@ export function NavLink({ text, route }: { text: string; route: string }) { return ( diff --git a/src/ui/Title.tsx b/src/ui/Title.tsx index f60448e..56cac41 100644 --- a/src/ui/Title.tsx +++ b/src/ui/Title.tsx @@ -5,13 +5,6 @@ export function Title({ className = '', }: PropsWithChildren<{ className?: string }>) { return ( -

    - {children} -

    +

    {children}

    ); } diff --git a/yarn.lock b/yarn.lock index a07d7e2..11cd9eb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1784,6 +1784,11 @@ react-router@6.23.1: dependencies: "@remix-run/router" "1.16.1" +react-swipeable@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-7.0.1.tgz#cd299f5986c5e4a7ee979839658c228f660e1e0c" + integrity sha512-RKB17JdQzvECfnVj9yDZsiYn3vH0eyva/ZbrCZXZR0qp66PBRhtg4F9yJcJTWYT5Adadi+x4NoG53BxKHwIYLQ== + react@^18.3.1: version "18.3.1" resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"