Created slider components for both desktop and mobile. Created support types
This commit is contained in:
@@ -6,20 +6,14 @@ import {
|
||||
dubaiMarinaDescriptionBadges,
|
||||
sliderBadgesCategory,
|
||||
} from "../data/aboutDubaiMarina";
|
||||
import {
|
||||
AnimatePresence,
|
||||
motion,
|
||||
useInView,
|
||||
useScroll,
|
||||
useTransform,
|
||||
} from "motion/react";
|
||||
import { AnimatePresence, motion, useScroll, useTransform } from "motion/react";
|
||||
import useWindowSize from "../hooks/useWindowSize";
|
||||
import TextBox from "./ui/TextBox";
|
||||
import SliderMobile from "./SliderMobile";
|
||||
import clsx from "clsx";
|
||||
import Slider from "./Slider";
|
||||
import PlusIcon from "./icons/map/PlusIcon";
|
||||
import EqualIcon from "./icons/EqualIcon";
|
||||
import AboutSlider from "./AboutSlider/AboutSlider";
|
||||
// import FullScreenButton from "./FullScreenButton";
|
||||
|
||||
function AboutDubaiMarina() {
|
||||
@@ -37,22 +31,12 @@ function AboutDubaiMarina() {
|
||||
});
|
||||
|
||||
const firstSectionOpacity = useTransform(scrollYProgress, [0, 0.2], [1, 0]);
|
||||
|
||||
const secondSectionY = useTransform(
|
||||
scrollYProgress,
|
||||
[0, 0.4],
|
||||
["100dvh", "0dvh"]
|
||||
);
|
||||
|
||||
const isSliderInView = useInView(sliderRef, {
|
||||
once: true,
|
||||
amount: 0.1,
|
||||
});
|
||||
// const isMapInView = useInView(mapRef, {
|
||||
// once: true,
|
||||
// margin: `0px 0px ${-window.innerHeight / 2}px 0px`,
|
||||
// });
|
||||
|
||||
return (
|
||||
<div className="relative bg-white" ref={containerRef}>
|
||||
<motion.section
|
||||
@@ -153,6 +137,7 @@ function AboutDubaiMarina() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center pt-[4.444vw] px-8 gap-[4.444vw] max-md:px-[4.444vw] max-2xl:pr-[3.125vw] max-2xl:-mr-[3.125vw] max-2xl:p-6 max-2xl:items-start max-2xl:gap-[4.167vw]">
|
||||
<h1 className="font-mixcase-unmixed 2xl:text-[3.889vw] text-[#0D1922] tracking-[-0.05em] max-md:text-2xl max-2xl:text-[7.292vw] max-2xl:self-center">
|
||||
Dubai, <span className="text-[#0D192266]">within reach</span>
|
||||
@@ -199,19 +184,11 @@ function AboutDubaiMarina() {
|
||||
ref={sliderRef}
|
||||
className="flex flex-col gap-8 max-md:hidden"
|
||||
>
|
||||
<motion.div
|
||||
key={`slider-${selectedCategorySlider}`}
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
animate={
|
||||
isSliderInView
|
||||
? { opacity: 1, y: 0 }
|
||||
: { opacity: 0, y: 140 }
|
||||
}
|
||||
exit={{ opacity: 0, y: -40 }}
|
||||
transition={{ duration: 0.6, ease: "easeIn" }}
|
||||
>
|
||||
<Slider categoryName={selectedCategorySlider} />
|
||||
</motion.div>
|
||||
<div className="2xl:mx-auto 2xl:w-[63.333vw] 2xl:h-[27.639vw] md:w-[calc(100%-3.125vw)] md:h-[51.823vw] mb-[1vw] max-md:hidden">
|
||||
<AboutSlider
|
||||
items={dubaiMarinaSlider[selectedCategorySlider]}
|
||||
/>
|
||||
</div>
|
||||
<motion.div
|
||||
key={`badges-${selectedCategorySlider}`}
|
||||
className="flex gap-[0.556vw] w-[63.333vw] flex-wrap justify-center max-2xl:w-[93.75vw]"
|
||||
@@ -236,6 +213,7 @@ function AboutDubaiMarina() {
|
||||
</div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
<div className="text-center w-full flex flex-col items-center gap-[2.222vw] bg-white max-md:gap-[6.667vw] max-2xl:gap-[3.125vw]">
|
||||
<h1 className="font-mixcase-unmixed text-[3.889vw] text-[#0D1922] w-[44.861vw] leading-[100%] tracking-[-0.05em] pt-[8.056vw] max-md:text-[6.667vw] max-md:w-full max-2xl:text-[7.292vw] max-2xl:w-[84.115vw]">
|
||||
Dubai's first-ever combinable Apartments
|
||||
|
||||
+54
-74
@@ -1,43 +1,41 @@
|
||||
import { useRef, useState } from "react"
|
||||
import { AnimatePresence, motion, useInView, useScroll } from "motion/react"
|
||||
import AboutHQScrollSlider from "./AboutHQ/AboutHQSlider"
|
||||
import { marasiDriveMapCards } from "../data/aboutMarasiDrive"
|
||||
import { useRef, useState } from "react";
|
||||
import { useScroll } from "motion/react";
|
||||
import AboutHQScrollSlider from "./AboutHQ/AboutHQSlider";
|
||||
import { marasiDriveMapCards } from "../data/aboutMarasiDrive";
|
||||
|
||||
import clsx from "clsx"
|
||||
import EqualIcon from "./icons/EqualIcon"
|
||||
import PlusIcon from "./icons/map/PlusIcon"
|
||||
import MarasiDriveMapMobile from "./MarasiDriveMapMobile"
|
||||
import MarasiDriveMapCard from "./MarasiDriveMapCard"
|
||||
import BrochureButton from "./ui/BrochureButton"
|
||||
import AboutHQSliderTablet from "./AboutHQ/AboutHQSliderTablet"
|
||||
import AboutHQSliderMobile from "./AboutHQ/AboutHQSliderMobile"
|
||||
import { hqDescription, hqSlider } from "../data/aboutHQ"
|
||||
import UnitTypesSlider from "./AboutHQ/UnitTypesSlider"
|
||||
import UnitTypesSliderMobile from "./AboutHQ/UnitTypesSliderMobile"
|
||||
import clsx from "clsx";
|
||||
import EqualIcon from "./icons/EqualIcon";
|
||||
import PlusIcon from "./icons/map/PlusIcon";
|
||||
import MarasiDriveMapMobile from "./MarasiDriveMapMobile";
|
||||
import MarasiDriveMapCard from "./MarasiDriveMapCard";
|
||||
import BrochureButton from "./ui/BrochureButton";
|
||||
import AboutHQSliderTablet from "./AboutHQ/AboutHQSliderTablet";
|
||||
import AboutHQSliderMobile from "./AboutHQ/AboutHQSliderMobile";
|
||||
import { hqDescription, hqSlider } from "../data/aboutHQ";
|
||||
import AboutSlider from "./AboutSlider/AboutSlider";
|
||||
import AboutSliderMobile from "./AboutSlider/AboutSliderMobile";
|
||||
import { HQTabs } from "../types/HQ";
|
||||
|
||||
export default function AboutHQ() {
|
||||
const target = useRef<HTMLDivElement>(null);
|
||||
const { scrollYProgress } = useScroll({
|
||||
target,
|
||||
});
|
||||
const [selectedCategorySlider, setSelectedCategorySlider] =
|
||||
useState<keyof typeof hqSlider>("Modular Office Collection");
|
||||
|
||||
const sliderRef = useRef(null);
|
||||
const isSliderInView = useInView(sliderRef, {
|
||||
once: true,
|
||||
amount: 0.1,
|
||||
});
|
||||
const [selectedCategorySlider, setSelectedCategorySlider] = useState<HQTabs>(
|
||||
"Modular Office Collection"
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative bg-white">
|
||||
|
||||
{/* Hero */}
|
||||
<div className="2xl:sticky relative 2xl:top-[4.444vw] ">
|
||||
<section className="w-full 2xl:h-[calc(100dvh-4.444vw)] relative md:h-[calc(100dvh-4rem)] h-[calc(100dvh-3.15rem)] md:max-h-[125vw] max-md:max-h-[162.222vw] flex flex-col justify-between bg-white 2xl:p-[2.222vw] 2xl:pt-[5vw] md:max-2xl:p-6 md:max-2xl:pt-10 p-4 pt-8 overflow-hidden">
|
||||
|
||||
<div className="2xl:size-[100vw] md:size-[118.75vw] size-[137.5vw] rounded-full absolute 2xl:top-1/2 2xl:-translate-y-1/2 2xl:left-[36.667vw] md:top-[46.484vw] md:left-1/2 max-2xl:-translate-x-1/2 left-1/2 top-[48.611vw] -translate-y-0 bg-[#F3F3F2]" />
|
||||
<img src="/images/about-complex/hq/main_bg.png" alt="" className="absolute 2xl:h-[103%] 2xl:left-[46.528vw] md:bottom-0 md:right-[-35vw] md:h-[85%] max-md:h-auto max-md:bottom-0 max-md:left-0" />
|
||||
<img
|
||||
src="/images/about-complex/hq/main_bg.png"
|
||||
alt=""
|
||||
className="absolute 2xl:h-[103%] 2xl:left-[46.528vw] md:bottom-0 md:right-[-35vw] md:h-[85%] max-md:h-auto max-md:bottom-0 max-md:left-0"
|
||||
/>
|
||||
|
||||
<div className="2xl:space-y-[1.667vw] md:max-2xl:space-y-6 space-y-4 relative">
|
||||
<h1 className="2xl:text-[5vw] md:max-2xl:text-7xl text-[32px] leading-none tracking-[-0.07em] font-mixcase-unmixed font-medium whitespace-pre-line">
|
||||
@@ -48,20 +46,27 @@ export default function AboutHQ() {
|
||||
</div>
|
||||
|
||||
<div className="2xl:space-y-[1.111vw] md:max-2xl:space-y-6 relative max-md:hidden">
|
||||
<img src="/images/about-complex/hq/logo.svg" alt="" className=" md:max-2xl:flex hidden mb-[6.25vw]" />
|
||||
<img
|
||||
src="/images/about-complex/hq/logo.svg"
|
||||
alt=""
|
||||
className=" md:max-2xl:flex hidden mb-[6.25vw]"
|
||||
/>
|
||||
<h4 className="text-h4 text-[#00BED7] font-medium whitespace-pre-line">
|
||||
{`Welcome to the office you
|
||||
actually want to show up for`}
|
||||
</h4>
|
||||
<p className="text-s opacity-70 2xl:w-1/4 md:max-2xl:w-1/3">
|
||||
HQ by Rove was born out of a question: what if the
|
||||
office could feel alive again? Now, the first
|
||||
ever hospitality-branded office building in Dubai is here to
|
||||
answer it. Starting in Marasi Bay
|
||||
HQ by Rove was born out of a question: what if the office could
|
||||
feel alive again? Now, the first ever hospitality-branded office
|
||||
building in Dubai is here to answer it. Starting in Marasi Bay
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<img src="/images/about-complex/hq/logo.svg" alt="" className="absolute 2xl:top-[1.806vw] 2xl:right-[2.222vw] 2xl:w-[6.111vw] md:max-2xl:hidden w-[17.778vw] max-md:left-1/2 max-md:top-[39.444vw] max-md:-translate-x-1/2" />
|
||||
<img
|
||||
src="/images/about-complex/hq/logo.svg"
|
||||
alt=""
|
||||
className="absolute 2xl:top-[1.806vw] 2xl:right-[2.222vw] 2xl:w-[6.111vw] md:max-2xl:hidden w-[17.778vw] max-md:left-1/2 max-md:top-[39.444vw] max-md:-translate-x-1/2"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@@ -71,10 +76,9 @@ export default function AboutHQ() {
|
||||
actually want to show up for`}
|
||||
</h4>
|
||||
<p className="text-s opacity-70 2xl:w-1/4 md:max-2xl:w-1/3">
|
||||
HQ by Rove was born out of a question: what if the
|
||||
office could feel alive again? Now, the first
|
||||
ever hospitality-branded office building in Dubai is here to
|
||||
answer it. Starting in Marasi Bay
|
||||
HQ by Rove was born out of a question: what if the office could
|
||||
feel alive again? Now, the first ever hospitality-branded office
|
||||
building in Dubai is here to answer it. Starting in Marasi Bay
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
@@ -89,7 +93,8 @@ export default function AboutHQ() {
|
||||
<div className="flex flex-col 2xl:gap-[2.222vw] 2xl:sticky top-[5vw]">
|
||||
<div className="flex flex-col items-center 2xl:gap-[2.222vw]">
|
||||
<h2 className="font-mixcase-unmixed text-h1 text-center whitespace-pre-line max-md:mb-[24px] md:max-2xl:mb-8 opacity-100">
|
||||
<span className="opacity-40">More than an office,</span> <br className="2xl:hidden" /> a lifestyle.
|
||||
<span className="opacity-40">More than an office,</span>{" "}
|
||||
<br className="2xl:hidden" /> a lifestyle.
|
||||
</h2>
|
||||
<p className="text-s opacity-70 whitespace-pre-line text-center max-md:whitespace-normal max-md:mb-12 md:max-2xl:mb-16">
|
||||
{`Living rooms became boardrooms, kitchens became creative hubs.
|
||||
@@ -97,9 +102,7 @@ export default function AboutHQ() {
|
||||
answer - an office with a living touch.`}
|
||||
</p>
|
||||
</div>
|
||||
<AboutHQScrollSlider
|
||||
scrollYProgress={scrollYProgress}
|
||||
/>
|
||||
<AboutHQScrollSlider scrollYProgress={scrollYProgress} />
|
||||
<AboutHQSliderTablet />
|
||||
<AboutHQSliderMobile />
|
||||
</div>
|
||||
@@ -107,11 +110,13 @@ export default function AboutHQ() {
|
||||
</div>
|
||||
|
||||
{/* Features */}
|
||||
<div className="flex relative bg-white flex-col items-center pt-[4.444vw] px-8 gap-[4.444vw] max-md:px-[4.444vw] max-2xl:pr-[3.125vw] max-2xl:-mr-[3.125vw] max-2xl:p-6 max-2xl:items-start max-2xl:gap-[4.167vw]">
|
||||
<div className="flex relative bg-white flex-col items-center pt-[4.444vw] px-8 gap-[4.444vw] max-md:px-[4.444vw] max-2xl:items-start max-2xl:gap-[4.167vw]">
|
||||
<h1 className="font-mixcase-unmixed 2xl:text-[3.889vw] text-[#0D1922] tracking-[-0.05em] max-md:text-2xl max-2xl:text-[7.292vw] max-2xl:self-center">
|
||||
Dubai, <span className="text-[#0D192266]">within reach</span>
|
||||
</h1>
|
||||
<UnitTypesSliderMobile />
|
||||
|
||||
<AboutSliderMobile description={hqDescription} />
|
||||
|
||||
<div
|
||||
className="z-1 flex w-full text-[#0D1922]/40 gap-[1.111vw] max-md:hidden
|
||||
max-2xl:overflow-x-auto max-2xl:self-start max-2xl:snap-x max-2xl:snap-mandatory
|
||||
@@ -122,9 +127,7 @@ export default function AboutHQ() {
|
||||
key={descriptionItem.title}
|
||||
className="2xl:flex-1 text-center flex flex-col gap-[1.111vw] cursor-pointer hover:text-[#0D1922B2] transition-all duration-200
|
||||
max-2xl:w-[45.833vw] max-2xl:h-[12.76vw] max-2xl:shrink-0"
|
||||
onClick={() =>
|
||||
setSelectedCategorySlider(descriptionItem.title )
|
||||
}
|
||||
onClick={() => setSelectedCategorySlider(descriptionItem.title)}
|
||||
>
|
||||
<div className="h-[2px] bg-gray-300 w-full"></div>
|
||||
<h5
|
||||
@@ -148,35 +151,16 @@ export default function AboutHQ() {
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<AnimatePresence mode="wait">
|
||||
<div
|
||||
ref={sliderRef}
|
||||
className="flex flex-col gap-8 max-md:hidden"
|
||||
>
|
||||
<motion.div
|
||||
key={`slider-${selectedCategorySlider}`}
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
animate={
|
||||
isSliderInView
|
||||
? { opacity: 1, y: 0 }
|
||||
: { opacity: 0, y: 140 }
|
||||
}
|
||||
exit={{ opacity: 0, y: -40 }}
|
||||
transition={{ duration: 0.6, ease: "easeIn" }}
|
||||
>
|
||||
<UnitTypesSlider categoryName={selectedCategorySlider} />
|
||||
</motion.div>
|
||||
<div className="2xl:mx-auto 2xl:w-[63.333vw] 2xl:h-[27.639vw] md:w-[calc(100%-3.125vw)] md:h-[51.823vw] mb-[1vw] max-md:hidden">
|
||||
<AboutSlider items={hqSlider[selectedCategorySlider]} />
|
||||
</div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
{/* Apartments */}
|
||||
<div className="text-center relative w-full flex flex-col items-center gap-[2.222vw] bg-white max-md:gap-[6.667vw] max-2xl:gap-[3.125vw]">
|
||||
<h1 className="font-mixcase-unmixed text-[3.889vw] text-[#0D1922] w -[44.861vw] leading-[100%] tracking-[-0.05em] pt-[8.056vw] max-md:text-[6.667vw] max-md:w-full max-2xl:text-[7.292vw] max-2xl:w-[84.115vw]">
|
||||
{`Modular Office Collection:`}
|
||||
<p className="opacity-40">
|
||||
Combinable units
|
||||
</p>
|
||||
<p className="opacity-40">Combinable units</p>
|
||||
</h1>
|
||||
<p className="text-s text-[#0D1922B2] leading-[140%] tracking-[-0.02em] whitespace-pre-line">
|
||||
{`Enjoy the option to combine 2 offices and create a
|
||||
@@ -224,9 +208,7 @@ export default function AboutHQ() {
|
||||
</h4>
|
||||
</div>
|
||||
</div>
|
||||
<h5 className="2xl:hidden text-h5 text-[#0D1922B2] max-md:block">
|
||||
or
|
||||
</h5>
|
||||
<h5 className="2xl:hidden text-h5 text-[#0D1922B2] max-md:block">or</h5>
|
||||
<div className="flex gap-4 relative max-md:flex-col max-md:gap-[2.222vw] max-md:w-full max-md:px-[4.444vw]">
|
||||
<div
|
||||
className="w-[19.028vw] text-[#0D1922] h-[27.778vw] p-6 rounded-2xl bg-[#F3F3F2] text-left bg-[url(/images/about-complex/hq/combinable/Studio.png)] bg-[length:16.806vw_16.806vw] bg-no-repeat bg-center
|
||||
@@ -274,9 +256,7 @@ export default function AboutHQ() {
|
||||
{/* Map */}
|
||||
<section className="bg-white relative w-full overflow-clip flex flex-col gap-[3.333vw] 2xl:pt-[9.444vw] 2xl:px-[2.222vw] 2xl:pb-[2.222vw] md:max-2xl:pt-[104px] md:max-2xl:px-6 pt-16 px-4 pb-8 md:max-2xl:pb-6 md:max-2xl:gap-[6.25vw]">
|
||||
<div className="flex flex-col gap-[2.222vw] items-center max-md:gap-[8.889vw] md:max-2xl:gap-[4.167vw]">
|
||||
<h2 className="text-h1 font-mixcase-unmixed">
|
||||
Seamlessly connected
|
||||
</h2>
|
||||
<h2 className="text-h1 font-mixcase-unmixed">Seamlessly connected</h2>
|
||||
<p className="text-s opacity-70 whitespace-pre-line text-center max-md:mb-[48px] max-md:whitespace-normal">
|
||||
{`From the Dubai Canal at your doorstep to Sheikh Zayed Road in minutes, with
|
||||
Downtown, DIFC and d3 all close by, you’re never far from the city’s pulse.`}
|
||||
@@ -334,5 +314,5 @@ export default function AboutHQ() {
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import ArrowLeftIcon from "../icons/ArrowLeftIcon";
|
||||
import ArrowRightIcon from "../icons/ArrowRightIcon";
|
||||
import { hqSlider } from "../../data/aboutHQ";
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import Button from "../ui/Button";
|
||||
|
||||
function UnitTypesSlider({
|
||||
categoryName,
|
||||
}: {
|
||||
categoryName: "Modular Office Collection" | "A La Carte" | "Loft Office Collection";
|
||||
}) {
|
||||
const [currentSlide, setCurrentSlide] = useState(0);
|
||||
const [direction, setDirection] = useState(-1);
|
||||
|
||||
const handleNextSlide = () => {
|
||||
if (currentSlide < hqSlider[categoryName].length - 1) {
|
||||
setDirection(1);
|
||||
setCurrentSlide(currentSlide + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePreviousSlide = () => {
|
||||
if (currentSlide > 0) {
|
||||
setDirection(-1);
|
||||
setCurrentSlide(currentSlide - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handlers = useSwipeable({
|
||||
onSwipedLeft: handleNextSlide,
|
||||
onSwipedRight: handlePreviousSlide,
|
||||
preventScrollOnSwipe: true,
|
||||
touchEventOptions: {
|
||||
passive: false,
|
||||
},
|
||||
trackMouse: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentSlide(0);
|
||||
}, [categoryName]);
|
||||
|
||||
useEffect(() => {
|
||||
if(currentSlide === 0) {
|
||||
setDirection(1);
|
||||
}
|
||||
else if(currentSlide === hqSlider[categoryName].length - 1) {
|
||||
setDirection(-1);
|
||||
}
|
||||
}, [currentSlide]);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...handlers}
|
||||
className="relative w-[63.333vw] h-[27.639vw] rounded-3xl overflow-hidden max-md:w-[91.111vw] max-md:h-[110.556vw] max-2xl:w-[93.75vw] max-2xl:h-[51.823vw]"
|
||||
>
|
||||
<AnimatePresence custom={direction} initial={false}>
|
||||
<motion.div
|
||||
key={currentSlide}
|
||||
className="absolute inset-0 bg-cover bg-no-repeat bg-center before:absolute before:inset-0 before:bg-[#0D1922]/20 before:z-[1]"
|
||||
style={{
|
||||
backgroundImage: `url(${hqSlider[categoryName][currentSlide].image})`,
|
||||
}}
|
||||
custom={direction}
|
||||
initial={{ x: direction > 0 ? "100%" : "-100%" }}
|
||||
animate={{ x: 0, transition: { duration: 0.5, ease: "easeInOut" } }}
|
||||
exit={{
|
||||
x: direction < 0 ? "100%" : "-100%",
|
||||
transition: { duration: 0.8, ease: "easeInOut" },
|
||||
}}
|
||||
/>
|
||||
</AnimatePresence>
|
||||
<div className="z-[1] flex relative flex-col justify-between p-6 w-full h-full">
|
||||
<div className="flex flex-col gap-4">
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.h3
|
||||
key={`title-${currentSlide}`}
|
||||
className="text-h3 max-md:text-h5 text-white"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
{hqSlider[categoryName][currentSlide].title}
|
||||
</motion.h3>
|
||||
</AnimatePresence>
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.p
|
||||
key={`desc-${currentSlide}`}
|
||||
className="text-s w-[19.861vw] tracking-[-0.02em] leading-[140%] text-white max-md:w-full max-md:text-caption-m max-2xl:w-[37.24vw]"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
{hqSlider[categoryName][currentSlide].description}
|
||||
</motion.p>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
<div className="flex items-center gap-[0.556vw] relative max-md:gap-[2.222vw]">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onlyIcon
|
||||
onClick={handlePreviousSlide}
|
||||
disabled={currentSlide === 0}
|
||||
className="disabled:!bg-[#fff] disabled:!bg-opacity-80 disabled:!cursor-default hover:bg-[#F3F3F2] transition-all duration-200"
|
||||
>
|
||||
<span className="size-5">
|
||||
<ArrowLeftIcon />
|
||||
</span>
|
||||
</Button>
|
||||
<div className="text-s text-white">
|
||||
{currentSlide + 1}/{hqSlider[categoryName].length}
|
||||
</div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onlyIcon
|
||||
onClick={handleNextSlide}
|
||||
disabled={
|
||||
currentSlide === hqSlider[categoryName].length - 1
|
||||
}
|
||||
className="disabled:!bg-[#fff] disabled:!bg-opacity-80 disabled:!cursor-default hover:bg-[#F3F3F2] transition-all duration-200"
|
||||
>
|
||||
<span className="size-5">
|
||||
<ArrowRightIcon />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default UnitTypesSlider;
|
||||
@@ -1,87 +0,0 @@
|
||||
import { useScroll, useTransform, motion } from 'motion/react';
|
||||
import { useRef } from 'react'
|
||||
import UnitTypesSlider from './UnitTypesSlider';
|
||||
|
||||
export default function UnitTypesSliderMobile() {
|
||||
const sliderMobileRef = useRef(null);
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: sliderMobileRef,
|
||||
offset: ["start start", "end end"],
|
||||
});
|
||||
|
||||
const opacityFirstSlide = useTransform(
|
||||
scrollYProgress,
|
||||
[0, 1 / 3],
|
||||
[1, 0]
|
||||
);
|
||||
const opacitySecondSlide = useTransform(
|
||||
scrollYProgress,
|
||||
[ 2 / 3, 1],
|
||||
[ 1, 0]
|
||||
);
|
||||
const opacityThirdSlide = useTransform(
|
||||
scrollYProgress,
|
||||
[ 2 / 3, 1],
|
||||
[ 1, 1]
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
className="md:hidden flex flex-col gap-[6.667vw]"
|
||||
ref={sliderMobileRef}
|
||||
>
|
||||
<motion.div
|
||||
className="sticky top-[18vw] h-full flex flex-col gap-[6.667vw]"
|
||||
style={{ opacity: opacityFirstSlide }}
|
||||
>
|
||||
<div className="h-[2px] bg-gray-300 w-full"></div>
|
||||
<h5
|
||||
className={
|
||||
"text-h5 tracking-[-0.02em] mt-[0.556vw] font-[500] text-[#00BED7] text-center р-10"
|
||||
}
|
||||
>
|
||||
Modular Office Collection
|
||||
</h5>
|
||||
<p className="text-m text-center text-[#0D1922]/70 mt-[-2.222vw]">
|
||||
Spaces that easily expand and adapt
|
||||
</p>
|
||||
<UnitTypesSlider categoryName="Modular Office Collection" />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
className="sticky h-full top-[18vw] flex flex-col gap-[6.667vw] bg-white"
|
||||
style={{ opacity: opacitySecondSlide }}
|
||||
>
|
||||
<div className="h-[2px] bg-gray-300 w-full"></div>
|
||||
<h5
|
||||
className={
|
||||
"text-h5 tracking-[-0.02em] mt-[0.556vw] font-[500] text-[#00BED7] text-center"
|
||||
}
|
||||
>
|
||||
A La Carte
|
||||
</h5>
|
||||
<p className="text-m text-center text-[#0D1922]/70 mt-[-2.222vw]">
|
||||
Bespoke offices designed around you
|
||||
</p>
|
||||
<UnitTypesSlider categoryName="A La Carte" />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
className="sticky h-full top-[18vw] flex flex-col gap-[6.667vw] bg-white"
|
||||
style={{ opacity: opacityThirdSlide }}
|
||||
>
|
||||
<div className="h-[2px] bg-gray-300 w-full"></div>
|
||||
<h5
|
||||
className={
|
||||
"text-h5 tracking-[-0.02em] mt-[0.556vw] font-[500] text-[#00BED7] text-center"
|
||||
}
|
||||
>
|
||||
Loft Office Collection
|
||||
</h5>
|
||||
<p className="text-m text-center text-[#0D1922]/70 mt-[-2.222vw] h-10">
|
||||
Double-height ceilings and the warm atmosphere of home
|
||||
</p>
|
||||
<UnitTypesSlider categoryName="Loft Office Collection" />
|
||||
</motion.div>
|
||||
{/* <div className="h-[40vw]"></div> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
import { AboutSliderProps, ControlsProps } from "../../types/AboutSlider";
|
||||
import { ReactNode, useEffect, useRef, useState } from "react";
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
import ArrowRightIcon from "../icons/ArrowRightIcon";
|
||||
import ArrowLeftIcon from "../icons/ArrowLeftIcon";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import clsx from "clsx";
|
||||
|
||||
export default function AboutSlider({ items, className }: AboutSliderProps) {
|
||||
const totalSlides = items.length || 0;
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [currentSlide, setCurrentSlide] = useState(0);
|
||||
const [containerWidth, setContainerWidth] = useState(0);
|
||||
|
||||
function handleNext() {
|
||||
if (currentSlide === totalSlides - 1) return;
|
||||
setCurrentSlide((currentSlide + 1) % totalSlides);
|
||||
scroll(1);
|
||||
}
|
||||
|
||||
function handlePrevious() {
|
||||
if (currentSlide === 0) return;
|
||||
setCurrentSlide((currentSlide - 1 + totalSlides) % totalSlides);
|
||||
scroll(-1);
|
||||
}
|
||||
|
||||
function scroll(direction: 1 | -1) {
|
||||
if (!containerRef.current) return;
|
||||
containerRef.current.scrollLeft += direction * containerWidth;
|
||||
}
|
||||
|
||||
const swipeHandlers = useSwipeable({
|
||||
onSwipedLeft: handleNext,
|
||||
onSwipedRight: handlePrevious,
|
||||
preventScrollOnSwipe: true,
|
||||
touchEventOptions: {
|
||||
passive: false,
|
||||
},
|
||||
trackMouse: true,
|
||||
});
|
||||
|
||||
function refCallback(el: HTMLDivElement | null) {
|
||||
if (!el) return;
|
||||
containerRef.current = el;
|
||||
setContainerWidth(el.clientWidth || 0);
|
||||
|
||||
// Передаем элемент в ref от useSwipeable
|
||||
if (swipeHandlers.ref && typeof swipeHandlers.ref === "function") {
|
||||
swipeHandlers.ref(el);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentSlide(0);
|
||||
if (!containerRef.current) return;
|
||||
containerRef.current.scrollTo({ left: 0, behavior: "instant" });
|
||||
}, [items]);
|
||||
|
||||
return (
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key={items[0]?.title || "default"}
|
||||
initial={{ opacity: 0, y: "2%" }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: "-2%" }}
|
||||
transition={{ duration: 0.25 }}
|
||||
className="relative size-full overflow-hidden 2xl:rounded-[1.667vw] md:rounded-[3.125vw] rounded-[4.444vw]"
|
||||
>
|
||||
<motion.div
|
||||
key={currentSlide}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.25 }}
|
||||
className="absolute 2xl:top-[1.667vw] 2xl:left-[1.667vw] md:top-[3.125vw] md:left-[3.125vw] top-[4.444vw] left-[4.444vw] z-[3]"
|
||||
>
|
||||
<h2 className="text-h3 max-md:text-h5 text-white font-medium 2xl:mb-[1.111vw] md:mb-[2.083vw] mb-[4.444vw]">
|
||||
{items[currentSlide] && items[currentSlide].title}
|
||||
</h2>
|
||||
<p className="2xl:max-w-[19.861vw] md:max-w-[37.24vw] max-w-[95%] text-s max-md:text-caption-m text-white">
|
||||
{items[currentSlide] && items[currentSlide].description}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div
|
||||
ref={refCallback}
|
||||
className={clsx(
|
||||
"relative size-full overflow-hidden flex snap-x snap-mandatory scroll-smooth ",
|
||||
className
|
||||
)}
|
||||
onMouseDown={swipeHandlers.onMouseDown}
|
||||
>
|
||||
{items.map((item, index) => (
|
||||
<div key={index} className="w-full flex-shrink-0 snap-center">
|
||||
<img
|
||||
draggable={false}
|
||||
className="w-full h-full object-cover"
|
||||
src={item.image}
|
||||
alt={item.title}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="absolute 2xl:bottom-[1.667vw] 2xl:left-[1.667vw] md:bottom-[3.125vw] md:left-[3.125vw] bottom-[6.667vw] left-[6.667vw] z-[3]">
|
||||
<Controls
|
||||
handleNext={handleNext}
|
||||
handlePrevious={handlePrevious}
|
||||
currentSlide={currentSlide}
|
||||
totalSlides={totalSlides}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Затенение */}
|
||||
<div className="absolute pointer-events-none inset-0 bg-[radial-gradient(ellipse_at_top_left,rgba(0,0,0,0.3)_0%,rgba(0,0,0,0)_100%)] z-[2]" />
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
|
||||
function Controls({
|
||||
handleNext,
|
||||
handlePrevious,
|
||||
currentSlide,
|
||||
totalSlides,
|
||||
}: ControlsProps) {
|
||||
function ControlButton({
|
||||
children,
|
||||
onClick,
|
||||
disabled,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
onClick: () => void;
|
||||
disabled: boolean;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
className="2xl:size-[2.778vw] md:size-[5.208vw] size-[11.111vw] bg-white text-black 2xl:rounded-[0.833vw] md:rounded-[1.563vw] rounded-[3.333vw] flex items-center justify-center disabled:opacity-50"
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
<div className="2xl:size-[1.389vw] md:size-[2.604vw] size-[5.556vw]">
|
||||
{children}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center 2xl:gap-[0.556vw] md:gap-[1.042vw] gap-[2.222vw]">
|
||||
<ControlButton onClick={handlePrevious} disabled={currentSlide === 0}>
|
||||
<ArrowLeftIcon />
|
||||
</ControlButton>
|
||||
|
||||
<span className="text-s text-white">
|
||||
{currentSlide + 1} / {totalSlides}
|
||||
</span>
|
||||
|
||||
<ControlButton
|
||||
onClick={handleNext}
|
||||
disabled={currentSlide === totalSlides - 1}
|
||||
>
|
||||
<ArrowRightIcon />
|
||||
</ControlButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { motion } from "motion/react";
|
||||
import { useRef } from "react";
|
||||
import { hqSlider } from "../../data/aboutHQ";
|
||||
import AboutSlider from "./AboutSlider";
|
||||
import { AboutSliderMobileProps } from "../../types/AboutSlider";
|
||||
import { dubaiMarinaSlider } from "../../data/aboutDubaiMarina";
|
||||
|
||||
export default function AboutSliderMobile({
|
||||
description,
|
||||
}: AboutSliderMobileProps) {
|
||||
const sliderMobileRef = useRef(null);
|
||||
// const { scrollYProgress } = useScroll({
|
||||
// target: sliderMobileRef,
|
||||
// offset: ["start start", "end end"],
|
||||
// });
|
||||
const data =
|
||||
typeof description === typeof hqSlider ? hqSlider : dubaiMarinaSlider;
|
||||
|
||||
// const slidesCount = description.length;
|
||||
|
||||
// const transforms = Array.from({ length: slidesCount }, (_, index) =>
|
||||
// useTransform(
|
||||
// scrollYProgress,
|
||||
// [index / slidesCount, (index + 1) / slidesCount],
|
||||
// [1, 0]
|
||||
// )
|
||||
// );
|
||||
|
||||
return (
|
||||
<div
|
||||
className="md:hidden flex flex-col gap-[6.667vw]"
|
||||
ref={sliderMobileRef}
|
||||
>
|
||||
{description.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
className="sticky top-[18vw] h-full flex flex-col gap-[6.667vw] bg-white"
|
||||
>
|
||||
<div className="h-[2px] bg-gray-300 w-full"></div>
|
||||
<h5
|
||||
className={
|
||||
"text-h5 tracking-[-0.02em] mt-[0.556vw] font-[500] text-[#00BED7] text-center р-10"
|
||||
}
|
||||
>
|
||||
{item.title}
|
||||
</h5>
|
||||
<p className="text-m text-center text-[#0D1922]/70 mt-[-2.222vw]">
|
||||
{item.description}
|
||||
</p>
|
||||
|
||||
<div className="w-full h-[110.556vw]">
|
||||
<AboutSlider items={data[item.title as keyof typeof data]} />
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import ArrowLeftIcon from "./icons/ArrowLeftIcon";
|
||||
import ArrowRightIcon from "./icons/ArrowRightIcon";
|
||||
import { dubaiMarinaSlider } from "../data/aboutDubaiMarina";
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import Button from "./ui/Button";
|
||||
|
||||
function Slider({
|
||||
categoryName,
|
||||
}: {
|
||||
categoryName: "Wellness" | "Fitness" | "Community" | "Convenience";
|
||||
}) {
|
||||
const [currentSlide, setCurrentSlide] = useState(0);
|
||||
const [direction, setDirection] = useState(-1);
|
||||
|
||||
const handleNextSlide = () => {
|
||||
if (currentSlide < dubaiMarinaSlider[categoryName].length - 1) {
|
||||
setDirection(1);
|
||||
setCurrentSlide(currentSlide + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePreviousSlide = () => {
|
||||
if (currentSlide > 0) {
|
||||
setDirection(-1);
|
||||
setCurrentSlide(currentSlide - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handlers = useSwipeable({
|
||||
onSwipedLeft: handleNextSlide,
|
||||
onSwipedRight: handlePreviousSlide,
|
||||
preventScrollOnSwipe: true,
|
||||
touchEventOptions: {
|
||||
passive: false,
|
||||
},
|
||||
trackMouse: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentSlide(0);
|
||||
}, [categoryName]);
|
||||
|
||||
return (
|
||||
<div
|
||||
{...handlers}
|
||||
className="relative w-[63.333vw] h-[27.639vw] rounded-3xl overflow-hidden max-md:w-[91.111vw] max-md:h-[110.556vw] max-2xl:w-[93.75vw] max-2xl:h-[51.823vw]"
|
||||
>
|
||||
<AnimatePresence custom={direction} initial={false}>
|
||||
<motion.div
|
||||
key={currentSlide}
|
||||
className="absolute inset-0 bg-cover bg-no-repeat bg-center before:absolute before:inset-0 before:bg-[#0D1922]/20 before:z-[1]"
|
||||
style={{
|
||||
backgroundImage: `url(${dubaiMarinaSlider[categoryName][currentSlide].image})`,
|
||||
}}
|
||||
custom={direction}
|
||||
initial={{ x: direction > 0 ? "100%" : "-100%" }}
|
||||
animate={{ x: 0, transition: { duration: 0.5, ease: "easeInOut" } }}
|
||||
exit={{
|
||||
x: direction > 0 ? "100%" : "-100%",
|
||||
transition: { duration: 0.8, ease: "easeInOut" },
|
||||
}}
|
||||
/>
|
||||
</AnimatePresence>
|
||||
<div className="z-[1] flex relative flex-col justify-between p-6 w-full h-full">
|
||||
<div className="flex flex-col gap-4">
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.h3
|
||||
key={`title-${currentSlide}`}
|
||||
className="text-h3 max-md:text-h5 text-white"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
{dubaiMarinaSlider[categoryName][currentSlide].title}
|
||||
</motion.h3>
|
||||
</AnimatePresence>
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.p
|
||||
key={`desc-${currentSlide}`}
|
||||
className="text-s w-[19.861vw] tracking-[-0.02em] leading-[140%] text-white max-md:w-full max-md:text-caption-m max-2xl:w-[37.24vw]"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
{dubaiMarinaSlider[categoryName][currentSlide].description}
|
||||
</motion.p>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
<div className="flex items-center gap-[0.556vw] relative max-md:gap-[2.222vw]">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onlyIcon
|
||||
onClick={handlePreviousSlide}
|
||||
disabled={currentSlide === 0}
|
||||
className="disabled:!bg-[#fff] disabled:!bg-opacity-80 disabled:!cursor-default hover:bg-[#F3F3F2] transition-all duration-200"
|
||||
>
|
||||
<span className="size-5">
|
||||
<ArrowLeftIcon />
|
||||
</span>
|
||||
</Button>
|
||||
<div className="text-s text-white">
|
||||
{currentSlide + 1}/{dubaiMarinaSlider[categoryName].length}
|
||||
</div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onlyIcon
|
||||
onClick={handleNextSlide}
|
||||
disabled={
|
||||
currentSlide === dubaiMarinaSlider[categoryName].length - 1
|
||||
}
|
||||
className="disabled:!bg-[#fff] disabled:!bg-opacity-80 disabled:!cursor-default hover:bg-[#F3F3F2] transition-all duration-200"
|
||||
>
|
||||
<span className="size-5">
|
||||
<ArrowRightIcon />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Slider;
|
||||
@@ -1,6 +1,8 @@
|
||||
import { motion, useScroll, useTransform } from "framer-motion";
|
||||
import Slider from "./Slider";
|
||||
import { useRef } from "react";
|
||||
import AboutSlider from "./AboutSlider/AboutSlider";
|
||||
import { dubaiMarinaSlider } from "../data/aboutDubaiMarina";
|
||||
|
||||
function SliderMobile() {
|
||||
const sliderMobileRef = useRef(null);
|
||||
const { scrollYProgress } = useScroll({
|
||||
@@ -45,7 +47,9 @@ function SliderMobile() {
|
||||
<p className="text-m text-center text-[#0D1922]/70 mt-[-2.222vw]">
|
||||
Unlock your inner zen in our wellness playground
|
||||
</p>
|
||||
<Slider categoryName="Wellness" />
|
||||
<div className="w-full h-[110.556vw]">
|
||||
<AboutSlider items={dubaiMarinaSlider["Wellness"]} />
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
className="sticky h-full top-[18vw] flex flex-col gap-[6.667vw] bg-white"
|
||||
@@ -62,7 +66,9 @@ function SliderMobile() {
|
||||
<p className="text-m text-center text-[#0D1922]/70 mt-[-2.222vw]">
|
||||
Cancel all your membership. Your new home has it all
|
||||
</p>
|
||||
<Slider categoryName="Fitness" />
|
||||
<div className="w-full h-[110.556vw]">
|
||||
<AboutSlider items={dubaiMarinaSlider["Fitness"]} />
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
className="sticky h-full top-[18vw] flex flex-col gap-[6.667vw] bg-white"
|
||||
@@ -79,7 +85,9 @@ function SliderMobile() {
|
||||
<p className="text-m text-center text-[#0D1922]/70 mt-[-2.222vw] h-10">
|
||||
Connect. Engage. Thrive.
|
||||
</p>
|
||||
<Slider categoryName="Community" />
|
||||
<div className="w-full h-[110.556vw]">
|
||||
<AboutSlider items={dubaiMarinaSlider["Community"]} />
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
className="sticky h-full top-[18vw] flex flex-col gap-[6.667vw] z-1 bg-white"
|
||||
@@ -96,7 +104,10 @@ function SliderMobile() {
|
||||
<p className="text-m text-center text-[#0D1922]/70 mt-[-2.222vw] h-10">
|
||||
Your smart living hub
|
||||
</p>
|
||||
<Slider categoryName="Convenience" />
|
||||
|
||||
<div className="w-full h-[110.556vw]">
|
||||
<AboutSlider items={dubaiMarinaSlider["Convenience"]} />
|
||||
</div>
|
||||
</motion.div>
|
||||
<div className="h-[40vw]"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { DMDescription, DMSlider, DMBadges } from "../types/DubaiMarina";
|
||||
|
||||
export const dubaiMarinaDescriptionBadges = [
|
||||
"Fullyfurnished apartments",
|
||||
"Vibrant art installations",
|
||||
@@ -27,7 +29,7 @@ export const dubaiMarinaFeatures = [
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const dubaiMarinaDescription = [
|
||||
export const dubaiMarinaDescription: DMDescription = [
|
||||
{
|
||||
title: "Wellness",
|
||||
description: "Unlock your inner zen in our wellness playground",
|
||||
@@ -44,9 +46,9 @@ export const dubaiMarinaDescription = [
|
||||
title: "Convenience",
|
||||
description: "Your smart living hub",
|
||||
},
|
||||
] as const;
|
||||
];
|
||||
|
||||
export const dubaiMarinaSlider = {
|
||||
export const dubaiMarinaSlider: DMSlider = {
|
||||
Wellness: [
|
||||
{
|
||||
title: "Indoor Infinity Pool",
|
||||
@@ -102,9 +104,9 @@ export const dubaiMarinaSlider = {
|
||||
image: "/images/about-complex/dubai-marina/design.jpg",
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
};
|
||||
|
||||
export const sliderBadgesCategory = {
|
||||
export const sliderBadgesCategory: DMBadges = {
|
||||
Wellness: [
|
||||
"Vitality Pool",
|
||||
"Reflexology Pool",
|
||||
|
||||
+5
-3
@@ -1,3 +1,5 @@
|
||||
import { HQDescription, HQSlider } from "../types/HQ";
|
||||
|
||||
export const hqFeatures = [
|
||||
{
|
||||
name: "Reception Lounge",
|
||||
@@ -33,7 +35,7 @@ export const hqFeatures = [
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const hqDescription = [
|
||||
export const hqDescription: HQDescription = [
|
||||
{
|
||||
title: "Modular Office Collection",
|
||||
description: "Spaces that easily expand and adapt",
|
||||
@@ -46,9 +48,9 @@ export const hqDescription = [
|
||||
title: "Loft Office Collection",
|
||||
description: "Double-height ceilings and the warm atmosphere of home",
|
||||
},
|
||||
] as const;
|
||||
];
|
||||
|
||||
export const hqSlider = {
|
||||
export const hqSlider: HQSlider = {
|
||||
"Modular Office Collection": [
|
||||
{
|
||||
title: "Studio",
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { DMDescription } from "./DubaiMarina";
|
||||
import { HQDescription } from "./HQ";
|
||||
|
||||
export type AboutSliderItem = {
|
||||
title: string;
|
||||
description: string;
|
||||
image: string;
|
||||
};
|
||||
|
||||
export interface ControlsProps {
|
||||
handleNext: () => void;
|
||||
handlePrevious: () => void;
|
||||
currentSlide: number;
|
||||
totalSlides: number;
|
||||
}
|
||||
|
||||
export interface AboutSliderProps {
|
||||
items: AboutSliderItem[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export interface AboutSliderMobileProps {
|
||||
description: DMDescription | HQDescription;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { AboutSliderItem } from "./AboutSlider";
|
||||
|
||||
export type DMTabs = "Wellness" | "Fitness" | "Community" | "Convenience"
|
||||
export type DMDescription = {
|
||||
title: DMTabs,
|
||||
description: string
|
||||
}[]
|
||||
|
||||
export type DMSlider = Record<DMTabs, AboutSliderItem[]>
|
||||
export type DMBadges = Record<DMTabs, string[]>
|
||||
@@ -0,0 +1,9 @@
|
||||
import { AboutSliderItem } from "./AboutSlider";
|
||||
|
||||
export type HQTabs = "Modular Office Collection" | "A La Carte" | "Loft Office Collection"
|
||||
export type HQDescription = {
|
||||
title: HQTabs,
|
||||
description: string
|
||||
}[]
|
||||
|
||||
export type HQSlider = Record<HQTabs, AboutSliderItem[]>
|
||||
Reference in New Issue
Block a user