Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99fa5a93d0 | |||
| a1b896b37f | |||
| 72bfdeb5a3 | |||
| d1b6eb7c82 | |||
| 4afbf6bf66 | |||
| 54d48c8ce3 | |||
| 226397afac | |||
| 1497d6ede3 | |||
| 6a415ec165 |
@@ -2,4 +2,6 @@
|
||||
# VITE_API_URL=http://192.168.1.144:4002
|
||||
# VITE_API_URL=http://194.26.138.94:4002
|
||||
# VITE_API_URL=https://irthtest.online/api
|
||||
VITE_API_URL=https://irth.graff.estate/api
|
||||
VITE_API_URL=https://irth.graff.estate/api
|
||||
VITE_GOOGLE_MAP_API_KEY=AIzaSyD1aCnh8qEIh9ACrZWeHddYJLyHMX4KsoE
|
||||
VITE_GOOGLE_MAP_ID=30ff24bdc133c941ee0d0608
|
||||
@@ -25,3 +25,5 @@ public/virtual-tours
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
.env
|
||||
@@ -4,11 +4,13 @@
|
||||
"": {
|
||||
"name": "irth-new",
|
||||
"dependencies": {
|
||||
"@googlemaps/markerclusterer": "^2.6.2",
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"@tanstack/react-query": "^5.74.4",
|
||||
"@tanstack/react-query-devtools": "^5.74.7",
|
||||
"@tweenjs/tween.js": "^25.0.0",
|
||||
"@uidotdev/usehooks": "^2.4.1",
|
||||
"@vis.gl/react-google-maps": "^1.5.4",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
@@ -111,6 +113,8 @@
|
||||
|
||||
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.2.8", "", { "dependencies": { "@eslint/core": "^0.13.0", "levn": "^0.4.1" } }, "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA=="],
|
||||
|
||||
"@googlemaps/markerclusterer": ["@googlemaps/markerclusterer@2.6.2", "", { "dependencies": { "@types/supercluster": "^7.1.3", "fast-equals": "^5.2.2", "supercluster": "^8.0.1" } }, "sha512-U6uVhq8iWhiIckA89sgRu8OK35mjd6/3CuoZKWakKEf0QmRRWpatlsPb3kqXkoWSmbcZkopRiI4dnW6DQSd7bQ=="],
|
||||
|
||||
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
||||
|
||||
"@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
|
||||
@@ -237,6 +241,10 @@
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
|
||||
|
||||
"@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
|
||||
|
||||
"@types/google.maps": ["@types/google.maps@3.58.1", "", {}, "sha512-X9QTSvGJ0nCfMzYOnaVs/k6/4L+7F5uCS+4iUmkLEls6J9S/Phv+m/i3mDeyc49ZBgwab3EFO1HEoBY7k98EGQ=="],
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
||||
"@types/node": ["@types/node@22.14.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA=="],
|
||||
@@ -245,6 +253,8 @@
|
||||
|
||||
"@types/react-dom": ["@types/react-dom@19.1.1", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w=="],
|
||||
|
||||
"@types/supercluster": ["@types/supercluster@7.1.3", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.29.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.29.0", "@typescript-eslint/type-utils": "8.29.0", "@typescript-eslint/utils": "8.29.0", "@typescript-eslint/visitor-keys": "8.29.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-PAIpk/U7NIS6H7TEtN45SPGLQaHNgB7wSjsQV/8+KYokAb2T/gloOA/Bee2yd4/yKVhPKe5LlaUGhAZk5zmSaQ=="],
|
||||
@@ -265,6 +275,8 @@
|
||||
|
||||
"@uidotdev/usehooks": ["@uidotdev/usehooks@2.4.1", "", { "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg=="],
|
||||
|
||||
"@vis.gl/react-google-maps": ["@vis.gl/react-google-maps@1.5.4", "", { "dependencies": { "@types/google.maps": "^3.54.10", "fast-deep-equal": "^3.1.3" }, "peerDependencies": { "react": ">=16.8.0 || ^19.0 || ^19.0.0-rc", "react-dom": ">=16.8.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-pD3e2wDtOfd439mamkacRgrM6I2B/lue61QCR0pGQT8MVaG9pz9/LajHbsjZW2lms8Ao8mf2PQJeiGC2FxI0Fw=="],
|
||||
|
||||
"@vitejs/plugin-react-swc": ["@vitejs/plugin-react-swc@3.8.1", "", { "dependencies": { "@swc/core": "^1.11.11" }, "peerDependencies": { "vite": "^4 || ^5 || ^6" } }, "sha512-aEUPCckHDcFyxpwFm0AIkbtv6PpUp3xTb9wYGFjtABynXjCYKkWoxX0AOK9NT9XCrdk6mBBUOeHQS+RKdcNO1A=="],
|
||||
|
||||
"acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
|
||||
@@ -343,6 +355,8 @@
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-equals": ["fast-equals@5.2.2", "", {}, "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw=="],
|
||||
|
||||
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
||||
|
||||
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
||||
@@ -403,6 +417,8 @@
|
||||
|
||||
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
||||
|
||||
"kdbush": ["kdbush@4.0.2", "", {}, "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA=="],
|
||||
|
||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||
|
||||
"ky": ["ky@1.8.1", "", {}, "sha512-7Bp3TpsE+L+TARSnnDpk3xg8Idi8RwSLdj6CMbNWoOARIrGrbuLGusV0dYwbZOm4bB3jHNxSw8Wk/ByDqJEnDw=="],
|
||||
@@ -513,6 +529,8 @@
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||
|
||||
"supercluster": ["supercluster@8.0.1", "", { "dependencies": { "kdbush": "^4.0.2" } }, "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ=="],
|
||||
|
||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.3", "", {}, "sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g=="],
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@googlemaps/markerclusterer": "^2.6.2",
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"@tanstack/react-query": "^5.74.4",
|
||||
"@tanstack/react-query-devtools": "^5.74.7",
|
||||
"@tweenjs/tween.js": "^25.0.0",
|
||||
"@uidotdev/usehooks": "^2.4.1",
|
||||
"@vis.gl/react-google-maps": "^1.5.4",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="32" height="32" rx="16" fill="#0D1922"/>
|
||||
<path d="M13 11.6869H11C10.4477 11.6869 10 12.1346 10 12.6869V22C10 22.5523 10.4477 23 11 23H20.9993C21.5516 23 21.9993 22.5523 21.9993 22.0001L21.9999 12.687C22 12.1346 21.5522 11.6869 20.9999 11.6869H19M13 11.6869V11C13 9.34315 14.3431 8 16 8V8C17.6569 8 19 9.34315 19 11V11.6869M13 11.6869H19" stroke="white" stroke-width="1.5" stroke-linecap="square"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 514 B |
@@ -20,6 +20,9 @@ import clsx from "clsx";
|
||||
import Slider from "./Slider";
|
||||
import PlusIcon from "./icons/map/PlusIcon";
|
||||
import EqualIcon from "./icons/EqualIcon";
|
||||
import GoogleMap from "./google-map/GoogleMap";
|
||||
import { GoogleMapData } from "../data/googleMapData";
|
||||
import GoogleMapMobile from "./google-map/GoogleMapMobile";
|
||||
// import FullScreenButton from "./FullScreenButton";
|
||||
|
||||
function AboutDubaiMarina() {
|
||||
@@ -336,7 +339,7 @@ function AboutDubaiMarina() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-[2.222vw] bg-white px-[2.222vw] pb-[8.333vw] max-md:gap-[6.667vw] max-md:px-[4.444vw] max-2xl:gap-[4.167vw]">
|
||||
{/* <h1
|
||||
<h1
|
||||
className="font-mixcase-unmixed text-[3.889vw] text-[#0D1922] w-[44.861vw] leading-[100%] tracking-[-0.05em] pt-[7.222vw] text-center
|
||||
max-md:text-[6.667vw] max-md:w-full max-2xl:text-[7.292vw] max-2xl:w-[84.115vw]"
|
||||
>
|
||||
@@ -347,31 +350,18 @@ function AboutDubaiMarina() {
|
||||
living meets modern convenience. Enjoy an energetic lifestyle surrounded by
|
||||
trendy cafés, shops, and entertainment options – all within reach.`}
|
||||
</p>
|
||||
<motion.img
|
||||
ref={mapRef}
|
||||
src="/images/about-complex/dubai-marina/central_map.png"
|
||||
alt="central map"
|
||||
className="rounded-3xl object-cover object-center aspect-[1376/609] max-md:hidden max-2xl:w-[93.75vw] max-2xl:h-[89.583vw] max-2xl:aspect-[720/688] max-2xl:mt-[1.563vw]"
|
||||
initial={{ width: "47.083vw" }}
|
||||
animate={
|
||||
isMapInView ? { width: "95.556vw" } : { width: "47.083vw" }
|
||||
}
|
||||
transition={{ duration: 0.6, ease: "easeInOut" }}
|
||||
/>
|
||||
<div className="min-md:hidden relative w-[89.167vw] aspect-square overflow-hidden rounded-2xl">
|
||||
<img
|
||||
src="/images/about-complex/dubai-marina/central_map.png"
|
||||
alt="central map"
|
||||
className="w-full h-full object-cover object-center scale-120"
|
||||
<div className="w-full 2xl:aspect-[1376/688] max-2xl:aspect-[720/688] max-md:hidden rounded-[1.667vw] overflow-hidden">
|
||||
<GoogleMap
|
||||
markers={GoogleMapData.markers}
|
||||
mapCenter={GoogleMapData.dubaiMarinaDefaultCenter}
|
||||
/>
|
||||
<div className="absolute bottom-[1.111vw] right-[1.111vw] w-[11.111vw] h-[11.111vw]">
|
||||
<FullScreenButton
|
||||
isFullScreen={false}
|
||||
onClick={() => {}}
|
||||
onFullScreenChange={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<GoogleMapMobile
|
||||
markers={GoogleMapData.markers}
|
||||
mapCenter={GoogleMapData.dubaiMarinaDefaultCenter}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.section>
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import {
|
||||
marasiDriveFeatures,
|
||||
marasiDriveDescriptionBadges,
|
||||
marasiDriveMapCards,
|
||||
// marasiDriveMapCards,
|
||||
} from "../data/aboutMarasiDrive";
|
||||
import MarariDriveNeighboursSliderDesktop from "./MarasiDriveNeighboursSliderDesktop";
|
||||
import MarasiDriveInteriorsSlider from "./MarasiDriveInteriorsSlider";
|
||||
import MarasiDriveInteriorsSliderMobile from "./MarasiDriveInteriorsSliderMobile";
|
||||
import MarasiDriveNeighboursSliderMobile from "./MarasiDriveNeighboursSliderMobile";
|
||||
import MarasiDriveMapMobile from "./MarasiDriveMapMobile";
|
||||
import GoogleMapMobile from "./google-map/GoogleMapMobile";
|
||||
import TextBox from "./ui/TextBox";
|
||||
import { useRef } from "react";
|
||||
import { useScroll } from "motion/react";
|
||||
import MarasiDriveMapCard from "./MarasiDriveMapCard";
|
||||
|
||||
// import MarasiDriveMapCard from "./MarasiDriveMapCard";
|
||||
import GoogleMap from "./google-map/GoogleMap";
|
||||
import MarasiDriveNeighboursSliderTablet from "./MarasiDriveNeighboursSliderTablet";
|
||||
import CustomScrollBar from "./ui/ScrollBar";
|
||||
import BrochureButton from "./ui/BrochureButton";
|
||||
import { GoogleMapData } from "../data/googleMapData";
|
||||
|
||||
function AboutMarasiDrive() {
|
||||
const target = useRef<HTMLDivElement>(null);
|
||||
@@ -98,19 +99,32 @@ function AboutMarasiDrive() {
|
||||
destination for artful inspiration and cleverly activated spaces.`}
|
||||
</p>
|
||||
</div>
|
||||
<div ref={homeSliderRef} className="flex max-2xl:flex-wrap max-2xl:justify-center 2xl:gap-[0.556vw] gap-2 md:max-2xl:w-[93.75vw] max-md:flex-row max-md:flex-nowrap max-md:overflow-x-scroll max-md:overflow-y-hidden max-md:justify-start max-md:snap-x max-md:snap-mandatory [&::-webkit-scrollbar]:h-[1.111vw] [&::-webkit-scrollbar]:w-[none] [&::-webkit-scrollbar-thumb]:bg-transparent [&::-webkit-scrollbar-thumb]:w-4 [&::-webkit-scrollbar-thumb]:rounded-full max-md:-mx-4 max-md:px-4">
|
||||
<div
|
||||
ref={homeSliderRef}
|
||||
className="flex max-2xl:flex-wrap max-2xl:justify-center 2xl:gap-[0.556vw] gap-2 md:max-2xl:w-[93.75vw] max-md:flex-row max-md:flex-nowrap max-md:overflow-x-scroll max-md:overflow-y-hidden max-md:justify-start max-md:snap-x max-md:snap-mandatory [&::-webkit-scrollbar]:h-[1.111vw] [&::-webkit-scrollbar]:w-[none] [&::-webkit-scrollbar-thumb]:bg-transparent [&::-webkit-scrollbar-thumb]:w-4 [&::-webkit-scrollbar-thumb]:rounded-full max-md:-mx-4 max-md:px-4"
|
||||
>
|
||||
{marasiDriveFeatures.map(({ image, name }) => (
|
||||
<div key={name} className="relative md:max-2xl:w-[30.208vw] max-md:w-full max-md:max-w-[530px] max-md:flex-shrink-0 max-md:snap-center">
|
||||
<div
|
||||
key={name}
|
||||
className="relative md:max-2xl:w-[30.208vw] max-md:w-full max-md:max-w-[530px] max-md:flex-shrink-0 max-md:snap-center"
|
||||
>
|
||||
<img
|
||||
src={image}
|
||||
alt={name}
|
||||
className="object-cover object-center 2xl:rounded-[1.667vw] max-md:!aspect-[328/287] max-md:rounded-2xl md:max-2xl:rounded-[3.125vw]"
|
||||
/>
|
||||
<span className="md:hidden absolute bottom-[16px] text-white text-h5 left-1/2 -translate-x-1/2 text-center text-nowrap">{name}</span>
|
||||
<span className="md:hidden absolute bottom-[16px] text-white text-h5 left-1/2 -translate-x-1/2 text-center text-nowrap">
|
||||
{name}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<CustomScrollBar containerRef={homeSliderRef} inlinePadding={16} trackStyle="min-md:hidden" thumbStyle="min-md:hidden" />
|
||||
<CustomScrollBar
|
||||
containerRef={homeSliderRef}
|
||||
inlinePadding={16}
|
||||
trackStyle="min-md:hidden"
|
||||
thumbStyle="min-md:hidden"
|
||||
/>
|
||||
</section>
|
||||
<section className="bg-white w-full overflow-clip flex flex-col 2xl:gap-[4.444vw] gap-12 2xl:pt-[9.444vw] 2xl:px-[2.222vw] 2xl:pb-[2.222vw] md:max-2xl:pt-[104px] md:max-2xl:px-6 md:max-2xl:pb-8 pt-16 px-4 pb-8">
|
||||
<div className="flex flex-col 2xl:gap-[2.222vw] md:max-2xl:gap-8 gap-6 items-center">
|
||||
@@ -165,7 +179,7 @@ function AboutMarasiDrive() {
|
||||
</p>
|
||||
</div>
|
||||
<MarasiDriveInteriorsSlider />
|
||||
<MarasiDriveInteriorsSliderMobile/>
|
||||
<MarasiDriveInteriorsSliderMobile />
|
||||
</section>
|
||||
</div>
|
||||
<div className="2xl:sticky relative">
|
||||
@@ -220,22 +234,30 @@ function AboutMarasiDrive() {
|
||||
trendy cafés, shops, and entertainment options - all within reach.`}
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-6 grid-rows-5 gap-x-[1.111vw] gap-y-[0.556vw] max-2xl:hidden">
|
||||
{/* <div className="grid grid-cols-6 grid-rows-5 gap-x-[1.111vw] gap-y-[0.556vw] max-2xl:hidden">
|
||||
{marasiDriveMapCards.map((card) => (
|
||||
<MarasiDriveMapCard {...card} key={card.title} />
|
||||
))}
|
||||
<div className="col-start-3 col-span-full row-start-1 row-span-full">
|
||||
<img
|
||||
src="/images/about-complex/marasi-drive/map/map.png"
|
||||
alt=""
|
||||
className="object-cover size-full 2xl:rounded-[1.667vw]"
|
||||
<div className="col-start-3 col-span-full row-start-1 row-span-full rounded-[1.667vw] overflow-hidden">
|
||||
<GoogleMap
|
||||
mapCenter={GoogleMapData.marasiDriveDefaultCenter}
|
||||
markers={GoogleMapData.markers}
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
<div className="w-full 2xl:aspect-[1376/676] max-2xl:aspect-[720/688] max-md:hidden rounded-[1.667vw] overflow-hidden">
|
||||
<GoogleMap
|
||||
mapCenter={GoogleMapData.marasiDriveDefaultCenter}
|
||||
markers={GoogleMapData.markers}
|
||||
/>
|
||||
</div>
|
||||
<MarasiDriveMapMobile/>
|
||||
<GoogleMapMobile
|
||||
mapCenter={GoogleMapData.marasiDriveDefaultCenter}
|
||||
markers={GoogleMapData.markers}
|
||||
/>
|
||||
</section>
|
||||
<section className="bg-white w-full overflow-clip flex items-stretch justify-center gap-[1.111vw] 2xl:px-[10.278vw] 2xl:pt-[9.444vw] 2xl:pb-[15vw] md:max-2xl:py-[104px] md:mx-2xl:px-6 max-md:pt-20 max-md:pb-12 px-4 max-2xl:flex-col-reverse md:max-2xl:gap-[6.25vw] md:max-2xl:px-6">
|
||||
<div >
|
||||
<div>
|
||||
<img
|
||||
src="/images/about-complex/marasi-drive/podium.png"
|
||||
className="object-cover size-full rounded-[1.667vw] max-md:mt-[48px] max-md:aspect-[328/400] max-md:rounded-[6.667vw] md:max-2xl:rounded-[3.125vw] md:max-2xl:aspect-[720/400]"
|
||||
@@ -254,7 +276,9 @@ function AboutMarasiDrive() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-[1.111vw]">
|
||||
<h5 className="text-h5 font-medium max-md:mb-[16px] md:max-2xl:mb-[2.083vw]">Download our brochures</h5>
|
||||
<h5 className="text-h5 font-medium max-md:mb-[16px] md:max-2xl:mb-[2.083vw]">
|
||||
Download our brochures
|
||||
</h5>
|
||||
<div className="space-y-[0.833vw] max-md:space-y-[3.333vw] md:max-2xl:space-y-[1.563vw]">
|
||||
<BrochureButton
|
||||
title={"Main Brochure"}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import { useRef } from "react";
|
||||
import { marasiDriveMapCards } from "../data/aboutMarasiDrive";
|
||||
import CustomScrollBar from "./ui/ScrollBar";
|
||||
|
||||
function MarasiDriveMapMobile() {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<div className="min-2xl:hidden relative flex flex-col">
|
||||
<div className="max-md:aspect-[328/544] md:max-2xl:aspect[1/1] max-2xl:mb-4">
|
||||
<img
|
||||
src="/images/about-complex/marasi-drive/map/map.png"
|
||||
alt=""
|
||||
className="object-cover size-full rounded-[6.667vw] md:max-2xl:rounded-[3.125vw]"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="flex flex-nowrap overflow-x-scroll scroll-pl-4 gap-x-[16px] overflow-y-hidden justify-start snap-x snap-mandatory [&::-webkit-scrollbar]:h-[1.111vw] [&::-webkit-scrollbar]:w-[none] [&::-webkit-scrollbar-thumb]:bg-[#FFFFFF] [&::-webkit-scrollbar-thumb]:w-4 [&::-webkit-scrollbar-thumb]:rounded-full -mx-4 px-4 "
|
||||
>
|
||||
{marasiDriveMapCards.map((card, index) => (
|
||||
<div key={index} className="snap-start">
|
||||
<div
|
||||
className={`rounded-[6.667vw] px-[4.444vw] py-[3.333vw] w-[51.111vw] aspect-[184/122] bg-[#F3F3F2] flex-shrink-0 flex flex-col justify-between relative md:max-2xl:w-[25vw] md:max-2xl:rounded-[3.125vw] md:max-2xl:px-[2.083vw] md:max-2xl:py-[1.563vw]`}
|
||||
>
|
||||
<div className="space-y-[0.278vw]">
|
||||
<p className="text-m">{card.title}</p>
|
||||
<p className="text-s text-[#73787C]">{`${card.mins} mins`}</p>
|
||||
</div>
|
||||
<img
|
||||
src={card.image}
|
||||
className="rounded-[0.278vw] size-[13.333vw] object-cover absolute bottom-[4.444vw] right-[3.333vw] md:max-2xl:size-[6.25vw] md:max-2xl:bottom-[1.563vw] md:max-2xl:right-[2.083vw]"
|
||||
alt={card.title}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<CustomScrollBar containerRef={containerRef} inlinePadding={16} trackStyle="min-2xl:hidden max-2xl:translate-y-5" thumbStyle="min-2xl:hidden"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MarasiDriveMapMobile;
|
||||
@@ -0,0 +1,71 @@
|
||||
import IGMapPoi from "../../types/IGMapPoi";
|
||||
import GoogleMapMarkers from "./GoogleMapMarkers";
|
||||
import { Map, useMap } from "@vis.gl/react-google-maps";
|
||||
import { useEffect, useState } from "react";
|
||||
// import GoogleMapFilterButtons from "./GoogleMapFilterButtons";
|
||||
|
||||
interface IGMapProps {
|
||||
mapCenter: google.maps.LatLngLiteral;
|
||||
defaultZoom?: number;
|
||||
markers?: IGMapPoi[];
|
||||
minZoom?: number;
|
||||
mobile?: boolean;
|
||||
mobileActive?: boolean;
|
||||
}
|
||||
|
||||
export type MapFilter = "All" | "Hotels" | "Malls" | "Entertainment" | "Other";
|
||||
export default function GoogleMap({
|
||||
mapCenter,
|
||||
markers,
|
||||
defaultZoom = 14,
|
||||
minZoom = 10,
|
||||
mobile = false,
|
||||
mobileActive = false,
|
||||
}: IGMapProps) {
|
||||
const [mapMarkersFilter, setMapMarkersFilter] = useState<MapFilter>("All");
|
||||
const [isInteractale, setIsInteractale] = useState(!mobile);
|
||||
const map = useMap();
|
||||
|
||||
useEffect(() => {
|
||||
if (!map) return;
|
||||
|
||||
setIsInteractale(!mobile || (mobile && mobileActive));
|
||||
if (!mobileActive && mobile) {
|
||||
map.panTo(mapCenter);
|
||||
map.setZoom(defaultZoom);
|
||||
}
|
||||
}, [mobile, mobileActive, defaultZoom, mapCenter, map]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mobileActive) setMapMarkersFilter("All");
|
||||
}, [mobileActive]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`size-[100%] ${
|
||||
isInteractale ? "pointer-events-auto" : "pointer-events-none"
|
||||
}`}
|
||||
>
|
||||
<Map
|
||||
mapId={import.meta.env.VITE_GOOGLE_MAP_ID}
|
||||
defaultCenter={mapCenter}
|
||||
defaultZoom={defaultZoom}
|
||||
disableDefaultUI={true}
|
||||
minZoom={minZoom}
|
||||
gestureHandling={
|
||||
mobile ? (mobileActive ? "greedy" : "none") : "cooperative"
|
||||
}
|
||||
>
|
||||
{markers && (
|
||||
<GoogleMapMarkers data={markers} filter={mapMarkersFilter} />
|
||||
)}
|
||||
{/* <GoogleMapFilterButtons
|
||||
mobile={mobile}
|
||||
mobileActive={mobileActive}
|
||||
currentFilter={mapMarkersFilter}
|
||||
onChange={setMapMarkersFilter}
|
||||
/> */}
|
||||
</Map>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { useState } from "react";
|
||||
import AllIcon from "../icons/map/AllIcon";
|
||||
import EntertainmentIcon from "../icons/map/EntertainmentIcon";
|
||||
import HotelIcon from "../icons/map/HotelIcon";
|
||||
import MallsIcon from "../icons/map/MallsIcon";
|
||||
import OtherIcon from "../icons/map/OtherIcon";
|
||||
import Button from "../ui/Button";
|
||||
import { AnimatePresence, motion } from "motion/react";
|
||||
import { MapFilter } from "./GoogleMap";
|
||||
|
||||
export default function GoogleMapFilterButtons({
|
||||
mobile,
|
||||
mobileActive,
|
||||
currentFilter,
|
||||
onChange,
|
||||
}: {
|
||||
mobile: boolean;
|
||||
mobileActive: boolean;
|
||||
currentFilter: MapFilter;
|
||||
onChange: (newFilter: MapFilter) => void;
|
||||
}) {
|
||||
const filters: MapFilter[] = [
|
||||
"All",
|
||||
"Hotels",
|
||||
"Malls",
|
||||
"Entertainment",
|
||||
"Other",
|
||||
];
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const buttonsVisisble = (expanded && mobile && mobileActive) || !mobile;
|
||||
|
||||
const IconsByFilter = {
|
||||
All: <AllIcon />,
|
||||
Hotels: <HotelIcon />,
|
||||
Malls: <MallsIcon />,
|
||||
Entertainment: <EntertainmentIcon />,
|
||||
Other: <OtherIcon />,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex gap-[0.556vw] absolute items-center bottom-[1.111vw] left-1/2 -translate-x-1/2 ${
|
||||
mobile &&
|
||||
`flex-col gap-[1.111vw] w-full pt-5 translate-y-2 ${
|
||||
expanded && "backdrop-blur-[1px]"
|
||||
}`
|
||||
}`}
|
||||
>
|
||||
<AnimatePresence>
|
||||
{buttonsVisisble &&
|
||||
filters.map((key) => (
|
||||
<motion.div
|
||||
initial={{ translateY: 150, opacity: 0 }}
|
||||
animate={{ translateY: 0, opacity: 1 }}
|
||||
exit={{ translateY: 150, opacity: 0 }}
|
||||
// transition={{ duration: 0.1, delay: index * 0.1 }}
|
||||
key={key}
|
||||
>
|
||||
<Button
|
||||
onClick={() => onChange(key)}
|
||||
variant={key === currentFilter ? "cta" : "secondary"}
|
||||
>
|
||||
<div className="size-5">{IconsByFilter[key]}</div>
|
||||
{key}
|
||||
</Button>
|
||||
</motion.div>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
|
||||
{mobile && mobileActive && (
|
||||
<Button
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
variant={expanded ? "cta" : "secondary"}
|
||||
className="my-4 px-[3.889vw] py-[2.778vw] z-10 h-10 transition-all"
|
||||
>
|
||||
{!expanded && <div>{IconsByFilter[currentFilter]}</div>}
|
||||
|
||||
{expanded ? (
|
||||
<span>Apply</span>
|
||||
) : currentFilter === "All" ? (
|
||||
<span>Select Category</span>
|
||||
) : (
|
||||
<span>{currentFilter}</span>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { useMap } from "@vis.gl/react-google-maps";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import IGMapPoi from "../../types/IGMapPoi";
|
||||
import MapMarker from "./MapMarker";
|
||||
import {
|
||||
MarkerClusterer,
|
||||
DefaultRenderer,
|
||||
Cluster,
|
||||
Marker,
|
||||
} from "@googlemaps/markerclusterer";
|
||||
|
||||
class CustomMarkerRenderer extends DefaultRenderer {
|
||||
render({
|
||||
count,
|
||||
position,
|
||||
}: Cluster): google.maps.marker.AdvancedMarkerElement {
|
||||
const svgElement = document.createElement("div");
|
||||
svgElement.innerHTML = `
|
||||
<svg fill="white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="50" height="50">
|
||||
<circle cx="120" cy="120" opacity="1" r="70" />
|
||||
</svg>
|
||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: black; font-size: 12px; font-weight: bold;">
|
||||
${count}
|
||||
</div>`;
|
||||
|
||||
const marker = new google.maps.marker.AdvancedMarkerElement({
|
||||
position,
|
||||
content: svgElement,
|
||||
zIndex: Math.max(1000, count),
|
||||
});
|
||||
|
||||
return marker;
|
||||
}
|
||||
}
|
||||
|
||||
export default function GoogleMapMarkers({
|
||||
data,
|
||||
filter,
|
||||
}: {
|
||||
data: IGMapPoi[];
|
||||
filter: string;
|
||||
}) {
|
||||
const map = useMap();
|
||||
const [markers, setMarkers] = useState<{ [key: string]: Marker }>({});
|
||||
const [selectedMarker, setSelectedMarker] = useState<number | null>(null);
|
||||
const clusterer = useRef<MarkerClusterer | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!map) return;
|
||||
if (!clusterer.current) {
|
||||
clusterer.current = new MarkerClusterer({
|
||||
map: map,
|
||||
renderer: new CustomMarkerRenderer(),
|
||||
});
|
||||
}
|
||||
}, [map]);
|
||||
|
||||
useEffect(() => {
|
||||
clusterer.current?.clearMarkers();
|
||||
clusterer.current?.addMarkers(Object.values(markers));
|
||||
}, [markers]);
|
||||
|
||||
const setMarkerRef = (marker: Marker | null, key: string) => {
|
||||
if (marker && markers[key]) return;
|
||||
if (!marker && !markers[key]) return;
|
||||
|
||||
setMarkers((prev) => {
|
||||
if (prev[key] === marker) return prev;
|
||||
|
||||
if (marker) {
|
||||
return { ...prev, [key]: marker };
|
||||
} else {
|
||||
const newMarkers = { ...prev };
|
||||
delete newMarkers[key];
|
||||
return newMarkers;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{data.map(
|
||||
(poi: IGMapPoi, index: number) =>
|
||||
(filter === poi.type || filter === "All") && (
|
||||
<MapMarker
|
||||
key={index}
|
||||
markerKey={index}
|
||||
poi={poi}
|
||||
setMarkerRef={setMarkerRef}
|
||||
isSelected={selectedMarker === index}
|
||||
setSelectedMarker={setSelectedMarker}
|
||||
>
|
||||
{poi.customMarker}
|
||||
</MapMarker>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import { useEffect, useState } from "react";
|
||||
// import { marasiDriveMapCards } from "../data/aboutMarasiDrive";
|
||||
// import CustomScrollBar from "./ui/ScrollBar";
|
||||
// import { GoogleMapData } from "../data/googleMapData";
|
||||
import GoogleMap from "./GoogleMap";
|
||||
import Button from "../ui/Button";
|
||||
import FullScreenIcon from "../icons/FullScreenIcon";
|
||||
import useModalStore from "../../stores/useModalStore";
|
||||
import IGMapPoi from "../../types/IGMapPoi";
|
||||
|
||||
function GoogleMapMobile({
|
||||
markers,
|
||||
mapCenter,
|
||||
}: {
|
||||
markers?: IGMapPoi[];
|
||||
mapCenter: google.maps.LatLngLiteral;
|
||||
}) {
|
||||
// const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [mapActive, setMapActive] = useState(false);
|
||||
const { modal, setModal } = useModalStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (mapActive) setModal(<MapModal />);
|
||||
else setModal(null);
|
||||
}, [mapActive]);
|
||||
|
||||
useEffect(() => {
|
||||
setMapActive(!!modal);
|
||||
}, [modal]);
|
||||
|
||||
const MapModal = () => {
|
||||
return (
|
||||
<div
|
||||
className={`relative max-md:aspect-[360/640] md:max-2xl:aspect[1/1] max-2xl:mb-4 overflow-clip rounded-[4.444vw] h-[calc(100dvh-100px)]`}
|
||||
>
|
||||
<GoogleMap
|
||||
mapCenter={mapCenter}
|
||||
markers={markers}
|
||||
mobileActive={true}
|
||||
mobile={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="md:hidden relative flex flex-col">
|
||||
{
|
||||
<div
|
||||
className={` relative max-md:aspect-[328/328] scroll-mt-10 md:max-2xl:aspect[1/1] transition-all max-2xl:mb-4 overflow-clip rounded-[4.444vw]`}
|
||||
>
|
||||
<GoogleMap
|
||||
mapCenter={mapCenter}
|
||||
markers={markers}
|
||||
mobileActive={false}
|
||||
mobile={true}
|
||||
/>
|
||||
<Button
|
||||
className={`absolute size-[11.111vw] right-[1.111vw] bottom-[1.111vw] `}
|
||||
onClick={() => setMapActive(true)}
|
||||
>
|
||||
<div className="size-[5.556vw] text-[#0D1922]">
|
||||
<FullScreenIcon />
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
{/*
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="flex flex-nowrap overflow-x-scroll scroll-pl-4 gap-x-[16px] overflow-y-hidden justify-start snap-x snap-mandatory [&::-webkit-scrollbar]:h-[1.111vw] [&::-webkit-scrollbar]:w-[none] [&::-webkit-scrollbar-thumb]:bg-[#FFFFFF] [&::-webkit-scrollbar-thumb]:w-4 [&::-webkit-scrollbar-thumb]:rounded-full -mx-4 px-4 "
|
||||
>
|
||||
{marasiDriveMapCards.map((card, index) => (
|
||||
<div key={index} className="snap-start">
|
||||
<div
|
||||
className={`rounded-[6.667vw] px-[4.444vw] py-[3.333vw] w-[51.111vw] aspect-[184/122] bg-[#F3F3F2] flex-shrink-0 flex flex-col justify-between relative md:max-2xl:w-[25vw] md:max-2xl:rounded-[3.125vw] md:max-2xl:px-[2.083vw] md:max-2xl:py-[1.563vw]`}
|
||||
>
|
||||
<div className="space-y-[0.278vw]">
|
||||
<p className="text-m">{card.title}</p>
|
||||
<p className="text-s text-[#73787C]">{`${card.mins} mins`}</p>
|
||||
</div>
|
||||
<img
|
||||
src={card.image}
|
||||
className="rounded-[0.278vw] size-[13.333vw] object-cover absolute bottom-[4.444vw] right-[3.333vw] md:max-2xl:size-[6.25vw] md:max-2xl:bottom-[1.563vw] md:max-2xl:right-[2.083vw]"
|
||||
alt={card.title}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<CustomScrollBar
|
||||
containerRef={containerRef}
|
||||
inlinePadding={16}
|
||||
trackStyle="min-2xl:hidden max-2xl:translate-y-5"
|
||||
thumbStyle="min-2xl:hidden"
|
||||
/> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GoogleMapMobile;
|
||||
@@ -0,0 +1,67 @@
|
||||
import { AdvancedMarker, useMap } from "@vis.gl/react-google-maps";
|
||||
import type { Marker } from "@googlemaps/markerclusterer";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import IGMapPoi from "../../types/IGMapPoi";
|
||||
|
||||
interface IGMapMarker {
|
||||
markerKey: number;
|
||||
poi: IGMapPoi;
|
||||
setMarkerRef: (marker: Marker | null, key: string) => void | undefined;
|
||||
children: React.ReactNode;
|
||||
isSelected: boolean;
|
||||
setSelectedMarker: React.Dispatch<React.SetStateAction<number | null>>;
|
||||
}
|
||||
|
||||
export default function MapMarker({
|
||||
poi,
|
||||
children,
|
||||
markerKey,
|
||||
setMarkerRef,
|
||||
isSelected,
|
||||
setSelectedMarker,
|
||||
}: IGMapMarker) {
|
||||
const map = useMap();
|
||||
const { location, ignoreClusterization, label } = poi;
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const markerRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ignoreClusterization) {
|
||||
setMarkerRef(markerRef.current, markerKey.toString());
|
||||
}
|
||||
}, [ignoreClusterization, markerKey, setMarkerRef]);
|
||||
|
||||
return (
|
||||
<AdvancedMarker
|
||||
key={markerKey}
|
||||
position={location}
|
||||
ref={markerRef}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
onClick={() => {
|
||||
map?.panTo({ lat: location.lat, lng: location.lng + 0.001 }); // 0.001 is a small align to fit long labels (generally for mobile devices)
|
||||
map?.setZoom(17);
|
||||
setSelectedMarker(markerKey);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={`relative flex items-center gap-x-2 ${
|
||||
isHovered || isSelected ? "[&>.gmap-img-container]:z-150" : ""
|
||||
}`}
|
||||
>
|
||||
{children}
|
||||
{label && (
|
||||
<div
|
||||
className={`label-container text-black absolute text-s left-0 opacity-0 w-max pointer-events-none transition-[left,opacity] bg-white pl-11 pr-3 py-2.5 rounded-2xl ${
|
||||
isHovered || isSelected
|
||||
? "opacity-100 left-[calc(100%-38px)] pointer-events-auto z-140"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
{label}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</AdvancedMarker>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const AllIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
width={21}
|
||||
height={20}
|
||||
viewBox="0 0 21 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M6.517 13.97h.028v.024zm0-7.952h.029v.023zm7.971 7.976h.029v.024zm-.029-7.976h.029v.023z"
|
||||
stroke="currentColor"
|
||||
strokeWidth={3}
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default AllIcon;
|
||||
@@ -0,0 +1,19 @@
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const EntertainmentIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
width={21}
|
||||
height={20}
|
||||
viewBox="0 0 21 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M7.49 6.07a.1.1 0 0 1 .186 0l1.188 3.01a.1.1 0 0 0 .056.057l3.01 1.187a.1.1 0 0 1 0 .186l-3.01 1.188a.1.1 0 0 0-.056.056l-1.188 3.01a.1.1 0 0 1-.186 0l-1.187-3.01a.1.1 0 0 0-.056-.056L3.236 10.51a.1.1 0 0 1 0-.186l3.01-1.187a.1.1 0 0 0 .057-.056zm7.13 4.882a.05.05 0 0 1 .093 0l.77 1.954a.05.05 0 0 0 .029.028l1.953.77a.05.05 0 0 1 0 .093l-1.953.77a.05.05 0 0 0-.028.029l-.77 1.953a.05.05 0 0 1-.094 0l-.77-1.953a.05.05 0 0 0-.028-.028l-1.954-.77a.05.05 0 0 1 0-.094l1.954-.77a.05.05 0 0 0 .028-.028zm-1.25-8.334a.05.05 0 0 1 .093 0l.77 1.954a.05.05 0 0 0 .029.028l1.953.77a.05.05 0 0 1 0 .093l-1.953.77a.05.05 0 0 0-.028.029l-.77 1.953a.05.05 0 0 1-.094 0l-.77-1.953a.05.05 0 0 0-.028-.028l-1.954-.77a.05.05 0 0 1 0-.094l1.954-.77a.05.05 0 0 0 .028-.028z"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default EntertainmentIcon;
|
||||
@@ -0,0 +1,19 @@
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const HotelIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
width={21}
|
||||
height={20}
|
||||
viewBox="0 0 21 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M15.834 10.247h.333a1 1 0 0 1 1 1v2.19a1 1 0 0 1-1 1H4.834a1 1 0 0 1-1-1v-2.19a1 1 0 0 1 1-1h.333m10.667 0V6.882c0-.579-.448-1.048-1-1.048H10.5m5.334 4.413H10.5m-5.333 0V6.882c0-.579.448-1.048 1-1.048H10.5m-5.333 4.413H10.5m-5.833 4.19v1.397m11.667-1.397v1.397M10.5 10.247V5.834"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default HotelIcon;
|
||||
@@ -0,0 +1,20 @@
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const MallsIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
width={21}
|
||||
height={20}
|
||||
viewBox="0 0 21 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M8 6.406H6.5a1 1 0 0 0-1 1v7.428a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V7.406a1 1 0 0 0-1-1H13m-5 0v-.572a2.5 2.5 0 0 1 2.5-2.5v0a2.5 2.5 0 0 1 2.5 2.5v.572m-5 0h5"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="square"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default MallsIcon;
|
||||
@@ -0,0 +1,21 @@
|
||||
import { SVGProps } from "react";
|
||||
|
||||
const OtherIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
width={21}
|
||||
height={20}
|
||||
viewBox="0 0 21 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M10.5 4.166v11.667M5.45 7.086l10.104 5.834m-10.1 0 10.103-5.833"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default OtherIcon;
|
||||
@@ -0,0 +1,10 @@
|
||||
export default function PinIcon() {
|
||||
return (
|
||||
<svg fill="none" width={16} height={16}>
|
||||
<path
|
||||
d="M6.134 10.5a1 1 0 0 0 1.732 0l5.196-9a1 1 0 0 0-.866-1.5H1.804a1 1 0 0 0-.866 1.5z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/* eslint-disable no-loss-of-precision */
|
||||
import IGMapPoi from "../types/IGMapPoi";
|
||||
import MarasiMarker from "../../public/images/search/rove_home_marasi_drive.png";
|
||||
import MarinaMarker from "../../public/images/search/rove_home_dubai_marina.png";
|
||||
import { marasiDriveMapCards } from "./aboutMarasiDrive";
|
||||
import PinIcon from "../components/icons/map/PinIcon";
|
||||
|
||||
function GetMarkerHTML(name: string, primary: boolean = false) {
|
||||
const src = !primary
|
||||
? marasiDriveMapCards.find((item) => item.title === name)?.image
|
||||
: name;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`gmap-img-container z-20 overflow-hidden rounded-full flex justify-center items-center border-2 border-white ${
|
||||
!primary ? "size-8" : "size-14"
|
||||
}`}
|
||||
>
|
||||
<img src={src} className="" alt="" />
|
||||
</div>
|
||||
{/* ADD POINTER FOR PRIMARY POINTS */}
|
||||
{primary && (
|
||||
<div className=" size-4 mx-auto text-white absolute -bottom-[20px] left-1/2 -translate-x-1/2">
|
||||
<PinIcon />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const GoogleMapData: {
|
||||
marasiDriveDefaultCenter: { lat: number; lng: number };
|
||||
dubaiMarinaDefaultCenter: { lat: number; lng: number };
|
||||
markers: IGMapPoi[];
|
||||
} = {
|
||||
marasiDriveDefaultCenter: {
|
||||
lat: 25.183476007744233,
|
||||
lng: 55.274782084720286,
|
||||
},
|
||||
dubaiMarinaDefaultCenter: {
|
||||
lat: 25.069466431595334,
|
||||
lng: 55.128736429300375,
|
||||
},
|
||||
markers: [
|
||||
{
|
||||
//Rove Dubai Marina Hotel
|
||||
location: { lat: 25.069466431595334, lng: 55.128736429300375 },
|
||||
type: "Hotels",
|
||||
ignoreClusterization: true,
|
||||
customMarker: GetMarkerHTML(MarinaMarker, true),
|
||||
clickableAreaScale: 3,
|
||||
label: "Rove Dubai Marina Hotel",
|
||||
},
|
||||
{
|
||||
location: { lat: 25.181371868546396, lng: 55.27515332907251 },
|
||||
type: "Hotels",
|
||||
ignoreClusterization: true,
|
||||
customMarker: GetMarkerHTML(MarasiMarker, true),
|
||||
clickableAreaScale: 3,
|
||||
label: "Rove Marasi Drive Hotel",
|
||||
},
|
||||
// HOTELS
|
||||
{
|
||||
location: { lat: 25.197249056658244, lng: 55.27438894800352 },
|
||||
type: "Hotels",
|
||||
customMarker: GetMarkerHTML("Burj Khalifa"),
|
||||
label: "Burj Khalifa",
|
||||
},
|
||||
{
|
||||
location: { lat: 25.195237825881613, lng: 55.27528478028406 },
|
||||
type: "Hotels",
|
||||
customMarker: GetMarkerHTML("Dubai Fountain"),
|
||||
label: "The Dubai Fountain",
|
||||
},
|
||||
{
|
||||
location: { lat: 25.197748564692787, lng: 55.2802703666763 },
|
||||
type: "Hotels",
|
||||
customMarker: GetMarkerHTML("Dubai Mall"),
|
||||
label: "Dubai Mall",
|
||||
},
|
||||
{
|
||||
location: { lat: 25.195811139364157, lng: 55.27207211658565 },
|
||||
type: "Hotels",
|
||||
customMarker: GetMarkerHTML("Dubai Opera"),
|
||||
label: "Dubai Opera",
|
||||
},
|
||||
{
|
||||
location: { lat: 25.18774242603455, lng: 55.27024382999044 },
|
||||
type: "Hotels",
|
||||
customMarker: GetMarkerHTML("Marasi Promenade"),
|
||||
label: "Marasi Promenade",
|
||||
},
|
||||
{
|
||||
location: { lat: 25.203319687345175, lng: 55.279495993292876 },
|
||||
type: "Hotels",
|
||||
customMarker: GetMarkerHTML("Rove Downtown Hotel"),
|
||||
label: "Rove Downtown Hotel",
|
||||
},
|
||||
{
|
||||
location: { lat: 25.20535369013859, lng: 55.26803667795135 },
|
||||
type: "Hotels",
|
||||
customMarker: GetMarkerHTML("Rove City Walk Hotel"),
|
||||
label: "Rove City Walk Hotel",
|
||||
},
|
||||
{
|
||||
location: { lat: 25.20768362042376, lng: 55.26248238220776 },
|
||||
type: "Hotels",
|
||||
customMarker: GetMarkerHTML("City Walk"),
|
||||
label: "City Walk",
|
||||
},
|
||||
{
|
||||
location: { lat: 25.204504325496778, lng: 55.265693333774536 },
|
||||
type: "Hotels",
|
||||
customMarker: GetMarkerHTML("Coca-Cola Arena"),
|
||||
label: "Coca-Cola Arena",
|
||||
},
|
||||
{
|
||||
location: { lat: 25.26087724994889, lng: 55.372561586751665 },
|
||||
type: "Hotels",
|
||||
customMarker: GetMarkerHTML("Dubai International Airport"),
|
||||
label: "Dubai International Airport",
|
||||
},
|
||||
],
|
||||
};
|
||||
+9
-6
@@ -1,6 +1,6 @@
|
||||
// Initialize Eruda for mobile debugging in development
|
||||
if (import.meta.env.DEV) {
|
||||
import('eruda').then(eruda => eruda.default.init());
|
||||
import("eruda").then((eruda) => eruda.default.init());
|
||||
}
|
||||
|
||||
import "./index.css";
|
||||
@@ -24,6 +24,7 @@ import TestPage from "./pages/TestPage.tsx";
|
||||
import UnitPage from "./pages/UnitPage.tsx";
|
||||
import PopupContainer from "./components/PopupContainer.tsx";
|
||||
import VirtualTourPage from "./pages/VirtualTourPage.tsx";
|
||||
import { APIProvider } from "@vis.gl/react-google-maps";
|
||||
|
||||
const route = createBrowserRouter([
|
||||
{
|
||||
@@ -88,10 +89,12 @@ const route = createBrowserRouter([
|
||||
|
||||
createRoot(document.getElementById("root")!).render(
|
||||
<>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RouterProvider router={route} />
|
||||
<PopupContainer />
|
||||
<ModalContainer />
|
||||
</QueryClientProvider>
|
||||
<APIProvider apiKey={import.meta.env.VITE_GOOGLE_MAP_API_KEY}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RouterProvider router={route} />
|
||||
<PopupContainer />
|
||||
<ModalContainer />
|
||||
</QueryClientProvider>
|
||||
</APIProvider>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export default interface IGMapPoi {
|
||||
location: google.maps.LatLngLiteral;
|
||||
ignoreClusterization?:boolean;
|
||||
type:"All" | "Hotels" | "Malls" | "Entertainment"| "Other";
|
||||
|
||||
// Scale multiplyer for Pin (MapMarker.tsx) component. (To match visible marker size).
|
||||
clickableAreaScale?:number;
|
||||
label?:string;
|
||||
customMarker?: React.ReactNode;
|
||||
}
|
||||
Reference in New Issue
Block a user