touch functionality
This commit is contained in:
@@ -3,6 +3,7 @@ import { ISwitchLabel } from "../../types/switchLabel";
|
||||
import SwitchToggle from "../SwitchToggle";
|
||||
import { IAparmentRes } from "../../types/apartmentsRes";
|
||||
import { apartmentRoutes } from "../../consts/apartmentsRoutes";
|
||||
import { apartmentLayoutImages } from "../../consts/apartmentLayoutsImages";
|
||||
|
||||
const apartmentLayouts: ISwitchLabel[] = [
|
||||
{ id: "1", label: "Layout" },
|
||||
@@ -14,6 +15,8 @@ interface ApartmentLayoutProps {
|
||||
}
|
||||
|
||||
const ApartmentLayout = ({ currentApartment }: ApartmentLayoutProps) => {
|
||||
const apartmentLayoutImage =
|
||||
apartmentLayoutImages[currentApartment.Unit_Type];
|
||||
const [currentLabel, setCurrentLabel] = useState(apartmentLayouts[0]);
|
||||
const handleOnSwitchClick = (label: ISwitchLabel) => {
|
||||
setCurrentLabel(label);
|
||||
@@ -27,7 +30,7 @@ const ApartmentLayout = ({ currentApartment }: ApartmentLayoutProps) => {
|
||||
<div className="w-full xl:px-[304px] sm:px-24 h-full flex justify-center items-center">
|
||||
<img
|
||||
className="w-full h-full object-contain max-h-[628px]"
|
||||
src={imageLayout ? imageLayout : "/images/layout-1.png"}
|
||||
src={imageLayout ? imageLayout : apartmentLayoutImage}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
@@ -37,6 +40,7 @@ const ApartmentLayout = ({ currentApartment }: ApartmentLayoutProps) => {
|
||||
className="absolute right-10 bottom-10"
|
||||
/>
|
||||
<SwitchToggle
|
||||
isDisabled
|
||||
labels={apartmentLayouts}
|
||||
currentLabel={currentLabel}
|
||||
onClick={handleOnSwitchClick}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useEffect, useState } from "react";
|
||||
import Button from "../Button";
|
||||
import HeartIcon from "../icons/Heart";
|
||||
import LeftArrowSliderIcon from "../icons/LeftArrowSliderIcon";
|
||||
import { IAparmentRes } from "../../types/apartmentsRes";
|
||||
import useFavorites from "../../store/useFavorites";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface ButtonPanelProps {
|
||||
currentApartment: IAparmentRes;
|
||||
@@ -17,7 +17,7 @@ const ButtonPanel = ({ currentApartment }: ButtonPanelProps) => {
|
||||
const { setFavorites } = useFavorites();
|
||||
|
||||
const handleOnBackClick = () => {
|
||||
navigate("../search");
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
const handleOnFavoriteClick = (
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
interface EastWingHighlightingProps {
|
||||
handleOnMouseOut:
|
||||
handleOnMouseOut?:
|
||||
| (() => void)
|
||||
| ((event: React.MouseEvent<SVGSVGElement, MouseEvent>) => void);
|
||||
handleOnMouseOver: (
|
||||
handleOnMouseOver?: (
|
||||
event: React.MouseEvent<SVGSVGElement, MouseEvent>
|
||||
) => void;
|
||||
handleOnApartmentClick: (
|
||||
handleOnApartmentClick?: (
|
||||
e: React.MouseEvent<SVGSVGElement, MouseEvent>
|
||||
) => void;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
interface FloorEastWingHighlightingProps {
|
||||
handleOnMouseOut:
|
||||
handleOnMouseOut?:
|
||||
| (() => void)
|
||||
| ((event: React.MouseEvent<SVGSVGElement, MouseEvent>) => void);
|
||||
handleOnMouseOver: (
|
||||
handleOnMouseOver?: (
|
||||
event: React.MouseEvent<SVGSVGElement, MouseEvent>
|
||||
) => void | (() => void);
|
||||
handleOnApartmentClick: (
|
||||
handleOnApartmentClick?: (
|
||||
event: React.MouseEvent<SVGSVGElement, MouseEvent>
|
||||
) => void;
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
interface WestWingTopLevelsHighlightingProps {
|
||||
handleOnMouseOut:
|
||||
handleOnMouseOut?:
|
||||
| (() => void)
|
||||
| ((event: React.MouseEvent<SVGSVGElement, MouseEvent>) => void);
|
||||
handleOnMouseOver:
|
||||
handleOnMouseOver?:
|
||||
| (() => void)
|
||||
| ((event: React.MouseEvent<SVGSVGElement, MouseEvent>) => void);
|
||||
handleOnApartmentClick: (
|
||||
handleOnApartmentClick?: (
|
||||
event: React.MouseEvent<SVGSVGElement, MouseEvent>
|
||||
) => void;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
import { apartmentsWithoutVirtualTour } from "../../consts/apartmentsWithoutVirtualTour";
|
||||
import { IAparmentRes } from "../../types/apartmentsRes";
|
||||
import Button from "../Button";
|
||||
import RightArrowSliderIcon from "../icons/RightArrowSliderIcon";
|
||||
import VirtualTourIcon from "../icons/VirtualTourIcon";
|
||||
import useModal from "../../store/useModal";
|
||||
import { MobileModalWrapper } from "../modals/mobile/MobileModalWrapper";
|
||||
import AboutComplexMobileModal from "../modals/mobile/AboutCompexMobileModal/AboutComplexMobileModal";
|
||||
import { IDesctiptionFloor } from "../../types/descriptionFloor";
|
||||
|
||||
interface MobileApartmentDescriptionProps {
|
||||
apartment: IAparmentRes | null;
|
||||
apartments: IAparmentRes[];
|
||||
apartmentDataType: string | null;
|
||||
floor: IDesctiptionFloor | null;
|
||||
}
|
||||
|
||||
const MobileApartmentDescription = ({
|
||||
apartment,
|
||||
apartmentDataType,
|
||||
apartments,
|
||||
floor,
|
||||
}: MobileApartmentDescriptionProps) => {
|
||||
const wing =
|
||||
apartment && apartment.Unit_No.split("-")[0] === "E"
|
||||
? "East Wing"
|
||||
: "West Wing";
|
||||
const isVirtualTourAvailiable =
|
||||
apartment &&
|
||||
!apartmentsWithoutVirtualTour.some(
|
||||
(aprt) =>
|
||||
aprt.type === apartment.Unit_Type || aprt.type === apartmentDataType
|
||||
);
|
||||
const { setModal } = useModal();
|
||||
|
||||
const handleOnExploreClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
event.preventDefault();
|
||||
if (!apartment) return;
|
||||
setModal(null);
|
||||
const timeout = setTimeout(() => {
|
||||
setModal(
|
||||
<MobileModalWrapper isHeightScreen>
|
||||
<AboutComplexMobileModal
|
||||
currentApartment={apartment}
|
||||
apartments={apartments}
|
||||
floor={floor}
|
||||
/>
|
||||
</MobileModalWrapper>
|
||||
);
|
||||
clearTimeout(timeout);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{apartment && (
|
||||
<div className="p-6 py-3">
|
||||
<div
|
||||
className={`bg-white rounded-2xl p-6 flex flex-col text-subheadline-s relative text-nowrap desc-shadow py-2 duration-300 ease-in-out transition-opacity min-w-[344px]`}
|
||||
>
|
||||
<h2 className="text-[#0D1922] font-semibold pt-6">
|
||||
{apartment?.Unit_Type},{" "}
|
||||
{apartment && Math.round(apartment?.Total_Area_Sqft)} Sqft
|
||||
</h2>
|
||||
<div className="flex gap-2 items-center text-caption-m font-semibold pt-1 border-b pb-4 justify-start">
|
||||
<p className="text-[#0D1922B2]">{wing}</p>
|
||||
<div className="w-1 h-1 bg-[#E2E2DC] rounded-full"></div>
|
||||
<p className="text-[#0D1922B2]">Floor {apartment.Floor}</p>
|
||||
<div className="w-1 h-1 bg-[#E2E2DC] rounded-full"></div>
|
||||
<p className="text-[#0D1922B2]">{apartment.Unit_No}</p>
|
||||
</div>
|
||||
<div className="flex justify-between items-center pt-4 gap-14 pb-4">
|
||||
<p className="font-semibold text-[#00BED7] text-subheadline-s">
|
||||
Unvailiable
|
||||
</p>
|
||||
{/* <p className="font-semibold text-[#00BED7] text-subheadline-s">
|
||||
AED 1,668,888
|
||||
</p> */}
|
||||
{isVirtualTourAvailiable && (
|
||||
<div
|
||||
className={`bg-[#30B21614] text-[#30B216] px-2 py-[6px] flex gap-1 items-center rounded-lg opacity-100`}
|
||||
>
|
||||
<VirtualTourIcon />
|
||||
<p className="text-caption-m font-semibold text-[#30B216]">
|
||||
3D-tour
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="w-0 h-0 border-transparent border-t-[14px] border-x-[6px] border-b-0 absolute left-6 -bottom-[13px] border-t-white"></div>
|
||||
<div className="w-full pb-6 ">
|
||||
<Button
|
||||
buttonType="cta"
|
||||
className="justify-center w-full"
|
||||
text="Explore"
|
||||
icon={<RightArrowSliderIcon />}
|
||||
iconPos="right"
|
||||
onClick={handleOnExploreClick}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileApartmentDescription;
|
||||
@@ -3,8 +3,8 @@ import { MouseEvent } from "react";
|
||||
import { IDesctiptionFloor } from "../../../types/descriptionFloor";
|
||||
|
||||
interface FloorsHighlightingProps {
|
||||
handleOnFloorClick: () => void;
|
||||
handleOnSkygardenClick: () => void;
|
||||
handleOnFloorClick: (e: MouseEvent | any) => void;
|
||||
handleOnSkygardenClick: (e: React.MouseEvent) => void;
|
||||
handleOnWingWrapperMouseEnter: (e: React.MouseEvent<SVGSVGElement>) => void;
|
||||
handleOnFloorMouseEnter:
|
||||
| ((
|
||||
|
||||
@@ -7,9 +7,7 @@ import RightArrowSliderIcon from "../../icons/RightArrowSliderIcon";
|
||||
interface MobileFloorDescriptionProps {
|
||||
descriptionFloor: IDesctiptionFloor | null;
|
||||
floorApartments: IAparmentRes[];
|
||||
onClick: (
|
||||
e: React.MouseEvent<Element, React.MouseEvent<Element, MouseEvent>>
|
||||
) => void;
|
||||
onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
}
|
||||
|
||||
const MobileFloorDescription = ({
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import Button from "../../Button";
|
||||
import CrossIcon from "../../icons/CrossIcon";
|
||||
import RightArrowSliderIcon from "../../icons/RightArrowSliderIcon";
|
||||
|
||||
interface MobileSkygardenDescriptionProps {
|
||||
onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
}
|
||||
|
||||
const MobileSkygardenDescription = ({
|
||||
onClick,
|
||||
}: MobileSkygardenDescriptionProps) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`bg-white rounded-2xl p-6 flex flex-col gap-4 w-[344px] absolute left-[414px] transition-opacity duration-300 desc-shadow h-[240px] pointer-events-auto`}
|
||||
>
|
||||
<div className="relative">
|
||||
<div className="flex justify-between border-b pb-4">
|
||||
<div className="flex flex-col">
|
||||
<p
|
||||
className={`text-[#0D1922] font-semibold text-[20px] duration-300 ease-in-out text-subheadline-s`}
|
||||
>
|
||||
Sky Garden
|
||||
</p>
|
||||
</div>
|
||||
<Button buttonType="tertiary" icon={<CrossIcon />} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 pt-4">
|
||||
<div className="flex items-center justify-between gap-8">
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="min-w-6 min-h-6 rounded-full bg-[#00BED7] text-white text-xs font-semibold">
|
||||
<p className="p-1 flex justify-center items-center">8</p>
|
||||
</div>
|
||||
<p className="text-s text-[#73787C] w-full">Indoor</p>
|
||||
</div>
|
||||
<p className="text-s text-[#0D1922] text-nowrap">3 amenties</p>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-8">
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="min-w-6 min-h-6 rounded-full bg-[#00BED7] text-white text-xs font-semibold">
|
||||
<p className="p-1 flex justify-center items-center">8</p>
|
||||
</div>
|
||||
<p className="text-s text-[#73787C] w-full">Studio</p>
|
||||
</div>
|
||||
<p className="text-s text-[#0D1922] text-nowrap">14 amenties</p>
|
||||
</div>
|
||||
<Button
|
||||
buttonType="cta"
|
||||
className="justify-center"
|
||||
text="Explore"
|
||||
icon={<RightArrowSliderIcon />}
|
||||
iconPos="right"
|
||||
onClick={onClick}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-0 h-0 border-t-0 border-r-[6px] border-b-[14px] border-l-[6px] border-transparent border-b-white absolute -top-[35px] left-[0px]"></div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileSkygardenDescription;
|
||||
@@ -1,10 +1,7 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { MouseEvent, useEffect, useState } from "react";
|
||||
import FloorDescription from "../FloorDescription";
|
||||
import { IDesctiptionFloor } from "../../../types/descriptionFloor";
|
||||
import SkygardenDescription from "../SkygardenDescription";
|
||||
import FloorSidebar from "../FloorSidebar/FloorSidebar";
|
||||
import SkygardenSidebar from "../SkygardenSidebar/SkygardenSidebar";
|
||||
import useWingSidebar from "../../../store/useWingSidebar";
|
||||
import useModal from "../../../store/useModal";
|
||||
import {
|
||||
@@ -26,14 +23,15 @@ import { isMobile } from "react-device-detect";
|
||||
import FloorsHighlighting from "./FloorsHighlighting";
|
||||
import MobileFloorDescription from "./MobileFloorDescription";
|
||||
import WingFloorModal from "../../modals/mobile/WingFloorModal";
|
||||
import MobileSkygardenDescription from "./MobileSkygardenDescription";
|
||||
import SkygardenModal from "../../modals/mobile/SkygardenModal";
|
||||
|
||||
const skyGardenFloor = 22;
|
||||
// const skyGardenFloor = 22;
|
||||
|
||||
const SequenceWing = () => {
|
||||
const [width, setWidth] = useState<number>(0);
|
||||
const [top, setTop] = useState<number>(0);
|
||||
const [left, setLeft] = useState<number>(0);
|
||||
// const leftWingRef = useRef(null);
|
||||
const { isSidebar, setIsSidebar } = useWingSidebar();
|
||||
const [mousePos, setMousePos] = useState<[number, number]>([0, 0]);
|
||||
const [currentHoveredFloor, setHoverCurrentFloor] =
|
||||
@@ -42,7 +40,7 @@ const SequenceWing = () => {
|
||||
const [currentFloor, setCurrentFloor] = useState<IDesctiptionFloor | null>(
|
||||
null
|
||||
);
|
||||
const [isWrapperHovered, setIsWrapperHovered] = useState(false);
|
||||
const [, setIsWrapperHovered] = useState(false);
|
||||
const [isSkygardenSidebar, setIsSkygardenSidebar] = useState(false);
|
||||
const [isFloorSidebar, setIsFloorSidebar] = useState(false);
|
||||
const { setModal } = useModal();
|
||||
@@ -50,12 +48,10 @@ const SequenceWing = () => {
|
||||
const [currentHoveredApartments, setCurrentHoveredApartments] = useState<
|
||||
IAparmentRes[]
|
||||
>([]);
|
||||
const [selectedApartments, setSelectedApartments] = useState<IAparmentRes[]>(
|
||||
[]
|
||||
);
|
||||
|
||||
const [isDescriptionFloorMobile, setIsDescriptionFloorMobile] =
|
||||
useState(false);
|
||||
const [isDescriptionSkygarden, setIsDescriptionSkygarden] = useState(false);
|
||||
|
||||
const {
|
||||
apartmentTypeCheckboxes,
|
||||
@@ -96,60 +92,17 @@ const SequenceWing = () => {
|
||||
setIsWrapperHovered(false);
|
||||
};
|
||||
|
||||
const handleOnFloorClick = (e: MouseEvent | any) => {
|
||||
const isFloorClicked = Boolean(e.currentTarget.dataset.id);
|
||||
if (isFloorClicked) {
|
||||
// detect mouse pos
|
||||
const screenWidth = window.innerWidth;
|
||||
const screenHeight = window.innerHeight;
|
||||
// >1072
|
||||
if (screenWidth > laptopWidth) {
|
||||
const _top = screenWidth / 2 - screenHeight / 2;
|
||||
setMousePos([e.clientX - 384, e.clientY + Math.abs(_top) - 20]);
|
||||
} else {
|
||||
// 640-1072
|
||||
if (screenWidth > mobileWidht) {
|
||||
const _top = screenHeight / 4;
|
||||
const _left = laptopWidth - screenWidth;
|
||||
setMousePos([
|
||||
e.clientX - 440 + _left,
|
||||
e.clientY + Math.abs(_top) + 20,
|
||||
]);
|
||||
// <640
|
||||
} else {
|
||||
const _top = screenHeight / 2.5;
|
||||
const _left = laptopWidth - screenWidth;
|
||||
setMousePos([
|
||||
e.clientX - 440 + _left,
|
||||
e.clientY + Math.abs(_top) + 20,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
//detect currentFloor, current sidebar and apartment on floor
|
||||
if (!currentHoveredFloor && !currentHoveredApartments) return;
|
||||
setSelectedApartments(currentHoveredApartments);
|
||||
setCurrentFloor(currentHoveredFloor);
|
||||
if (!isMobile) {
|
||||
setIsFloorSidebar(true);
|
||||
setIsSkygardenSidebar(false);
|
||||
setIsSidebar(true);
|
||||
} else {
|
||||
setIsDescriptionFloorMobile(true);
|
||||
}
|
||||
} else {
|
||||
setIsDescriptionFloorMobile(false);
|
||||
}
|
||||
const handleOnFloorClick = () => {
|
||||
setIsDescriptionSkygarden(false);
|
||||
//detect currentFloor
|
||||
if (!currentHoveredFloor && !currentHoveredApartments) return;
|
||||
setCurrentFloor(currentHoveredFloor);
|
||||
};
|
||||
|
||||
const handleOnSkygardenClick = () => {
|
||||
if (!isMobile) {
|
||||
setIsSkygardenSidebar(true);
|
||||
setIsFloorSidebar(false);
|
||||
setIsSidebar(true);
|
||||
} else {
|
||||
// setModal(<WingFloorModal />);
|
||||
}
|
||||
setCurrentFloor(null);
|
||||
setIsDescriptionFloorMobile(false);
|
||||
setIsDescriptionSkygarden(true);
|
||||
};
|
||||
|
||||
function handleMouseMove(
|
||||
@@ -195,7 +148,6 @@ const SequenceWing = () => {
|
||||
|
||||
//detect currentFloor, current sidebar and apartment on floor
|
||||
if (!currentHoveredFloor && !currentHoveredApartments) return;
|
||||
setSelectedApartments(currentHoveredApartments);
|
||||
setCurrentFloor(currentHoveredFloor);
|
||||
if (!isMobile) {
|
||||
setIsFloorSidebar(true);
|
||||
@@ -203,6 +155,7 @@ const SequenceWing = () => {
|
||||
setIsSidebar(true);
|
||||
} else {
|
||||
setIsDescriptionFloorMobile(true);
|
||||
setCurrentFloor(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,11 +186,25 @@ const SequenceWing = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleOnExploreClick = (
|
||||
e: MouseEvent<Element, MouseEvent<Element, MouseEvent>>
|
||||
const handleOnExploreFloorClick = (
|
||||
e: MouseEvent<Element, MouseEvent<Element, MouseEvent>> | any
|
||||
) => {
|
||||
e.stopPropagation();
|
||||
setModal(<WingFloorModal />);
|
||||
if (!currentHoveredFloor) return;
|
||||
setModal(
|
||||
<WingFloorModal
|
||||
currentFloor={currentHoveredFloor}
|
||||
floorApartments={apartments}
|
||||
/>
|
||||
);
|
||||
setIsDescriptionFloorMobile(false);
|
||||
};
|
||||
const handleOnExploreSkygardenClick = (
|
||||
e: MouseEvent<Element, MouseEvent<Element, MouseEvent>> | any
|
||||
) => {
|
||||
e.stopPropagation();
|
||||
setIsDescriptionSkygarden(false);
|
||||
setModal(<SkygardenModal />);
|
||||
setIsDescriptionFloorMobile(false);
|
||||
};
|
||||
|
||||
@@ -313,33 +280,16 @@ const SequenceWing = () => {
|
||||
sortList,
|
||||
]);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (isLoading) {
|
||||
// setModal(<LoaderModal />);
|
||||
// } else {
|
||||
// setModal(null);
|
||||
// }
|
||||
// }, [isLoading, setModal]);
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
setModal(<LoaderModal />);
|
||||
} else {
|
||||
setModal(null);
|
||||
}
|
||||
}, [isLoading, setModal]);
|
||||
|
||||
return (
|
||||
<div className="absolute left-0 overflow-hidden h-screen w-screen select-none ">
|
||||
<div
|
||||
className=" absolute h-[calc(100vh-56px)] right-0 w-1/2 duration-300 transition-all "
|
||||
style={{ right: `${isFloorSidebar ? "0" : "-50%"}` }}
|
||||
>
|
||||
<FloorSidebar
|
||||
floorApartments={selectedApartments}
|
||||
currentFloor={currentFloor}
|
||||
onMouseEnter={handleOnWingWrapperMouseLeave}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className=" absolute h-[calc(100vh-56px)] right-0 w-1/2 duration-300 transition-all"
|
||||
style={{ right: `${isSkygardenSidebar ? "0" : "-50%"}` }}
|
||||
>
|
||||
<SkygardenSidebar onMouseEnter={handleOnWingWrapperMouseLeave} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="absolute left-0 transition-[left]"
|
||||
style={{
|
||||
@@ -351,60 +301,29 @@ const SequenceWing = () => {
|
||||
}`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`absolute z-40 top-0 left-0 transition-opacity duration-300 ease-in-out lg:flex hidden ${
|
||||
currentHoveredFloor?.floor === skyGardenFloor && isWrapperHovered
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
}`}
|
||||
style={{
|
||||
top: mousePos[1] - 25,
|
||||
left: `${
|
||||
isSkygardenSidebar || isFloorSidebar
|
||||
? `calc(${mousePos[0]}px + 25%)`
|
||||
: `${mousePos[0]}px`
|
||||
}`,
|
||||
}}
|
||||
>
|
||||
<SkygardenDescription />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`absolute z-40 top-0 left-0 transition-opacity duration-300 ease-in-out lg:flex hidden ${
|
||||
currentHoveredFloor?.floor !== skyGardenFloor &&
|
||||
currentHoveredFloor &&
|
||||
isWrapperHovered
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
}`}
|
||||
style={{
|
||||
top: mousePos[1],
|
||||
left: `${
|
||||
isSkygardenSidebar || isFloorSidebar
|
||||
? `calc(${mousePos[0]}px + 25%)`
|
||||
: `${mousePos[0]}px`
|
||||
}`,
|
||||
}}
|
||||
>
|
||||
<FloorDescription
|
||||
descriptionFloor={currentHoveredFloor}
|
||||
floorApartments={currentHoveredApartments}
|
||||
/>
|
||||
</div>
|
||||
{/* */}
|
||||
<div
|
||||
className={`absolute z-40 top-0 left-0 transition-opacity duration-300 ease-in-out ${
|
||||
isDescriptionFloorMobile ? "opacity-100" : "opacity-0"
|
||||
isDescriptionSkygarden ? "" : "hidden"
|
||||
}`}
|
||||
style={{
|
||||
top: `${mousePos[1]}px`,
|
||||
left: `${mousePos[0]}px`,
|
||||
}}
|
||||
>
|
||||
<MobileSkygardenDescription onClick={handleOnExploreSkygardenClick} />
|
||||
</div>
|
||||
<div
|
||||
className={`absolute z-40 top-0 left-0 transition-opacity duration-300 ease-in-out ${
|
||||
isDescriptionFloorMobile ? "" : "hidden"
|
||||
}`}
|
||||
style={{
|
||||
top: `${mousePos[1]}px`,
|
||||
left: `${mousePos[0]}px`,
|
||||
// top: `${mousePos[1] + 140}px`,
|
||||
// left: `${mousePos[0] + 366}px`,
|
||||
}}
|
||||
>
|
||||
<MobileFloorDescription
|
||||
onClick={handleOnExploreClick}
|
||||
onClick={handleOnExploreFloorClick}
|
||||
descriptionFloor={currentHoveredFloor}
|
||||
floorApartments={currentHoveredApartments}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from "react";
|
||||
// import { useSwipeable } from "react-swipeable";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import SkygardenIndoorLayout from "./SkygardenIndoorLayout";
|
||||
import { ISwitchLabel } from "../../../types/switchLabel";
|
||||
import SwitchToggle from "../../SwitchToggle";
|
||||
@@ -14,13 +14,18 @@ const LayoutSlider = () => {
|
||||
const [currentLabel, setCurrentLabel] = useState<ISwitchLabel>(
|
||||
skygardenLayouts[0]
|
||||
);
|
||||
const handlers = useSwipeable({
|
||||
onSwipedLeft: () => setCurrentLabel(skygardenLayouts[1]),
|
||||
onSwipedRight: () => setCurrentLabel(skygardenLayouts[0]),
|
||||
trackMouse: true,
|
||||
});
|
||||
|
||||
const handleOnSwitchClick = (label: ISwitchLabel) => {
|
||||
setCurrentLabel(label);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="select-none flex flex-col items-center" {...handlers}>
|
||||
<SwitchToggle
|
||||
labels={skygardenLayouts}
|
||||
currentLabel={currentLabel}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { FullScreen, useFullScreenHandle } from "react-full-screen";
|
||||
import { useEffect } from "react";
|
||||
import useModal from "../../../store/useModal";
|
||||
import DesktopHeader from "./DesktopHeader";
|
||||
import MobileHeader from "./MobileHeader";
|
||||
import useFullScreen from "../../../store/useFullScreen";
|
||||
|
||||
@@ -14,12 +12,12 @@ const Layout = () => {
|
||||
|
||||
useEffect(() => {
|
||||
setOnFullscreen(onFullscreenHandle);
|
||||
}, []);
|
||||
}, [onFullscreenHandle, setOnFullscreen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FullScreen handle={onFullscreenHandle}>
|
||||
{isMobile ? <MobileHeader /> : <DesktopHeader />}
|
||||
<MobileHeader />
|
||||
{modal}
|
||||
<Outlet />
|
||||
</FullScreen>
|
||||
|
||||
@@ -18,7 +18,7 @@ const MobileHeader = () => {
|
||||
isToggled
|
||||
? "rounded-ee-lg rounded-es-lg shadow-[#00000026] shadow-md"
|
||||
: ""
|
||||
} text-white flex flex-col text-sm items-center transition-all duration-500 ease-in-out absolute top-0 left-0 z-[99999900] overflow-hidden font-usual ${
|
||||
} text-white flex flex-col text-sm items-center transition-all duration-500 ease-in-out absolute top-0 left-0 z-[99999999] overflow-hidden font-usual ${
|
||||
isToggled ? "max-h-[472px]" : "max-h-14"
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import { Tab } from "../../../types/tab";
|
||||
import NavbarDesktop from "./NavbarDesktop";
|
||||
import NavbarMobile from "./NavbarMobile";
|
||||
import { tabs as _tabs } from "../../../consts/tabs";
|
||||
import useFavorites from "../../../store/useFavorites";
|
||||
@@ -50,15 +48,13 @@ const Navbar = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{isMobile ? (
|
||||
<NavbarMobile onTabClick={onTabClick} tabs={tabs} />
|
||||
) : (
|
||||
<NavbarDesktop
|
||||
selectedTab={selectedTab}
|
||||
{
|
||||
<NavbarMobile
|
||||
onTabClick={onTabClick}
|
||||
tabs={tabs}
|
||||
selectedTab={selectedTab}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,14 +4,20 @@ import NavbarTabMobile from "./NavbarTabMobile";
|
||||
interface NavbarProps {
|
||||
onTabClick: (tab: Tab) => void;
|
||||
tabs: Tab[];
|
||||
selectedTab: Tab | null;
|
||||
}
|
||||
|
||||
const NavbarMobile = ({ onTabClick, tabs }: NavbarProps) => (
|
||||
const NavbarMobile = ({ onTabClick, tabs, selectedTab }: NavbarProps) => (
|
||||
<nav
|
||||
className={`text-[#73787C] flex w-full flex-col bg-white rounded-lg px-4`}
|
||||
>
|
||||
{tabs.map((tab) => (
|
||||
<NavbarTabMobile key={tab.id} tab={tab} onClick={onTabClick} />
|
||||
<NavbarTabMobile
|
||||
key={tab.id}
|
||||
tab={tab}
|
||||
onClick={onTabClick}
|
||||
isSelected={selectedTab?.id === tab.id}
|
||||
/>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
|
||||
@@ -4,15 +4,16 @@ import RightArrowIcon from "../../icons/RightArrowIcon";
|
||||
interface NavbarTabProps {
|
||||
tab: Tab;
|
||||
onClick: (tab: Tab) => void;
|
||||
isSelected: boolean;
|
||||
}
|
||||
|
||||
const NavbarTabMobile = ({ tab, onClick }: NavbarTabProps) => {
|
||||
return (
|
||||
<button className="w-full border-b last:border-b-0 flex justify-between py-4 ">
|
||||
<div
|
||||
className=" text-[#0D1922] relative w-fit"
|
||||
onClick={() => onClick(tab)}
|
||||
>
|
||||
<button
|
||||
className="w-full border-b last:border-b-0 flex justify-between py-4 "
|
||||
onClick={() => onClick(tab)}
|
||||
>
|
||||
<div className=" text-[#0D1922] relative w-fit">
|
||||
<div className={` transition-all duration-300`}>{tab.value}</div>
|
||||
{tab.count !== 0 && (
|
||||
<div className="absolute top-0 -right-4 w-4 h-4 bg-[#00BED7] rounded-full text-white text-[10px] flex items-center justify-center">
|
||||
|
||||
@@ -44,13 +44,6 @@ const AboutComplexModal = ({
|
||||
|
||||
const handleOn3DTourClick = () => {
|
||||
setModal(null);
|
||||
// const virtualTour = apartmentRoutes.find(
|
||||
// (route) => route.type === apartment.Unit_Type
|
||||
// );
|
||||
|
||||
// const virtualRoute = virtualTour
|
||||
// ? virtualTour.virtualTour
|
||||
// : "apartments-studio-1";
|
||||
|
||||
if (!apartment) return;
|
||||
navigate(`../virtual-tour/${apartment.id}`);
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { IAparmentRes } from "../../../../types/apartmentsRes";
|
||||
import ApartmentLayout from "../../../apartmentPage/ApartmentLayout";
|
||||
import ButtonPanel from "./ButtonPanel";
|
||||
import { IDesctiptionFloor } from "../../../../types/descriptionFloor";
|
||||
import ApartmentSidebar from "../../../ApartmentSidebar";
|
||||
|
||||
interface AboutComplexMobileModalProps {
|
||||
currentApartment: IAparmentRes;
|
||||
apartments: IAparmentRes[];
|
||||
floor: IDesctiptionFloor | null;
|
||||
}
|
||||
|
||||
const AboutComplexMobileModal = ({
|
||||
currentApartment,
|
||||
apartments,
|
||||
floor,
|
||||
}: AboutComplexMobileModalProps) => {
|
||||
return (
|
||||
<div className="absolute min-h-screen w-screen bg-[#F3F3F2] pt-20 px-6 pb-6">
|
||||
<div className="flex flex-col lg:col-span-7 col-span-full ">
|
||||
{currentApartment && (
|
||||
<ButtonPanel
|
||||
currentApartment={currentApartment}
|
||||
apartments={apartments}
|
||||
floor={floor}
|
||||
/>
|
||||
)}
|
||||
{currentApartment && (
|
||||
<ApartmentLayout currentApartment={currentApartment} />
|
||||
)}
|
||||
{currentApartment && (
|
||||
<div className="lg:col-span-2 rounded-lg col-span-full pt-6">
|
||||
<ApartmentSidebar currentApartment={currentApartment} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AboutComplexMobileModal;
|
||||
@@ -0,0 +1,106 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import useFavorites from "../../../../store/useFavorites";
|
||||
import { IAparmentRes } from "../../../../types/apartmentsRes";
|
||||
import Button from "../../../Button";
|
||||
import HeartIcon from "../../../icons/Heart";
|
||||
import LeftArrowSliderIcon from "../../../icons/LeftArrowSliderIcon";
|
||||
import useModal from "../../../../store/useModal";
|
||||
import WingFloorModal from "../WingFloorModal";
|
||||
import { IDesctiptionFloor } from "../../../../types/descriptionFloor";
|
||||
|
||||
interface ButtonPanelProps {
|
||||
currentApartment: IAparmentRes;
|
||||
apartments: IAparmentRes[];
|
||||
floor: IDesctiptionFloor | null;
|
||||
}
|
||||
|
||||
const ButtonPanel = ({
|
||||
currentApartment,
|
||||
apartments,
|
||||
floor,
|
||||
}: ButtonPanelProps) => {
|
||||
const unitNo = currentApartment.Unit_No.split("-")[1];
|
||||
const [isFavorite, setIsFavorite] = useState(false);
|
||||
const { setFavorites } = useFavorites();
|
||||
const { setModal } = useModal();
|
||||
|
||||
const handleOnBackClick = () => {
|
||||
setModal(null);
|
||||
const timeout = setTimeout(() => {
|
||||
setModal(
|
||||
<WingFloorModal currentFloor={floor} floorApartments={apartments} />
|
||||
);
|
||||
clearTimeout(timeout);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const handleOnFavoriteClick = (
|
||||
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
e.stopPropagation();
|
||||
const favorites = localStorage.getItem("Favorites");
|
||||
|
||||
if (!favorites) {
|
||||
setIsFavorite(true);
|
||||
const updatedFavorites = JSON.stringify([currentApartment]);
|
||||
|
||||
localStorage.setItem("Favorites", updatedFavorites);
|
||||
} else {
|
||||
const _favorites = JSON.parse(favorites) as IAparmentRes[];
|
||||
|
||||
if (_favorites.some((apart) => apart.id === currentApartment.id)) {
|
||||
setIsFavorite(false);
|
||||
const updatedFavorites = [..._favorites].filter(
|
||||
(apart) => apart.id !== currentApartment.id
|
||||
);
|
||||
const convertedFavorites = JSON.stringify(updatedFavorites);
|
||||
setFavorites(updatedFavorites);
|
||||
localStorage.setItem("Favorites", convertedFavorites);
|
||||
} else {
|
||||
setIsFavorite(true);
|
||||
const updatedFavorites = [..._favorites, currentApartment];
|
||||
setFavorites(updatedFavorites);
|
||||
const convertedFavorites = JSON.stringify(updatedFavorites);
|
||||
localStorage.setItem("Favorites", convertedFavorites);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const favorites = localStorage.getItem("Favorites");
|
||||
if (favorites) {
|
||||
const _isFavorite = (JSON.parse(favorites) as IAparmentRes[]).some(
|
||||
(apart) => apart.id === currentApartment.id
|
||||
);
|
||||
setIsFavorite(_isFavorite);
|
||||
}
|
||||
}, [currentApartment.id]);
|
||||
|
||||
return (
|
||||
<div className="flex justify-between w-full gap-4 items-center pb-3">
|
||||
<div className="flex gap-4 items-center ">
|
||||
<Button
|
||||
icon={<LeftArrowSliderIcon />}
|
||||
buttonType="cta"
|
||||
onClick={handleOnBackClick}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<p className="text-[#73787C] text-caption-m font-semibold">
|
||||
№ {unitNo}
|
||||
</p>
|
||||
<h2 className="text-subheadline-s font-semibold text-[#0D1922]">
|
||||
{currentApartment.Unit_Type}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
buttonType="favorite"
|
||||
icon={<HeartIcon isFilled={isFavorite} />}
|
||||
isCircleRounded
|
||||
onClick={handleOnFavoriteClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ButtonPanel;
|
||||
@@ -15,18 +15,27 @@ import {
|
||||
initialViewCheckboxes,
|
||||
} from "../../../consts/initialMasterplanFilters";
|
||||
import { ICheckbox } from "../../../types/checkbox";
|
||||
import useMasterplanFilters from "../../../store/useMasterplanFilters";
|
||||
|
||||
const MasterplanFiltersModal = () => {
|
||||
const { setModal } = useModal();
|
||||
const { setIsAnimate } = useContext(MobileModalWrapperContext);
|
||||
const [apartmentTypeCheckboxes, setApartmentTypeCheckboxes] = useState<
|
||||
ICheckbox[]
|
||||
>(initialAparmentTypeCheckboxes);
|
||||
const [viewCheckboxes, setViewCheckboxes] = useState<ICheckbox[]>(
|
||||
|
||||
const {
|
||||
setApartmentTypeCheckboxes,
|
||||
setViewCheckboxes,
|
||||
setMultirangeSliders,
|
||||
setSwitchers,
|
||||
} = useMasterplanFilters();
|
||||
|
||||
const [localApartmentTypeCheckboxes, setLocalApartmentTypeCheckboxes] =
|
||||
useState<ICheckbox[]>(initialAparmentTypeCheckboxes);
|
||||
const [localViewCheckboxes, setLocalViewCheckboxes] = useState<ICheckbox[]>(
|
||||
initialViewCheckboxes
|
||||
);
|
||||
const [switchers, setSwitchers] = useState(initialSwitchers);
|
||||
const [multirangeSliders, setMultirangeSliders] = useState(initialSliders);
|
||||
const [localSwitchers, setLocalSwitchers] = useState(initialSwitchers);
|
||||
const [localMultirangeSliders, setLocalMultirangeSliders] =
|
||||
useState(initialSliders);
|
||||
const handlers = useSwipeable({
|
||||
onSwipedDown: () => handleOnCrossClick(),
|
||||
});
|
||||
@@ -42,49 +51,49 @@ const MasterplanFiltersModal = () => {
|
||||
};
|
||||
|
||||
const handleOnCheckboxApartmentTypeClick = (checkboxId: string) => {
|
||||
const updatedCheckboxes = apartmentTypeCheckboxes.map((cbox) => {
|
||||
const updatedCheckboxes = localApartmentTypeCheckboxes.map((cbox) => {
|
||||
if (checkboxId !== cbox.id) return cbox;
|
||||
const isSelected = !cbox.selected;
|
||||
|
||||
return { ...cbox, selected: isSelected };
|
||||
});
|
||||
|
||||
setApartmentTypeCheckboxes(updatedCheckboxes);
|
||||
setLocalApartmentTypeCheckboxes(updatedCheckboxes);
|
||||
};
|
||||
|
||||
const handleOnSwitcherClick = (switcherId: string) => {
|
||||
const updatedSwitchers = switchers.map((switcher) => {
|
||||
const updatedSwitchers = localSwitchers.map((switcher) => {
|
||||
if (switcherId !== switcher.id) return switcher;
|
||||
const { isSwitched } = switcher;
|
||||
|
||||
return { ...switcher, isSwitched: !isSwitched };
|
||||
});
|
||||
|
||||
setSwitchers(updatedSwitchers);
|
||||
setLocalSwitchers(updatedSwitchers);
|
||||
};
|
||||
|
||||
const handleOnSliderValueChange = (
|
||||
sliderId: string,
|
||||
e: [a: number, b: number]
|
||||
) => {
|
||||
const updatedSliders = multirangeSliders.map((slider) => {
|
||||
const updatedSliders = localMultirangeSliders.map((slider) => {
|
||||
if (sliderId !== slider.id) return slider;
|
||||
|
||||
return { ...slider, startValue: e[0], endValue: e[1] };
|
||||
});
|
||||
|
||||
setMultirangeSliders(updatedSliders);
|
||||
setLocalMultirangeSliders(updatedSliders);
|
||||
};
|
||||
|
||||
const handleOnViewCheckboxClick = (checkboxId: string) => {
|
||||
const updatedCheckboxes = viewCheckboxes.map((cbox) => {
|
||||
const updatedCheckboxes = localViewCheckboxes.map((cbox) => {
|
||||
if (checkboxId !== cbox.id) return cbox;
|
||||
const isSelected = !cbox.selected;
|
||||
|
||||
return { ...cbox, selected: isSelected };
|
||||
});
|
||||
|
||||
setViewCheckboxes(updatedCheckboxes);
|
||||
setLocalViewCheckboxes(updatedCheckboxes);
|
||||
};
|
||||
|
||||
const handleOnShowApartmentClick = () => {
|
||||
@@ -92,16 +101,21 @@ const MasterplanFiltersModal = () => {
|
||||
setIsAnimate(false);
|
||||
const timeout = setTimeout(() => {
|
||||
setModal(null);
|
||||
setApartmentTypeCheckboxes(localApartmentTypeCheckboxes);
|
||||
setViewCheckboxes(localViewCheckboxes);
|
||||
setMultirangeSliders(localMultirangeSliders);
|
||||
setSwitchers(localSwitchers);
|
||||
|
||||
clearTimeout(timeout);
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnResetClick = () => {
|
||||
setViewCheckboxes(initialViewCheckboxes);
|
||||
setSwitchers(initialSwitchers);
|
||||
setMultirangeSliders(initialSliders);
|
||||
setApartmentTypeCheckboxes(initialAparmentTypeCheckboxes);
|
||||
setLocalViewCheckboxes(initialViewCheckboxes);
|
||||
setLocalSwitchers(initialSwitchers);
|
||||
setLocalMultirangeSliders(initialSliders);
|
||||
setLocalApartmentTypeCheckboxes(initialAparmentTypeCheckboxes);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -124,7 +138,7 @@ const MasterplanFiltersModal = () => {
|
||||
<div className="flex flex-col w-full">
|
||||
<p className="text-[#0D1922] text-s pb-4">Apartment type</p>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{apartmentTypeCheckboxes.map((checkbox) => (
|
||||
{localApartmentTypeCheckboxes.map((checkbox) => (
|
||||
<Checkbox
|
||||
checkbox={checkbox}
|
||||
key={checkbox.id}
|
||||
@@ -134,7 +148,7 @@ const MasterplanFiltersModal = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-6 border-b pb-12">
|
||||
{multirangeSliders.map((slider) => (
|
||||
{localMultirangeSliders.map((slider) => (
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[#0D1922] text-s ">{slider.title}</p>
|
||||
@@ -147,7 +161,7 @@ const MasterplanFiltersModal = () => {
|
||||
</div>
|
||||
))}
|
||||
<div className="flex flex-col gap-2 pt-6">
|
||||
{switchers.map((switcher) => (
|
||||
{localSwitchers.map((switcher) => (
|
||||
<div key={switcher.id} className="flex justify-between w-full">
|
||||
<p className="text-s text-[#73787C]">{switcher.title}</p>
|
||||
<Switch switcher={switcher} onClick={handleOnSwitcherClick} />
|
||||
@@ -158,7 +172,7 @@ const MasterplanFiltersModal = () => {
|
||||
<div className="flex flex-col w-full">
|
||||
<p className="text-[#0D1922] text-s pb-4">Views</p>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{viewCheckboxes.map((checkbox) => (
|
||||
{localViewCheckboxes.map((checkbox) => (
|
||||
<Checkbox
|
||||
checkbox={checkbox}
|
||||
key={checkbox.id}
|
||||
|
||||
@@ -19,9 +19,13 @@ export const MobileModalWrapperContext =
|
||||
|
||||
interface MobileModalWrapperProps {
|
||||
children: React.ReactNode;
|
||||
isHeightScreen?: boolean;
|
||||
}
|
||||
|
||||
const MobileModalWrapper = ({ children }: MobileModalWrapperProps) => {
|
||||
const MobileModalWrapper = ({
|
||||
children,
|
||||
isHeightScreen = false,
|
||||
}: MobileModalWrapperProps) => {
|
||||
const [isAnimate, setIsAnimate] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -30,8 +34,10 @@ const MobileModalWrapper = ({ children }: MobileModalWrapperProps) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`h-screen w-screen pt-20 absolute z-[99999999] top-0 transition-all duration-300 ease-in-out ${
|
||||
isAnimate ? "backdrop-blur-sm bg-[#0D192266]" : "backdrop-blur-none"
|
||||
className={`h-screen w-screen ${
|
||||
isHeightScreen ? "" : "pt-20"
|
||||
} absolute z-[99999998] top-0 transition-all duration-300 ease-in-out ${
|
||||
isAnimate ? "" : "backdrop-blur-none"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import useModal from "../../../store/useModal";
|
||||
import MinusIcon from "../../icons/MinusIcon";
|
||||
import LayoutSlider from "../../complexWingPage/SkygardenSidebar/LayoutSlider";
|
||||
import ActivityCard from "../../complexWingPage/SkygardenSidebar/ActivityCard";
|
||||
import AmphitheatreIcon from "../../icons/activities/AmphitheatreIcon";
|
||||
import BoulderingWallIcon from "../../icons/activities/BoulderingWallIcon";
|
||||
import ChangingRoomsIcon from "../../icons/activities/ChangingRoomsIcon";
|
||||
import ChessTablesIcon from "../../icons/activities/ChessTablesIcon";
|
||||
import ClimbingRoomIcon from "../../icons/activities/ClimbingRoomIcon";
|
||||
import CommunalDiningTablesIcon from "../../icons/activities/CommunalDiningTablesIcon";
|
||||
import IndoorLapPoolIcon from "../../icons/activities/IndoorLapPoolIcon";
|
||||
import LushLandscapeIcon from "../../icons/activities/LushLandscapeIcon";
|
||||
import MultiPurposeCourtIcon from "../../icons/activities/MultiPurposeCourtIcon";
|
||||
import OutdoorCinemaIcon from "../../icons/activities/OutdoorCinemaIcon";
|
||||
import OutdoorCoworkingSpaceIcon from "../../icons/activities/OutdoorCoworkingSpaceIcon";
|
||||
import PadelPongIcon from "../../icons/activities/PadelPongIcon";
|
||||
import PingPongInATableIcon from "../../icons/activities/PingPongInATableIcon";
|
||||
import RunningWheelIcon from "../../icons/activities/RunningWheelIcon";
|
||||
import SunLoungingDeckIcon from "../../icons/activities/SunLoungingDeckIcon";
|
||||
import SuspendedLoungingNetsIcon from "../../icons/activities/SuspendedLoungingNetsIcon";
|
||||
import WellnessFeaturesIcon from "../../icons/activities/WellnessFeaturesIcon";
|
||||
import ZoneSlider from "../../complexWingPage/SkygardenSidebar/ZoneSlider";
|
||||
|
||||
const SkygardenModal = () => {
|
||||
const { setModal } = useModal();
|
||||
const [isAnimate, setIsAnimate] = useState(false);
|
||||
|
||||
const handleOnClose = () => {
|
||||
if (setIsAnimate) {
|
||||
setIsAnimate(false);
|
||||
const timeout = setTimeout(() => {
|
||||
setModal(null);
|
||||
clearTimeout(timeout);
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
const handlers = useSwipeable({
|
||||
onSwipedDown: () => handleOnClose(),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setIsAnimate(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`h-[calc(100vh-64px)] w-screen pt-[128px] absolute z-[99999999] top-0 duration-300 ease-in-out `}
|
||||
>
|
||||
<div
|
||||
className={`flex flex-col text-white px-4 pb-6 transition-opacity duration-300 ease-in-out ${
|
||||
isAnimate ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
>
|
||||
<p className="text-subheadline-s font-semibold">
|
||||
Rove Home Marasi Drive
|
||||
</p>
|
||||
<p className="text-s">Skygarden</p>
|
||||
</div>
|
||||
<div
|
||||
style={{ transform: `translateY(${isAnimate ? 0 : 100}%)` }}
|
||||
className="bg-[#F3F3F2] h-fit p-6 pt-0 flex flex-col gap-6 transition-transform ease-in-out relative"
|
||||
>
|
||||
<div
|
||||
className="w-[calc(100%-48px)] absolute flex justify-center py-3"
|
||||
{...handlers}
|
||||
>
|
||||
<MinusIcon />
|
||||
</div>
|
||||
<div className="pt-6 flex justify-between items-center">
|
||||
<div className="flex flex-col">
|
||||
<h2 className="font-semibold text-[#0D1922] text-subheadline-m">
|
||||
Skygarden
|
||||
</h2>
|
||||
<p className="text-s">22-23 floor</p>
|
||||
</div>
|
||||
<div className="py-[3px] px-2 text-white bg-[#00BED7] rounded-full text-caption-m">
|
||||
17 amenties
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-fit w-full bg-white rounded-lg flex justify-center items-center">
|
||||
<div className="px-4 bg-white font-semibold text-caption-m rounded-2xl justify-center items-center mb-4 pb-4 mx-6 flex flex-col gap-1 pt-4">
|
||||
<LayoutSlider />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4 border-t pt-4 pb-4 mx-6">
|
||||
<div className="font-semibold text-subheadline-s">
|
||||
Indoor Amenties
|
||||
</div>
|
||||
<ActivityCard
|
||||
title={"Indoor Lap Pool"}
|
||||
icon={<IndoorLapPoolIcon />}
|
||||
/>
|
||||
<ActivityCard
|
||||
title={"Wellness Features"}
|
||||
icon={<WellnessFeaturesIcon />}
|
||||
/>
|
||||
<ActivityCard title={"Changing Rooms"} icon={<ChangingRoomsIcon />} />
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4 border-t py-4 border-b mb-4 mx-6">
|
||||
<div className="font-semibold text-subheadline-s">
|
||||
Outdoor Amenties
|
||||
</div>
|
||||
<ActivityCard title={"Padel Pong"} icon={<PadelPongIcon />} />
|
||||
<ActivityCard
|
||||
title={"Sun Lounging Deck"}
|
||||
icon={<SunLoungingDeckIcon />}
|
||||
/>
|
||||
<ActivityCard title={"Outdoor Cinema"} icon={<OutdoorCinemaIcon />} />
|
||||
<ActivityCard
|
||||
title={"Bouldering Wall"}
|
||||
icon={<BoulderingWallIcon />}
|
||||
/>
|
||||
<ActivityCard
|
||||
title={"Ping Pong in a Tube"}
|
||||
icon={<PingPongInATableIcon />}
|
||||
/>
|
||||
<ActivityCard title={"Amphitheatre"} icon={<AmphitheatreIcon />} />
|
||||
<ActivityCard
|
||||
title={"Communal Dining Tables"}
|
||||
icon={<CommunalDiningTablesIcon />}
|
||||
/>
|
||||
<ActivityCard
|
||||
title={"Suspended Lounging Nets "}
|
||||
icon={<SuspendedLoungingNetsIcon />}
|
||||
/>
|
||||
<ActivityCard title={"Lush Landscape"} icon={<LushLandscapeIcon />} />
|
||||
<ActivityCard title={"Running Wheel"} icon={<RunningWheelIcon />} />
|
||||
<ActivityCard title={"Chess Tables"} icon={<ChessTablesIcon />} />
|
||||
<ActivityCard title={"Climbing Wall"} icon={<ClimbingRoomIcon />} />
|
||||
<ActivityCard
|
||||
title={"Outdoor Coworking Space"}
|
||||
icon={<OutdoorCoworkingSpaceIcon />}
|
||||
/>
|
||||
<ActivityCard
|
||||
title={"Multi-purpose Court"}
|
||||
icon={<MultiPurposeCourtIcon />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ZoneSlider />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SkygardenModal;
|
||||
@@ -1,11 +1,73 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import useModal from "../../../store/useModal";
|
||||
import { useEffect, useState } from "react";
|
||||
import MinusIcon from "../../icons/MinusIcon";
|
||||
import { IAparmentRes } from "../../../types/apartmentsRes";
|
||||
import { IDesctiptionFloor } from "../../../types/descriptionFloor";
|
||||
import { apartmentsWithoutVirtualTour } from "../../../consts/apartmentsWithoutVirtualTour";
|
||||
import EastWingHighlighting from "../../complexWingPage/FloorSidebar/EastWingHighlighting";
|
||||
import EastWingLayout from "../../complexWingPage/FloorSidebar/EastWingLayout";
|
||||
import WestWingHighlighting from "../../complexWingPage/FloorSidebar/WestWingHighlighting";
|
||||
import WestWingLayout from "../../complexWingPage/FloorSidebar/WestWingLayout";
|
||||
import WestWingTopLevelsHighlighting from "../../complexWingPage/FloorSidebar/WestWingTopLevelsHighlighting";
|
||||
import WestWingTopLevelsLayout from "../../complexWingPage/FloorSidebar/WestWingTopLevelsLayout";
|
||||
import { filterCurrentApartment } from "../../../calc/filterCurrentApartment";
|
||||
import MobileApartmentDescription from "../../complexWingPage/MobileApartmentDescription";
|
||||
|
||||
const WingFloorModal = () => {
|
||||
const getDefaultApartments = (currentFloor: IDesctiptionFloor | null) => {
|
||||
if (!currentFloor) return [];
|
||||
const defaultApartment: IAparmentRes[] = apartmentsWithoutVirtualTour.map(
|
||||
(aprt, index) => {
|
||||
const unitNo = `${currentFloor?.wing.slice(0, 1)}-${
|
||||
currentFloor.floor
|
||||
}0${index}`;
|
||||
return {
|
||||
id: unitNo,
|
||||
Floor: currentFloor.floor,
|
||||
Property_Status: "Available",
|
||||
Unit_Type: aprt.type,
|
||||
Project_Name: "Rove Home Marasi Drive",
|
||||
Suite_Area_Sqft: 0,
|
||||
Balcony_Area_Sqft: 0,
|
||||
No_Of_Bedrooms: 1,
|
||||
Unit_No: unitNo,
|
||||
Total_Area_Sqft: 0,
|
||||
No_of_Bathrooms: 0,
|
||||
Property_Name: "-",
|
||||
Unit_View: "-",
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
return defaultApartment;
|
||||
};
|
||||
|
||||
interface WingFloorModalProps {
|
||||
currentFloor: IDesctiptionFloor | null;
|
||||
floorApartments: IAparmentRes[];
|
||||
}
|
||||
|
||||
const WingFloorModal = ({
|
||||
currentFloor,
|
||||
floorApartments,
|
||||
}: WingFloorModalProps) => {
|
||||
const { setModal } = useModal();
|
||||
const [isAnimate, setIsAnimate] = useState(false);
|
||||
const [apartments, setApartments] = useState<IAparmentRes[]>([]);
|
||||
const [currentApartmentType, setCurrentApartmentType] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [currentApartment, setCurrentApartment] = useState<IAparmentRes | null>(
|
||||
null
|
||||
);
|
||||
const [isDescVisible, setIsDescVisible] = useState(false);
|
||||
const [mousePos, setMousePos] = useState<number[]>([0, 0]);
|
||||
const wing =
|
||||
floorApartments.length > 0 &&
|
||||
floorApartments[0].Unit_No.split("-")[0] === "E"
|
||||
? "East Wing"
|
||||
: "West Wing";
|
||||
|
||||
const handleOnClose = () => {
|
||||
if (setIsAnimate) {
|
||||
@@ -17,85 +79,194 @@ const WingFloorModal = () => {
|
||||
}
|
||||
};
|
||||
|
||||
function handleOnApartmentClick(
|
||||
event: React.MouseEvent<SVGSVGElement, MouseEvent>
|
||||
) {
|
||||
const apartmentType = event.currentTarget.dataset.type;
|
||||
if (!apartmentType) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentApartmentType(apartmentType);
|
||||
|
||||
// const apartments = [
|
||||
// ...getDefaultApartments(currentFloor),
|
||||
// ...floorApartments,
|
||||
// ];
|
||||
// console.log("apartments", apartments);
|
||||
|
||||
const apartment = filterCurrentApartment(apartments, apartmentType);
|
||||
if (!apartment) return;
|
||||
|
||||
setCurrentApartment(apartment);
|
||||
setIsDescVisible(true);
|
||||
}
|
||||
|
||||
const handlers = useSwipeable({
|
||||
onSwipedDown: () => handleOnClose(),
|
||||
});
|
||||
|
||||
function handleMouseClick(
|
||||
event: React.MouseEvent<SVGSVGElement, MouseEvent> | any
|
||||
) {
|
||||
const isApartmentClicked = event.target.parentElement.dataset.type;
|
||||
if (!isApartmentClicked) {
|
||||
setIsDescVisible(false);
|
||||
} else {
|
||||
const scrollOffsetY = window.scrollY;
|
||||
//detect mouse pos
|
||||
const y = event.clientY - 65 + scrollOffsetY - 205;
|
||||
const x = event.clientX - 50;
|
||||
setMousePos([x, y]);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentFloor) return;
|
||||
const defaultApartment: IAparmentRes[] = getDefaultApartments(currentFloor);
|
||||
|
||||
setApartments([...defaultApartment, ...floorApartments]);
|
||||
}, [currentFloor, currentFloor?.floor, currentFloor?.wing, floorApartments]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsAnimate(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("mousemove", handleMouseClick);
|
||||
return () => {
|
||||
window.removeEventListener("mousemove", handleMouseClick);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`h-[calc(100vh-64px)] w-screen pt-[128px] absolute z-[99999999] top-0 duration-300 ease-in-out `}
|
||||
>
|
||||
<>
|
||||
<div
|
||||
className={`flex flex-col text-white px-4 pb-6 transition-opacity duration-300 ease-in-out ${
|
||||
isAnimate ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
>
|
||||
<p className="text-subheadline-s font-semibold">
|
||||
Rove Home Marasi Drive
|
||||
</p>
|
||||
<p className="text-s">11 floor</p>
|
||||
</div>
|
||||
<div
|
||||
style={{ transform: `translateY(${isAnimate ? 0 : 100}%)` }}
|
||||
className="bg-[#F3F3F2] h-fit p-6 pt-0 flex flex-col gap-6 transition-transform ease-in-out relative"
|
||||
className={`h-[calc(100vh-64px)] w-screen pt-[128px] absolute z-[99999997] top-0 duration-300 ease-in-out overflow-x-clip`}
|
||||
>
|
||||
{isDescVisible && (
|
||||
<div
|
||||
className="absolute z-[99999999] w-fit h-fit top-0 left-0"
|
||||
style={{ top: `${mousePos[1]}px`, left: `${mousePos[0]}px` }}
|
||||
>
|
||||
<MobileApartmentDescription
|
||||
apartment={currentApartment}
|
||||
apartmentDataType={currentApartmentType}
|
||||
apartments={apartments}
|
||||
floor={currentFloor}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="w-[calc(100%-48px)] absolute flex justify-center py-3"
|
||||
{...handlers}
|
||||
className={`flex flex-col text-white px-4 pb-6 transition-opacity duration-300 ease-in-out ${
|
||||
isAnimate ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
>
|
||||
<MinusIcon />
|
||||
<p className="text-subheadline-s font-semibold">
|
||||
Rove Home Marasi Drive
|
||||
</p>
|
||||
<p className="text-s">{currentFloor?.floor} floor</p>
|
||||
</div>
|
||||
<div className="pt-6 flex justify-between items-center">
|
||||
<div className="flex flex-col">
|
||||
<h2 className="font-semibold text-[#0D1922] text-subheadline-m">
|
||||
11 floor
|
||||
</h2>
|
||||
<p className="text-s">East Wing</p>
|
||||
<div
|
||||
style={{ transform: `translateY(${isAnimate ? 0 : 100}%)` }}
|
||||
className="bg-[#F3F3F2] h-fit p-6 pt-0 flex flex-col gap-6 transition-transform ease-in-out relative"
|
||||
>
|
||||
<div
|
||||
className="w-[calc(100%-48px)] absolute flex justify-center py-3"
|
||||
{...handlers}
|
||||
>
|
||||
<MinusIcon />
|
||||
</div>
|
||||
<div className="py-[3px] px-2 text-white bg-[#00BED7] rounded-full text-caption-m">
|
||||
22 units
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center py-2 px-4 rounded-lg bg-white">
|
||||
<div>11 floor</div>
|
||||
<div className="flex items-center justify-center gap-6">
|
||||
<div className="flex gap-2 text-[#0D1922B2] items-center">
|
||||
<div className="w-5 h-5 flex items-center bg-[#A19E9E] text-white justify-center rounded-full pt-[1.5px] pb-[2.5px]">
|
||||
1
|
||||
</div>
|
||||
<p className="text-caption-m font-semibold">Studio flex</p>
|
||||
<div className="pt-6 flex justify-between items-center">
|
||||
<div className="flex flex-col">
|
||||
<h2 className="font-semibold text-[#0D1922] text-subheadline-m">
|
||||
{currentFloor?.floor} floor
|
||||
</h2>
|
||||
<p className="text-s">{wing}</p>
|
||||
</div>
|
||||
<div className="flex gap-2 text-[#0D1922B2] items-center">
|
||||
<div className="w-5 h-5 flex items-center bg-[#8299AD] text-white justify-center rounded-full pt-[1.5px] pb-[2.5px]">
|
||||
11
|
||||
</div>
|
||||
<p className="text-caption-m font-semibold">Studio</p>
|
||||
</div>
|
||||
<div className="flex gap-2 text-[#0D1922B2] items-center">
|
||||
<div className="w-5 h-5 flex items-center bg-[#BFC9D1] text-white justify-center rounded-full pt-[1.5px] pb-[2.5px]">
|
||||
3
|
||||
</div>
|
||||
<p className="text-caption-m font-semibold">1 Bedroom</p>
|
||||
</div>
|
||||
<div className="flex gap-2 text-[#0D1922B2] items-center">
|
||||
<div className="w-5 h-5 flex items-center bg-[#878FA3] text-white justify-center rounded-full pt-[1.5px] pb-[2.5px]">
|
||||
7
|
||||
</div>
|
||||
<p className="text-caption-m font-semibold">2 Bedroom</p>
|
||||
<div className="py-[3px] px-2 text-white bg-[#00BED7] rounded-full text-caption-m">
|
||||
{floorApartments.length} units
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-fit w-full bg-white rounded-lg flex justify-center items-center">
|
||||
<div className="">
|
||||
<img src="/images/floor-apartment-layout.png" alt="" />
|
||||
<div className="flex justify-between items-center py-2 px-4 rounded-lg bg-white">
|
||||
<div>{currentFloor?.floor} floor</div>
|
||||
<div className="flex items-center justify-center gap-6">
|
||||
<div className="flex gap-2 text-[#0D1922B2] items-center">
|
||||
<div className="w-5 h-5 flex items-center bg-[#A19E9E] text-white justify-center rounded-full pt-[1.5px] pb-[2.5px]">
|
||||
1
|
||||
</div>
|
||||
<p className="text-caption-m font-semibold">Studio flex</p>
|
||||
</div>
|
||||
<div className="flex gap-2 text-[#0D1922B2] items-center">
|
||||
<div className="w-5 h-5 flex items-center bg-[#8299AD] text-white justify-center rounded-full pt-[1.5px] pb-[2.5px]">
|
||||
11
|
||||
</div>
|
||||
<p className="text-caption-m font-semibold">Studio</p>
|
||||
</div>
|
||||
<div className="flex gap-2 text-[#0D1922B2] items-center">
|
||||
<div className="w-5 h-5 flex items-center bg-[#BFC9D1] text-white justify-center rounded-full pt-[1.5px] pb-[2.5px]">
|
||||
3
|
||||
</div>
|
||||
<p className="text-caption-m font-semibold">1 Bedroom</p>
|
||||
</div>
|
||||
<div className="flex gap-2 text-[#0D1922B2] items-center">
|
||||
<div className="w-5 h-5 flex items-center bg-[#878FA3] text-white justify-center rounded-full pt-[1.5px] pb-[2.5px]">
|
||||
7
|
||||
</div>
|
||||
<p className="text-caption-m font-semibold">2 Bedroom</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-10 bg-white flex gap-6 font-semibold text-caption-m rounded-2xl justify-center items-center flex-1 py-4 overflow-clip">
|
||||
{currentFloor?.wing === "West Wing" ? (
|
||||
currentFloor && currentFloor.floor <= 21 ? (
|
||||
<svg
|
||||
className="rotate-[135deg]"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="400"
|
||||
height="500"
|
||||
fill="none"
|
||||
viewBox="0 0 204 600"
|
||||
>
|
||||
<WestWingLayout />
|
||||
<WestWingHighlighting
|
||||
handleOnApartmentClick={handleOnApartmentClick}
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
className="rotate-[135deg]"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="400"
|
||||
height="700"
|
||||
fill="none"
|
||||
viewBox="0 0 204 600"
|
||||
>
|
||||
<WestWingTopLevelsLayout />
|
||||
<WestWingTopLevelsHighlighting
|
||||
handleOnApartmentClick={handleOnApartmentClick}
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
) : (
|
||||
<svg
|
||||
className="rotate-[135deg]"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
width="672"
|
||||
height="700"
|
||||
viewBox="0 0 672 280"
|
||||
>
|
||||
<EastWingLayout />
|
||||
<EastWingHighlighting
|
||||
handleOnApartmentClick={handleOnApartmentClick}
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
PORT=4002
|
||||
PORT=4003
|
||||
REFRESH_TOKEN=1000.da3146d49fa8a399f0c635e74954ff9c.e010dbb1bb605d7e1aa5bf7fc0521f8b
|
||||
Reference in New Issue
Block a user