fixes,todo:product list animations,showreel modal,etc
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
"react-input-mask": "^2.0.4",
|
||||
"react-rangeslider": "^2.2.0",
|
||||
"react-swipeable": "^7.0.1",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"react-usestateref": "^1.0.9",
|
||||
"usehooks-ts": "^3.1.0",
|
||||
"zustand": "^4.5.4"
|
||||
@@ -29,6 +30,7 @@
|
||||
"@types/react-dom": "^18",
|
||||
"@types/react-input-mask": "^3.0.5",
|
||||
"@types/react-rangeslider": "^2.2.7",
|
||||
"@types/react-transition-group": "^4.4.11",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.5",
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<svg width="345" height="304" viewBox="0 0 345 304" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_f_3894_14794)">
|
||||
<ellipse cx="0.372531" cy="-3.79121e-05" rx="154.894" ry="104.153" transform="rotate(135 0.372531 -3.79121e-05)" fill="#5545AC"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_3894_14794" x="-343.626" y="-343.998" width="687.996" height="687.996" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="106" result="effect1_foregroundBlur_3894_14794"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 686 B |
Binary file not shown.
|
Before Width: | Height: | Size: 260 KiB After Width: | Height: | Size: 259 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.6 MiB |
Binary file not shown.
@@ -38,7 +38,7 @@ export function Header() {
|
||||
{width >= 1280 && <LogoWithTextIcon />}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex border-x border-[#3D425C] max-xl:hidden">
|
||||
<div className="flex border-x border-[#3D425C] max-xl:hidden relative">
|
||||
<ProductsList />
|
||||
<NavLink href="/about">О компании</NavLink>
|
||||
<NavLink href="/blog">Блог</NavLink>
|
||||
@@ -48,7 +48,7 @@ export function Header() {
|
||||
<div className="flex">
|
||||
<Button
|
||||
onClick={() => setModal(<ModalWithForm />)}
|
||||
className="rounded-none btn-text font-bold max-sm:hidden px-10"
|
||||
className="rounded-none btn-text font-bold max-sm:hidden px-10 outline-none"
|
||||
>
|
||||
Оставить заявку
|
||||
</Button>
|
||||
|
||||
@@ -7,10 +7,10 @@ export function ModalContainer() {
|
||||
|
||||
return (
|
||||
modal && (
|
||||
<div className="fixed top-0 left-0 z-50 w-full h-full flex justify-center items-center bg-black bg-opacity-40 transition-opacity">
|
||||
<div onClick={e => e.stopPropagation()} className="cursor-default">
|
||||
{modal}
|
||||
</div>
|
||||
<div className="fixed top-0 left-0 z-10 w-full h-full flex justify-center items-start bg-black bg-opacity-40 transition-opacity">
|
||||
{/* <div onClick={e => e.stopPropagation()} className="cursor-default"> */}
|
||||
{modal}
|
||||
{/* </div> */}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import Products from '@/consts/products.json';
|
||||
import { useModalStore } from '@/stores/useModalStore';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import Image from 'next/image';
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useOnClickOutside } from 'usehooks-ts';
|
||||
import { CloseIcon } from '../icons/CloseIcon';
|
||||
|
||||
interface IProduct {
|
||||
@@ -11,40 +13,66 @@ interface IProduct {
|
||||
image: string;
|
||||
}
|
||||
|
||||
export default function ModalWithProducts() {
|
||||
export function ModalWithProducts() {
|
||||
const { setModal } = useModalStore();
|
||||
const [show, setShow] = useState(true);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useOnClickOutside(ref, () => setShow(false));
|
||||
|
||||
useEffect(() => {
|
||||
const listener = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
setModal(false);
|
||||
}
|
||||
if (e.key === 'Escape') setShow(false);
|
||||
};
|
||||
document.addEventListener('keydown', listener);
|
||||
return () => document.removeEventListener('keydown', listener);
|
||||
}, [setModal]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="p-6 bg-[#14161F] z-100 aspect-[1600/600] max-h-[calc(100vw/1600*600)] relative top-0 space-y-6">
|
||||
<h4 className="h4 font-medium flex justify-between items-center">
|
||||
GRAFF.estate
|
||||
<button
|
||||
onClick={() => setModal(null)}
|
||||
className="hover:bg-[#3D425C] rounded-full p-2"
|
||||
<AnimatePresence onExitComplete={() => setModal(null)}>
|
||||
{show && (
|
||||
<motion.div
|
||||
ref={ref}
|
||||
key={'products'}
|
||||
initial={{
|
||||
opacity: 0,
|
||||
// scaleY: 0,
|
||||
// originY: 0,
|
||||
}}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
// scaleY: 1,
|
||||
// originY: 0,
|
||||
}}
|
||||
transition={{ duration: 0.75, type: 'spring' }}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
// scaleY: 0,
|
||||
// originY: 0,
|
||||
}}
|
||||
className="pt-10 p-6 bg-[#14161F] bg-opacity-90 -z-20 aspect-[1600/720] top-[76px] relative space-y-6 transition-all backdrop-blur-3xl"
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</h4>
|
||||
<div className="grid grid-col-3 grid-rows-2 mt-px ml-px">
|
||||
{Products.map((product, index) => (
|
||||
<ProductItem
|
||||
key={product.id}
|
||||
{...product}
|
||||
className={`col-start-${(index % 3) + 1} row-start-${index < 3 ? 1 : 2}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<h4 className="h4 font-medium flex justify-between items-center">
|
||||
GRAFF.estate
|
||||
<button
|
||||
onClick={() => setShow(false)}
|
||||
className="hover:bg-[#3D425C] rounded-full p-2"
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
</h4>
|
||||
<div className="grid grid-col-3 grid-rows-2 mt-px ml-px">
|
||||
{Products.map((product, index) => (
|
||||
<ProductItem
|
||||
key={product.id}
|
||||
{...product}
|
||||
className={`col-start-${(index % 3) + 1} row-start-${index < 3 ? 1 : 2}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,9 +83,13 @@ function ProductItem({
|
||||
className,
|
||||
}: Omit<IProduct, 'id'> & { className?: string }) {
|
||||
return (
|
||||
<div
|
||||
<motion.div
|
||||
whileHover={{
|
||||
backgroundSize: '100% 100%',
|
||||
}}
|
||||
transition={{ duration: 0.2, type: 'just', delay: 0.1 }}
|
||||
className={
|
||||
'border border-[#3D425C] -mt-px -ml-px p-6 flex col-span-1 row-span-1 ' +
|
||||
'border border-[#3D425C] -mt-px -ml-px p-6 flex col-span-1 row-span-1 aspect-[517/304] bg-[url(/img/components/products/highlight.svg)] bg-[length:0px_0px] bg-left-top bg-no-repeat ' +
|
||||
className
|
||||
}
|
||||
>
|
||||
@@ -76,9 +108,11 @@ function ProductItem({
|
||||
src={image}
|
||||
alt={title}
|
||||
fill
|
||||
className="object-contain object-bottom !relative"
|
||||
priority
|
||||
sizes="100% 100%"
|
||||
className="object-contain !relative object-bottom"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { useModalStore } from '@/stores/useModalStore';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ChevronDownIcon } from '../icons/ChevronDownIcon';
|
||||
import { ChevronUpIcon } from '../icons/ChevronUpIcon';
|
||||
import ModalWithProducts from './ModalWithProducts';
|
||||
import { ModalWithProducts } from './ModalWithProducts';
|
||||
|
||||
export function ProductsList() {
|
||||
const { setModal, modal } = useModalStore();
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setShow(!!modal);
|
||||
}, [modal]);
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => setModal(modal ? null : <ModalWithProducts />)}
|
||||
className="btn-text font-medium px-8 py-6 flex gap-x-2 items-center border-r border-[#3D425C] hover:bg-[#3D425C]"
|
||||
onClick={() => {
|
||||
setModal(modal ? null : <ModalWithProducts />);
|
||||
setShow(prev => !prev);
|
||||
}}
|
||||
className={
|
||||
'btn-text font-medium px-8 py-6 flex gap-x-2 items-center border-r border-[#3D425C] hover:bg-[#3D425C] outline-none' +
|
||||
(show ? ' relative z-[101]' : '')
|
||||
}
|
||||
>
|
||||
Продукты
|
||||
{modal ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { CloseIcon } from '@/components/icons/CloseIcon';
|
||||
import { useModalStore } from '@/stores/useModalStore';
|
||||
|
||||
interface VideoModalProps {
|
||||
link: string;
|
||||
}
|
||||
|
||||
export function VideoModal({ link }: VideoModalProps) {
|
||||
const { setModal } = useModalStore();
|
||||
|
||||
const handleOnCloseClick = () => {
|
||||
setModal(null);
|
||||
console.log('aboba');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-screen h-screen absolute top-0 left-0 overflow-hidden flex justify-center items-center">
|
||||
<div className="aspect-video w-full flex items-center justify-center">
|
||||
<button
|
||||
className="p-4 rounded-full border border-[#3D425C] absolute z-100 top-4 right-4 cursor-pointer"
|
||||
onClick={handleOnCloseClick}
|
||||
>
|
||||
<CloseIcon />
|
||||
</button>
|
||||
<video src={link} className="aspect-video h-full" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -8,8 +8,8 @@ export function ChevronUpIcon() {
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill-Rule="evenodd"
|
||||
clip-Rule="evenodd"
|
||||
d="M12.0001 6.58594L19.7072 14.293L18.293 15.7073L12.0001 9.41436L5.70718 15.7073L4.29297 14.293L12.0001 6.58594Z"
|
||||
fill="white"
|
||||
/>
|
||||
|
||||
@@ -26,7 +26,7 @@ export const AvailableItem = forwardRef<HTMLDivElement, IAvailable>(
|
||||
minHeight: '17.6vw',
|
||||
}
|
||||
}
|
||||
transition={{ duration: 1, type: 'just' }}
|
||||
transition={{ duration: 1, type: 'just', delay: 0.5 }}
|
||||
exit={{
|
||||
minWidth: '31.6vw',
|
||||
minHeight: '17.6vw',
|
||||
|
||||
@@ -9,38 +9,66 @@ export interface IIntegration {
|
||||
company: string;
|
||||
}
|
||||
|
||||
export const IntegrationItem = forwardRef<HTMLDivElement, IIntegration>(
|
||||
({ img, title, year, company }, ref) => {
|
||||
return (
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
key={title}
|
||||
ref={ref}
|
||||
initial={{ minWidth: '31.6vw', minHeight: '31.8vw' }}
|
||||
// exit={{ minWidth: '31.6vw', minHeight: '31.8vw' }}
|
||||
transition={{ duration: 0.5, type: 'just' }}
|
||||
animate={!!ref && { minWidth: '48vw', minHeight: '48vw' }}
|
||||
className="flex flex-col relative duration-500 pointer-events-none"
|
||||
>
|
||||
<Image
|
||||
src={img}
|
||||
alt={''}
|
||||
fill
|
||||
priority
|
||||
sizes="auto"
|
||||
className="!relative flex-1 object-cover"
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<div className={`flex justify-between mb-${!ref ? 1 : 2}`}>
|
||||
<h4 className="h4 font-medium">{title}</h4>
|
||||
<h4 className="h4 font-medium">{year}</h4>
|
||||
</div>
|
||||
<p className="m-caption font-medium text-[#737AA1]">{company}</p>
|
||||
export const IntegrationItem = forwardRef<
|
||||
HTMLDivElement,
|
||||
IIntegration & { index: number }
|
||||
>(({ img, title, year, company, index }, ref) => {
|
||||
// const transitionStyles: Map<TransitionStatus, CSSProperties> = new Map([
|
||||
// ['entering', { minWidth: '48vw', minHeight: '48vw' }],
|
||||
// ['entered', { minWidth: '48vw', minHeight: '48vw' }],
|
||||
// ['exiting', { minWidth: '31.6vw', minHeight: '31.8vw' }],
|
||||
// ['exited', { minWidth: '31.6vw', minHeight: '31.8vw' }],
|
||||
// ]);
|
||||
|
||||
return (
|
||||
// <Transition
|
||||
// nodeRef={ref}
|
||||
// timeout={500}
|
||||
// in={!!ref}
|
||||
// exit={!ref}
|
||||
// appear
|
||||
// onEnter={() => console.log('enter')}
|
||||
// onEntering={() => console.log('entering')}
|
||||
// onEntered={() => console.log('entered')}
|
||||
// onExit={() => console.log('exit')}
|
||||
// onExiting={() => console.log('exiting')}
|
||||
// onExited={() => console.log('exited')}
|
||||
// >
|
||||
// {state => (
|
||||
<AnimatePresence onExitComplete={() => console.log('exit')}>
|
||||
<motion.div
|
||||
ref={ref}
|
||||
key={title + (index < 1 ? '123' : index > 3 ? '456' : '')}
|
||||
initial={{ minWidth: '31.6vw', minHeight: '31.8vw' }}
|
||||
transition={{ duration: 0.5, type: 'just', delay: 0.5 }}
|
||||
animate={!!ref && { minWidth: '48vw', minHeight: '48vw' }}
|
||||
// exit={{ minWidth: '31.6vw', minHeight: '31.8vw' }}
|
||||
className={'flex flex-col relative transition-all pointer-events-none'}
|
||||
// style={{
|
||||
// transition: 'all 0.5s ease-out',
|
||||
// ...transitionStyles.get(state),
|
||||
// }}
|
||||
>
|
||||
<Image
|
||||
src={img}
|
||||
alt={''}
|
||||
fill
|
||||
priority
|
||||
sizes="auto"
|
||||
className="!relative flex-1 object-cover"
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<div className={`flex justify-between mb-${!ref ? 1 : 2}`}>
|
||||
<h4 className="h4 font-medium">{title}</h4>
|
||||
<h4 className="h4 font-medium">{year}</h4>
|
||||
</div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
);
|
||||
},
|
||||
);
|
||||
<p className="m-caption font-medium text-[#737AA1]">{company}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
// )}
|
||||
// </Transition>
|
||||
);
|
||||
});
|
||||
|
||||
IntegrationItem.displayName = 'IntegrationItem';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { SliderWithScaling } from '@/ui/SliderWithScaling';
|
||||
import 'react-circular-progressbar/dist/styles.css';
|
||||
import { IIntegration, IntegrationItem } from './IntegrationItem';
|
||||
|
||||
export function IntegrationsSlider({
|
||||
|
||||
@@ -1,77 +1,35 @@
|
||||
'use client';
|
||||
|
||||
import { LoadingIcon } from '@/components/icons/LoadingIcon';
|
||||
import { PauseIcon } from '@/components/icons/PauseIcon';
|
||||
import { PlayIcon } from '@/components/icons/PlayIcon';
|
||||
import { VideoModal } from '@/components/Layout/VideoModal';
|
||||
import { ClassNameWrapper } from '@/hocs/ClassNameWrapper';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useHover } from 'usehooks-ts';
|
||||
import { useModalStore } from '@/stores/useModalStore';
|
||||
|
||||
export function Showreel() {
|
||||
const [isPlaying, setIsPlaying] = useState(true);
|
||||
const [isBuffering, setIsBuffering] = useState(false);
|
||||
|
||||
function handleOnPlaying() {
|
||||
setIsBuffering(false);
|
||||
}
|
||||
|
||||
function handleOnWaiting() {
|
||||
setIsBuffering(true);
|
||||
}
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
|
||||
const hovered = useHover(ref);
|
||||
const { modal, setModal } = useModalStore();
|
||||
|
||||
return (
|
||||
<div className="lg:mb-[200px]">
|
||||
<div ref={ref} className="relative flex justify-center items-center">
|
||||
<video
|
||||
ref={videoRef}
|
||||
src="/videos/pages/home/showreel_1080p_4000k_h264.mp4"
|
||||
muted
|
||||
autoPlay
|
||||
loop
|
||||
className="w-full object-cover"
|
||||
onPlaying={handleOnPlaying}
|
||||
onWaiting={handleOnWaiting}
|
||||
/>
|
||||
{(hovered || !isPlaying) && (
|
||||
<button
|
||||
onClick={() => {
|
||||
if (!isPlaying) {
|
||||
videoRef.current?.play();
|
||||
setIsPlaying(true);
|
||||
} else {
|
||||
videoRef.current?.pause();
|
||||
setIsPlaying(false);
|
||||
}
|
||||
}}
|
||||
className="absolute z-10 mx-auto p-8 rounded-full border"
|
||||
>
|
||||
<ClassNameWrapper
|
||||
className="w-10 h-10"
|
||||
element={isPlaying ? <PauseIcon /> : <PlayIcon />}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={
|
||||
'absolute aspect-video w-full h-full flex justify-center items-center bg-black bg-opacity-50 transition-opacity ' +
|
||||
(isBuffering ? 'opacity-100' : 'opacity-0')
|
||||
<div className="w-full bg-cover bg-no-repeat bg-[url(/img/pages/home/showreel/preview.png)] aspect-[1551/616] relative flex justify-center items-center">
|
||||
<button
|
||||
className="absolute z-10 mx-auto p-8 rounded-full border"
|
||||
onClick={() =>
|
||||
setModal(<VideoModal link={'/videos/pages/home/showreel.mp4'} />)
|
||||
}
|
||||
>
|
||||
<div className="flex gap-4 items-center">
|
||||
<span>
|
||||
<ClassNameWrapper
|
||||
className="animate-spin w-5 h-5"
|
||||
element={<LoadingIcon />}
|
||||
/>
|
||||
</span>
|
||||
<span>Загружаем шоурил...</span>
|
||||
</div>
|
||||
<ClassNameWrapper className="w-10 h-10" element={<PlayIcon />} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="absolute aspect-video w-full h-full flex justify-center items-center bg-black bg-opacity-50 transition-opacity">
|
||||
<div className="flex gap-4 items-center">
|
||||
<span>
|
||||
<ClassNameWrapper
|
||||
className="animate-spin w-5 h-5"
|
||||
element={<LoadingIcon />}
|
||||
/>
|
||||
</span>
|
||||
<span>Загружаем шоурил...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Descriptor } from '@/ui/Descriptor';
|
||||
import { LineThrow } from '@/ui/LineThrow';
|
||||
import { Title } from '@/ui/Title';
|
||||
import { Manrope } from 'next/font/google';
|
||||
import Image from 'next/image';
|
||||
@@ -9,10 +8,13 @@ const manrope = Manrope({ subsets: ['latin'] });
|
||||
export function Statistics() {
|
||||
return (
|
||||
<section>
|
||||
<Title className="mb-20">
|
||||
<Title className="mb-20 leading-[-2em]">
|
||||
Продавайте недвижимость <br />
|
||||
<span className="text-gradient">проще и </span>
|
||||
<LineThrow>быстрее</LineThrow>
|
||||
<span className="relative">
|
||||
<span className="text-[#52587A]">быстрее</span>
|
||||
<span className="absolute top-[55%] -left-[2.5%] 2xl:h-1.5 h-1 w-[105%] bg-white" />
|
||||
</span>
|
||||
<span className="text-gradient"> дороже</span>
|
||||
</Title>
|
||||
<div className="grid grid-cols-4 border-t border-[#3D425C]">
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ArrowMoreIcon } from '@/components/icons/ArrowMoreIcon';
|
||||
import { PlusIcon } from '@/components/icons/PlusIcon';
|
||||
import { ClassNameWrapper } from '@/hocs/ClassNameWrapper';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
export function Technology() {
|
||||
@@ -33,13 +32,7 @@ export function Technology() {
|
||||
презентовать объект покупателю из любой точки мира
|
||||
</p>
|
||||
<div className="col-start-4 col-span-6 rounded-lg [box-shadow:1.04px_1.04px_4.18px_0px_rgba(45,79,0,0.3)] relative">
|
||||
<Image
|
||||
src={'/img/pages/home/technology/Content.png'}
|
||||
fill
|
||||
alt={''}
|
||||
sizes="100%"
|
||||
className="!relative rounded-lg border-[8.36px] border-transparent [background:linear-gradient(72.18deg,#575A6C_1.53%,#616476_98.43%)_border-box] [-webkit-mask:linear-gradient(#fff_0_0)_padding-box,linear-gradient(#fff_0_0)] [-webkit-mask-composite:xor]"
|
||||
/>
|
||||
<video src="/img/pages/home/technology/content.mp4" autoPlay loop />
|
||||
</div>
|
||||
<div className="flex justify-between items-start col-start-4 col-span-6 mt-10">
|
||||
<h4 className="h4 font-medium w-2/3">
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
"text": "«Эффективность инструмента была подтверждена буквально в первый день после его внедрения»",
|
||||
"author": "Егор Бобров, Коммерческий директор авторского квартала «Машаров»"
|
||||
},
|
||||
{
|
||||
"image": "/img/pages/home/reviews/2.jpg",
|
||||
"text": "«Клиенты особенно ценят возможность легко выбрать квартиру с помощью 3D-модель жилого комплекса»",
|
||||
"author": "Олег Бондорев, Ведущий менеджер компании «ЭНКО»"
|
||||
},
|
||||
{
|
||||
"image": "/img/pages/home/reviews/3.jpg",
|
||||
"text": "«Клиенты особенно ценят возможность легко выбрать квартиру с помощью 3D-модель жилого комплекса»",
|
||||
"author": "Алина Веселова, Ведущий специалист отдела продаж"
|
||||
},
|
||||
{
|
||||
"image": "/img/pages/home/reviews/2.jpg",
|
||||
"text": "«Клиенты особенно ценят возможность легко выбрать квартиру с помощью 3D-модель жилого комплекса»",
|
||||
"author": "Олег Бондорев, Ведущий менеджер компании «ЭНКО»"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
|
||||
export function LineThrow({
|
||||
className = '',
|
||||
children,
|
||||
}: PropsWithChildren<{ className?: string }>) {
|
||||
return (
|
||||
<span
|
||||
className={'line-through decoration-white text-[#52587A]' + className}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
export function NavLink({
|
||||
href,
|
||||
@@ -12,8 +11,6 @@ export function NavLink({
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
@@ -22,10 +19,6 @@ export function NavLink({
|
||||
className
|
||||
}
|
||||
>
|
||||
{/* <ClassNameWrapper
|
||||
element={<ActiveCubeIcon />}
|
||||
className={pathname.startsWith(href) ? '' : 'invisible'}
|
||||
/> */}
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -22,7 +22,9 @@ export function SliderWithScaling<T>({
|
||||
controlsPosition,
|
||||
}: {
|
||||
slides: T[];
|
||||
SlideElement: ForwardRefExoticComponent<T & RefAttributes<HTMLDivElement>>;
|
||||
SlideElement: ForwardRefExoticComponent<
|
||||
T & { index: number } & RefAttributes<HTMLDivElement>
|
||||
>;
|
||||
className?: string;
|
||||
alignItems?: 'start' | 'center' | 'end';
|
||||
title: string;
|
||||
@@ -32,7 +34,6 @@ export function SliderWithScaling<T>({
|
||||
const width = useWindowWidth();
|
||||
const baseoffset = (-width / 1600) * 507 + 8;
|
||||
|
||||
// const [itemRef, scale] = useAnimate<HTMLDivElement>();
|
||||
const itemRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [slide, setSlide] = useState(0);
|
||||
@@ -57,8 +58,6 @@ export function SliderWithScaling<T>({
|
||||
);
|
||||
|
||||
const nextSlide = () => {
|
||||
// itemRef.current!.style.minWidth = minWidth;
|
||||
// itemRef.current!.style.minHeight = minHeight;
|
||||
dispatch('next');
|
||||
};
|
||||
|
||||
@@ -66,7 +65,9 @@ export function SliderWithScaling<T>({
|
||||
setSliderOffset(baseoffset);
|
||||
}, [baseoffset, order, slide]);
|
||||
|
||||
const prevSlide = () => {};
|
||||
const prevSlide = () => {
|
||||
dispatch('prev');
|
||||
};
|
||||
|
||||
const handlers = useSwipeable({
|
||||
onSwipedLeft: nextSlide,
|
||||
@@ -84,20 +85,23 @@ export function SliderWithScaling<T>({
|
||||
<div className="overflow-hidden -mx-6">
|
||||
<div {...handlers}>
|
||||
<div
|
||||
// ref={sliderRef}
|
||||
className={`flex items-${alignItems} gap-x-4 -mr-6 relative select-none`}
|
||||
style={{
|
||||
transform: `translateX(${sliderOffset}px)`,
|
||||
transitionDuration: `${sliderOffset !== 0 && sliderOffset !== 2 * baseoffset ? 1 : 0}s`,
|
||||
// transitionDelay: `${sliderOffset !== 0 && sliderOffset !== 2 * baseoffset ? 1 : 0}s`,
|
||||
}}
|
||||
>
|
||||
{order.map((slide, index) => (
|
||||
<SlideElement
|
||||
ref={index === 1 ? itemRef : null}
|
||||
key={index}
|
||||
{...slide}
|
||||
/>
|
||||
))}
|
||||
{order.map((slide, index) => {
|
||||
return (
|
||||
<SlideElement
|
||||
ref={index === 1 ? itemRef : null}
|
||||
index={index}
|
||||
key={index}
|
||||
{...slide}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,12 +14,27 @@ const config: Config = {
|
||||
},
|
||||
animation: {
|
||||
'infinite-scroll': 'infinite-scroll 25s linear infinite',
|
||||
'highlight-product': 'highlight-product 0.1s ease-in 0s',
|
||||
},
|
||||
keyframes: {
|
||||
'infinite-scroll': {
|
||||
from: { transform: 'translateX(0)' },
|
||||
to: { transform: 'translateX(-100%)' },
|
||||
},
|
||||
'highlight-product': {
|
||||
'100%': {
|
||||
backgroundImage: 'url(/img/components/products/highlight.svg)',
|
||||
},
|
||||
},
|
||||
scaling: {
|
||||
'0%': {
|
||||
transform: 'min-width 31.6vw min-height 31.8vw',
|
||||
transition: 'transform 500ms',
|
||||
},
|
||||
'100%': {
|
||||
transform: 'min-width 48vw min-height 48vw',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30"
|
||||
integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
|
||||
|
||||
"@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7":
|
||||
version "7.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.0.tgz#3af9a91c1b739c569d5d80cc917280919c544ecb"
|
||||
integrity sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@eslint-community/eslint-utils@^4.2.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
|
||||
@@ -241,6 +248,13 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-transition-group@^4.4.11":
|
||||
version "4.4.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.11.tgz#d963253a611d757de01ebb241143b1017d5d63d5"
|
||||
integrity sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@^18":
|
||||
version "18.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.3.tgz#9679020895318b0915d7a3ab004d92d33375c45f"
|
||||
@@ -795,6 +809,14 @@ doctrine@^3.0.0:
|
||||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
dom-helpers@^5.0.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
|
||||
integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.8.7"
|
||||
csstype "^3.0.2"
|
||||
|
||||
eastasianwidth@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
|
||||
@@ -2230,7 +2252,7 @@ prettier@^3.3.3:
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105"
|
||||
integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
|
||||
|
||||
prop-types@^15.8.1:
|
||||
prop-types@^15.6.2, prop-types@^15.8.1:
|
||||
version "15.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||
@@ -2288,6 +2310,16 @@ react-swipeable@^7.0.1:
|
||||
resolved "https://registry.yarnpkg.com/react-swipeable/-/react-swipeable-7.0.1.tgz#cd299f5986c5e4a7ee979839658c228f660e1e0c"
|
||||
integrity sha512-RKB17JdQzvECfnVj9yDZsiYn3vH0eyva/ZbrCZXZR0qp66PBRhtg4F9yJcJTWYT5Adadi+x4NoG53BxKHwIYLQ==
|
||||
|
||||
react-transition-group@^4.4.5:
|
||||
version "4.4.5"
|
||||
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
|
||||
integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
dom-helpers "^5.0.1"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react-usestateref@^1.0.9:
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/react-usestateref/-/react-usestateref-1.0.9.tgz#d40bc54db116e786b6b2bb1cd20fe06e7f8187f3"
|
||||
@@ -2327,6 +2359,11 @@ reflect.getprototypeof@^1.0.4:
|
||||
globalthis "^1.0.3"
|
||||
which-builtin-type "^1.1.3"
|
||||
|
||||
regenerator-runtime@^0.14.0:
|
||||
version "0.14.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
|
||||
integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
|
||||
|
||||
regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334"
|
||||
|
||||
Reference in New Issue
Block a user