fixed slider, refactoring
This commit is contained in:
@@ -36,7 +36,6 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.7",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.3.2",
|
||||
"tailwind-scrollbar": "^3.1.0",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.3.1"
|
||||
|
||||
@@ -101,7 +101,7 @@ function BurgerAnchor({
|
||||
return (
|
||||
<HashLink
|
||||
to={route}
|
||||
className="flex items-center px-10 py-6 gap-1 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]"
|
||||
className="flex items-center px-10 py-6 gap-1 btn-text bg-[#14161F] w-full last:border-b-0 [&:not(:last-child)]:border-b sm:border-l font-semibold border-[#3D425C] lg:hover:bg-[#3D425C] active:bg-[#14161F]"
|
||||
>
|
||||
<CubeIcon />
|
||||
{children}
|
||||
@@ -115,7 +115,7 @@ function ChooseLang({ currentLang }: { currentLang: 'RU' | 'EN' }) {
|
||||
<button
|
||||
onClick={() => setLang(currentLang)}
|
||||
className={
|
||||
'min-h-[72px] w-full h-full btn-text font-semibold bg-[#14161F] hover:bg-[#3D425C] border active:bg-[#14161F] ' +
|
||||
'min-h-[72px] w-full h-full btn-text font-semibold bg-[#14161F] lg:hover:bg-[#3D425C] border active:bg-[#14161F] ' +
|
||||
(lang === currentLang
|
||||
? '[border-image:linear-gradient(to_right,#798FFF,#D375FF)_3]'
|
||||
: 'border-[#3D425C]')
|
||||
@@ -134,7 +134,7 @@ function LangToggler({ lang }: { lang: Lang }) {
|
||||
return (
|
||||
<div
|
||||
ref={langTogglerRef}
|
||||
className="min-w-[101px] max-[1349px]:hidden hover:bg-[#3D425C] active:bg-[#14161F]"
|
||||
className="min-w-[101px] max-[1349px]:hidden lg:hover:bg-[#3D425C] active:bg-[#14161F]"
|
||||
>
|
||||
<button
|
||||
onClick={() => setOpen(prev => !prev)}
|
||||
|
||||
@@ -35,14 +35,14 @@ export function Contacts() {
|
||||
<div className="space-y-2 2xl:pr-4 xl:pr-2">
|
||||
<a
|
||||
href="mailto:info@graff.tech"
|
||||
className="2xl:h-16 h-14 px-6 py-4 2xl:text-base text-sm border rounded-full font-medium flex justify-between items-center w-full border-[#52587A] opacity-80 hover:opacity-100 transition-all"
|
||||
className="2xl:h-16 h-14 px-6 py-4 2xl:text-base text-sm border rounded-full font-medium flex justify-between items-center w-full border-[#52587A] lg:opacity-80 lg:hover:opacity-100 transition-all"
|
||||
>
|
||||
<span>Написать</span>
|
||||
<MailIcon className="lg:w-8 lg:h-8 w-6 h-6" />
|
||||
</a>
|
||||
<a
|
||||
href="tel:88007700067"
|
||||
className="2xl:h-16 h-14 px-6 py-4 2xl:text-base text-sm border rounded-full font-medium flex justify-between items-center w-full border-[#52587A] opacity-80 hover:opacity-100 transition-all"
|
||||
className="2xl:h-16 h-14 px-6 py-4 2xl:text-base text-sm border rounded-full font-medium flex justify-between items-center w-full border-[#52587A] lg:opacity-80 lg:hover:opacity-100 transition-all"
|
||||
>
|
||||
<span>Позвонить</span>
|
||||
<PhoneIcon className="lg:w-8 lg:h-8 w-6 h-6" />
|
||||
@@ -60,21 +60,21 @@ export function Contacts() {
|
||||
<a
|
||||
href="https://www.youtube.com/@GRAFFtech"
|
||||
target="_blank"
|
||||
className="group border border-[#3D425C] xl:p-4 p-3 rounded-full hover:border-[#52587A] transition-all"
|
||||
className="group border border-[#3D425C] xl:p-4 p-3 rounded-full lg:hover:border-[#52587A] transition-all"
|
||||
>
|
||||
<YouTubeIcon className="2xl:w-8 2xl:h-8 w-6 h-6" />
|
||||
</a>
|
||||
<a
|
||||
href="https://vk.com/graff.interactive"
|
||||
target="_blank"
|
||||
className="group border border-[#3D425C] xl:p-4 p-3 rounded-full hover:border-[#52587A] transition-all"
|
||||
className="group border border-[#3D425C] xl:p-4 p-3 rounded-full lg:hover:border-[#52587A] transition-all"
|
||||
>
|
||||
<VKIcon className="2xl:w-8 2xl:h-8 w-6 h-6" />
|
||||
</a>
|
||||
<a
|
||||
href="https://t.me/GRAFFinteractive"
|
||||
target="_blank"
|
||||
className="border rounded-full border-[#52587A] xl:p-4 p-3 opacity-80 hover:opacity-100 transition-all"
|
||||
className="border rounded-full border-[#52587A] xl:p-4 p-3 opacity-80 lg:hover:opacity-100 transition-all"
|
||||
>
|
||||
<TelegramIcon className="2xl:w-8 2xl:h-8 w-6 h-6" />
|
||||
</a>
|
||||
|
||||
@@ -162,7 +162,7 @@ export default function ContactsForm({
|
||||
<a
|
||||
href="https://graff.tech/privacypolicy"
|
||||
target="_blank"
|
||||
className="text-[#798FFF] cursor-pointer opacity-95 hover:opacity-100 transition-all"
|
||||
className="text-[#798FFF] cursor-pointer opacity-95 lg:hover:opacity-100 transition-all"
|
||||
>
|
||||
условия использования
|
||||
</a>{' '}
|
||||
@@ -170,7 +170,7 @@ export default function ContactsForm({
|
||||
<a
|
||||
href="https://graff.tech/privacypolicy"
|
||||
target="_blank"
|
||||
className="text-[#798FFF] cursor-pointer opacity-95 hover:opacity-100 transition-all"
|
||||
className="text-[#798FFF] cursor-pointer opacity-95 lg:hover:opacity-100 transition-all"
|
||||
>
|
||||
политику конфиденциальности
|
||||
</a>
|
||||
|
||||
@@ -27,7 +27,7 @@ function ModalWithForm() {
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setModal(null)}
|
||||
className="p-2 hover:bg-white hover:bg-opacity-10 transition-colors rounded-full"
|
||||
className="p-2 lg:hover:bg-white lg:hover:bg-opacity-10 transition-colors rounded-full"
|
||||
>
|
||||
<CloseIcon className="text-white" />
|
||||
</button>
|
||||
|
||||
@@ -37,7 +37,7 @@ export function Products() {
|
||||
<MiniTitle className="lg:hidden" text="Продукты" />
|
||||
<div
|
||||
className={
|
||||
'flex gax-y-4 bg-[#3D425C4D] bg-opacity-3 scrollbar-none rounded-xl p-1 mb-2 w-fit max-w-full overflow-auto sm:max-lg:mt-[13px] mt-6' +
|
||||
'flex gax-y-4 bg-[#3D425C4D] bg-opacity-3 [&::-webkit-scrollbar]:hidden rounded-xl p-1 mb-2 w-fit max-w-full overflow-auto sm:max-lg:mt-[13px] mt-6' +
|
||||
(curTab !== 2
|
||||
? ' max-[912px]:[-webkit-mask-image:_linear-gradient(to_left,rgba(32,35,50,0)_0%,rgba(32,35,50,1)_20%)]'
|
||||
: '')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useReducer, useState } from 'react';
|
||||
import { useEffect, useReducer, useState } from 'react';
|
||||
import { MiniTitle } from '../../ui/MiniTitle';
|
||||
import { useWindowWidth } from '../../hooks/useWindowWidth';
|
||||
import { Title } from '../../ui/Title';
|
||||
@@ -10,7 +10,7 @@ export function Projects() {
|
||||
return (
|
||||
<div
|
||||
id="projects"
|
||||
className="lg:py-[70px] lg:px-10 py-14 sm:px-6 px-5 overflow-hidden select-none"
|
||||
className="lg:py-[70px] lg:px-10 py-14 sm:px-6 px-5 overflow-hidden"
|
||||
>
|
||||
<Title className="desktop-figma:mb-[77px] lg:mb-14 mb-6">
|
||||
<span className="text-gradient">Большой опыт в работе</span> с
|
||||
@@ -56,12 +56,12 @@ function Project({
|
||||
style={{ backgroundImage: `url(${src})` }}
|
||||
/>
|
||||
<div className="p-5">
|
||||
<h4 className=" font-medium h4">{title}</h4>
|
||||
<h4 className="font-medium h4">{title}</h4>
|
||||
<div className="flex gap-2 mt-4">
|
||||
{tags.map(tag => (
|
||||
<p
|
||||
key={tag}
|
||||
className=" opacity-80 font-medium rounded-3xl py-3 px-4 border border-[#798FFF] m-text"
|
||||
className="opacity-80 font-medium rounded-3xl py-3 px-4 border border-[#798FFF] m-text"
|
||||
>
|
||||
{tag}
|
||||
</p>
|
||||
@@ -78,36 +78,39 @@ function Slider({
|
||||
projects: { src: string; title: string; tags: string[] }[];
|
||||
}) {
|
||||
const width = useWindowWidth();
|
||||
const baseOffset = useMemo(
|
||||
() => (width >= 1024 ? 640 : width >= 640 ? 536 : 336),
|
||||
[width],
|
||||
);
|
||||
const baseOffset = width >= 1024 ? 640 : width >= 640 ? 536 : 336;
|
||||
const [sliderOffset, setSliderOffset] = useState(-baseOffset);
|
||||
const [slide, setSlide] = useState(0);
|
||||
|
||||
const [order, dispatch] = useReducer(
|
||||
(state: typeof projects, action: string) => {
|
||||
if (action === 'next') {
|
||||
setSliderOffset(prev => prev + baseOffset);
|
||||
return [...state.slice(1), state[2]];
|
||||
}
|
||||
if (action === 'prev') {
|
||||
setSliderOffset(-baseOffset * 2);
|
||||
return [state[state.length - 3], ...state.slice(0, -1)];
|
||||
setSliderOffset(prev => prev + baseOffset);
|
||||
return [...state.slice(1), state[state.length - 3]];
|
||||
}
|
||||
if (action === 'next') {
|
||||
setSliderOffset(prev => prev - baseOffset);
|
||||
return [state[state.length - 2], ...state.slice(0, -1)];
|
||||
}
|
||||
return state;
|
||||
},
|
||||
[projects[projects.length - 1], ...projects, projects[0]],
|
||||
[
|
||||
projects[projects.length - 2],
|
||||
projects[projects.length - 1],
|
||||
...projects,
|
||||
projects[0],
|
||||
projects[1],
|
||||
],
|
||||
);
|
||||
|
||||
const handlers = useSwipeable({
|
||||
onSwipedLeft: () => {
|
||||
setSlide(prev => (prev === order.length - 3 ? 0 : prev + 1));
|
||||
dispatch('next');
|
||||
setSlide(prev => (prev === order.length - 5 ? 0 : prev + 1));
|
||||
dispatch('prev');
|
||||
},
|
||||
onSwipedRight: () => {
|
||||
setSlide(prev => (prev === 0 ? order.length - 3 : prev - 1));
|
||||
dispatch('prev');
|
||||
setSlide(prev => (prev === 0 ? order.length - 5 : prev - 1));
|
||||
dispatch('next');
|
||||
},
|
||||
trackMouse: true,
|
||||
preventScrollOnSwipe: true,
|
||||
@@ -115,16 +118,18 @@ function Slider({
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setSliderOffset(-baseOffset);
|
||||
setSliderOffset(-baseOffset * 2);
|
||||
}, [order, baseOffset, slide]);
|
||||
|
||||
useEffect(() => console.log('slider offset:', sliderOffset), [sliderOffset]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col lg:mt-4 sm:mt-3 mt-2">
|
||||
<div {...handlers}>
|
||||
<div
|
||||
className="flex gap-2 overflow-visible relative mb-[18px] -mr-10 select-none"
|
||||
style={{
|
||||
transition: `${sliderOffset === 0 || sliderOffset === -baseOffset * 2 ? 0 : 0.5}s`,
|
||||
transition: `${sliderOffset === -baseOffset || sliderOffset === -baseOffset * 3 ? 0 : 0.5}s`,
|
||||
transform: `translateX(${sliderOffset}px)`,
|
||||
}}
|
||||
>
|
||||
@@ -136,7 +141,7 @@ function Slider({
|
||||
<div className="flex items-center gap-4 lg:w-[clamp(720px,100vw-465px,1135px)] desktop-figma:w-[70.9vw] w-full self-start lg:ml-64">
|
||||
<button
|
||||
onClick={() => {
|
||||
setSlide(prev => (prev === 0 ? order.length - 3 : prev - 1));
|
||||
setSlide(prev => (prev === 0 ? order.length - 5 : prev - 1));
|
||||
dispatch('prev');
|
||||
}}
|
||||
className="max-sm:hidden"
|
||||
@@ -151,7 +156,7 @@ function Slider({
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setSlide(prev => (prev === order.length - 3 ? 0 : prev + 1));
|
||||
setSlide(prev => (prev === order.length - 5 ? 0 : prev + 1));
|
||||
dispatch('next');
|
||||
}}
|
||||
className="max-sm:hidden"
|
||||
@@ -162,3 +167,27 @@ function Slider({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// next
|
||||
// 2|0 1 2|0
|
||||
// |0 1 2|0 1
|
||||
// 0|1 2 0|1
|
||||
|
||||
// prev
|
||||
// 2|0 1 2|0
|
||||
// 1 2|0 1 2|
|
||||
// 1|2 0 1|2
|
||||
|
||||
// next
|
||||
// 1 2|0 1 2|0 1
|
||||
// 2|0 1 2|0 1 2
|
||||
// 2 0|1 2 0|1 2
|
||||
// 0|1 2 0|1 2 0
|
||||
// 0 1|2 0 1|2 0
|
||||
|
||||
// prev
|
||||
// 1 2|0 1 2|0 1
|
||||
// 0 1 2|0 1 2|0
|
||||
// 0 1|2 0 1|2 0
|
||||
// 2 0 1|2 0 1|2
|
||||
// 2 0|1 2 0|1 2
|
||||
|
||||
+1
-3
@@ -1,5 +1,3 @@
|
||||
import tailwindScrollbar from 'tailwind-scrollbar';
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
||||
@@ -19,5 +17,5 @@ export default {
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [tailwindScrollbar({ nocompatible: true })],
|
||||
plugins: [],
|
||||
};
|
||||
|
||||
@@ -2095,11 +2095,6 @@ supports-preserve-symlinks-flag@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
tailwind-scrollbar@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tailwind-scrollbar/-/tailwind-scrollbar-3.1.0.tgz#ff7596407b6da5209261d8ff03860ab9206a59e3"
|
||||
integrity sha512-pmrtDIZeHyu2idTejfV59SbaJyvp1VRjYxAjZBH0jnyrPRo6HL1kD5Glz8VPagasqr6oAx6M05+Tuw429Z8jxg==
|
||||
|
||||
tailwindcss@^3.4.4:
|
||||
version "3.4.4"
|
||||
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.4.tgz#351d932273e6abfa75ce7d226b5bf3a6cb257c05"
|
||||
|
||||
Reference in New Issue
Block a user