This commit is contained in:
2024-01-30 12:24:54 +05:00
parent f79aa0ce97
commit de6fc691e7
19 changed files with 310 additions and 286 deletions
+12 -6
View File
@@ -1,14 +1,20 @@
import { isMobile } from "react-device-detect";
import { useEffect } from "react";
import "./App.css";
import { useVilla } from "./hooks/useVilla";
import Desktop from "./pages/Desktop/Desktop";
import Apartment from "./pages/Mobile/Apartment";
// import MapComponent from "./components/Map/Map";
import { MapComponent } from "./components/Map/Map";
import Mobile from "./pages/Mobile/Mobile";
import useStore from "./store/store";
function App() {
return <>{isMobile ? <Apartment /> : <Desktop />}</>;
return <>{/* <MapComponent /> */}</>;
const { villa } = useVilla();
const { setCurrentVilla } = useStore();
useEffect(() => {
setCurrentVilla(villa);
}, [setCurrentVilla, villa]);
return <>{isMobile ? <Mobile /> : <Desktop />}</>;
}
export default App;
+2 -2
View File
@@ -38,9 +38,9 @@ const ViewToggle = ({ offset, isDesktop }: ViewSwitcherProps) => {
<div
className={`${
isDesktop ? "" : "border-2"
} even bg-white rounded-[32px] flex text-sm justify-center w-fit transition-all duration-300 ease-in-out select-none cursor-pointer`}
} even bg-white rounded-[32px] flex text-sm justify-center w-fit transition-all duration-300 ease-in-out select-none cursor-pointer mx-auto`}
style={{
opacity: offset ? offset : 1,
opacity: offset,
pointerEvents: `${offset === 0 ? "none" : "auto"}`,
}}
>
+10 -10
View File
@@ -1,33 +1,33 @@
import { Parameters } from "../../types/appartment";
import useStore from "../../store/store";
type ParameterDescriptionProps = {
params: Parameters;
};
const ParameterDescription = () => {
const { currentVilla } = useStore();
const ParameterDescription = ({ params }: ParameterDescriptionProps) => {
return (
<div className="flex py-6 gap-6 items-center justify-center border">
<h2 className="text-[#050409] font-medium text-2xl">{params.type}</h2>
<h2 className="text-[#050409] font-medium text-2xl">
{currentVilla && currentVilla.type}
</h2>
<div className="h-8 bg-[#DDD7D6] w-[1px]" />
<div className="flex flex-col">
<div className="flex gap-4 justify-between">
<div className="text-[#666668]">Villa Theme</div>
<div>{params.villaTheme}</div>
<div>{currentVilla && currentVilla.villaTheme}</div>
</div>
<div className="flex gap-4">
<div className="text-[#666668]">Total no. of Bedrooms</div>
<div>{params.totalCountBedroms}</div>
<div>{currentVilla && currentVilla.totalCountBedroms}</div>
</div>
</div>
<div className="h-8 bg-[#DDD7D6] w-[1px]" />
<div className="flex flex-col">
<div className="flex gap-4 justify-between">
<div className="text-[#666668]">Plot area</div>
<div>{params.plotArea}</div>
<div>{currentVilla && currentVilla.plotArea}</div>
</div>
<div className="flex gap-4">
<div className="text-[#666668]">Total Build up Area</div>
<div>{params.totalBuildUpArea}</div>
<div>{currentVilla && currentVilla.totalBuildUpArea}</div>
</div>
</div>
</div>
@@ -1,4 +1,3 @@
import { A1MViewParams } from "../../../consts/viewParams";
import CrossIcon from "../../../icons/CrossIcon";
import useStore from "../../../store/store";
import ButtonPanel from "./ButtonPanel";
@@ -8,7 +7,7 @@ const HelpPanel = () => {
const { setPanel, setModal } = useStore();
const handleOnClose = () => {
setModal(<ViewControllerModal parameters={A1MViewParams} />);
setModal(<ViewControllerModal />);
setPanel(<ButtonPanel />);
};
@@ -1,15 +1,18 @@
import { Parameters as ParametersType } from "../../../types/appartment";
import useStore from "../../../store/store";
type ImageSliderProps = {
parameters: ParametersType;
};
const ImageSlider = ({ parameters }: ImageSliderProps) => {
const ImageSlider = () => {
const { currentVilla } = useStore();
return (
<div className="flex p-6 gap-2 overflow-x-scroll">
{parameters.perspectiveWorkings.map((working) => (
<img className="rounded-lg" src={working} alt="" key={working} />
))}
{currentVilla &&
currentVilla.perspectiveWorkings.map((working) => (
<img
className="rounded-lg"
src={working.image}
alt=""
key={working.id}
/>
))}
</div>
);
};
@@ -1,13 +1,16 @@
import { useState } from "react";
import { useSwipeable } from "react-swipeable";
import { SliderType } from "../../../types/appartment";
import useStore from "../../../store/store";
type LayoutSliderProps = {
sliders: SliderType[];
};
const titleViews = [
{ id: 1, value: "Ground Floor" },
{ id: 2, value: "First Floor" },
{ id: 3, value: "Parking" },
];
const LayoutSlider = ({ sliders }: LayoutSliderProps) => {
const LayoutSlider = () => {
const [offset, setOffset] = useState(0);
const { currentVilla } = useStore();
const handleOnRight = () => {
if (offset < 0) {
@@ -16,7 +19,7 @@ const LayoutSlider = ({ sliders }: LayoutSliderProps) => {
};
const handleOnLeft = () => {
if (offset > 1 - sliders.length) {
if (currentVilla?.sliders && offset > 1 - currentVilla.sliders.length) {
setOffset((prev) => prev - 1);
}
};
@@ -34,9 +37,12 @@ const LayoutSlider = ({ sliders }: LayoutSliderProps) => {
transform: `translateY(${offset * 28}px)`,
}}
>
{sliders.map(({ title }) => (
<h2 key={title} className="h-7 font-medium text-[16px] text-center">
{title}
{titleViews.map((title) => (
<h2
key={title.id}
className="h-7 font-medium text-[16px] text-center"
>
{title.value}
</h2>
))}
</div>
@@ -49,26 +55,28 @@ const LayoutSlider = ({ sliders }: LayoutSliderProps) => {
transform: `translateX(${offset * 100}vw)`,
}}
>
{sliders.map((slider) => (
<div
className={`h-full min-w-full flex flex-col px-6 transition-all duration-300`}
key={slider.title}
>
<img src={slider.image} alt="" />
</div>
))}
{currentVilla?.sliders &&
currentVilla.sliders.map((slider) => (
<div
className={`h-full min-w-full flex flex-col px-6 transition-all duration-300`}
key={slider.id}
>
<img src={slider.image} alt="" />
</div>
))}
</div>
</div>
<div className="flex justify-center pb-4">
{sliders.map(({ title }, index) => (
<div className="p-1" key={title}>
<div
className={`w-2 h-2 rounded-full transition-all duration-300 ${
0 - index === offset ? "bg-[#050409]" : "bg-[#DDD7D6]"
}`}
></div>
</div>
))}
{currentVilla?.sliders &&
currentVilla.sliders.map((slider, index) => (
<div className="p-1" key={slider.id}>
<div
className={`w-2 h-2 rounded-full transition-all duration-300 ${
0 - index === offset ? "bg-[#050409]" : "bg-[#DDD7D6]"
}`}
></div>
</div>
))}
</div>
</div>
);
+12 -10
View File
@@ -1,30 +1,32 @@
import { Parameters } from "../../../types/appartment";
import useStore from "../../../store/store";
type ParametersProps = {
parameters: Parameters;
};
const Parameters = () => {
const { currentVilla } = useStore();
const Parameters = ({ parameters }: ParametersProps) => {
return (
<div className="p-6 border-b">
<h2 className="text-xl font-medium ">Parameters</h2>
<div className="flex flex-col gap-2 pt-4">
<div className="flex justify-between gap-4">
<div className="w-1/2 text-sm text-[#666668] font-medium">Type</div>
<div className="w-1/2 text-sm font-medium">{parameters.type}</div>
<div className="w-1/2 text-sm font-medium">
{currentVilla && currentVilla.type}
</div>
</div>
<div className="flex justify-between gap-4">
<div className="w-1/2 text-sm text-[#666668] font-medium">
Plot area
</div>
<div className="w-1/2 text-sm font-medium">{parameters.plotArea}</div>
<div className="w-1/2 text-sm font-medium">
{currentVilla && currentVilla.plotArea}
</div>
</div>
<div className="flex justify-between gap-4">
<div className="w-1/2 text-sm text-[#666668] font-medium">
Total Build up Area
</div>
<div className="w-1/2 text-sm font-medium">
{parameters.totalBuildUpArea}
{currentVilla && currentVilla.totalBuildUpArea}
</div>
</div>
<div className="flex justify-between gap-4">
@@ -32,7 +34,7 @@ const Parameters = ({ parameters }: ParametersProps) => {
Total no. of Bedrooms
</div>
<div className="w-1/2 text-sm font-medium">
{parameters.totalCountBedroms}
{currentVilla && currentVilla.totalCountBedroms}
</div>
</div>
<div className="flex justify-between gap-4">
@@ -40,7 +42,7 @@ const Parameters = ({ parameters }: ParametersProps) => {
Villa Theme
</div>
<div className="w-1/2 text-sm font-medium">
{parameters.villaTheme}
{currentVilla && currentVilla.villaTheme}
</div>
</div>
</div>
@@ -1,7 +1,6 @@
import { useState } from "react";
import useStore from "../../../store/store";
import ViewControllerModal from "./ViewControllerModal";
import { A1MViewParams } from "../../../consts/viewParams";
import ButtonPanel from "./ButtonPanel";
import { popups } from "../../../consts/popups";
@@ -14,7 +13,7 @@ const PopupModal = () => {
};
const handleOnComplete = () => {
setModal(<ViewControllerModal parameters={A1MViewParams} />);
setModal(<ViewControllerModal />);
setPanel(<ButtonPanel />);
};
@@ -1,20 +1,14 @@
import { useEffect, useState } from "react";
import { SwipeEventData, useSwipeable } from "react-swipeable";
import ButtonSwipperIcon from "../../../icons/ButtonSwipperIcon";
import { Parameters as ParametersType } from "../../../types/appartment";
// import { Parameters as ParametersType } from "../../../types/appartment";
import Parameters from "./Parameters";
// import LayoutSlider from "./LayoutSlider";
// import LayoutSlider from "./ImageSlider";
import LayoutSlider from "./LayoutSlider";
import ImageSlider from "./ImageSlider";
import ViewToggle from "../../ViewToggle";
type ViewControllerModalProps = {
parameters: ParametersType;
};
const ViewControllerModal = ({ parameters }: ViewControllerModalProps) => {
const { sliders } = parameters;
const ViewControllerModal = () => {
// const { sliders } = parameters;
const [offset, setOffset] = useState(1);
const [isTouchable, setIsTouchable] = useState(true);
const [scrollY, setScrollY] = useState(0);
@@ -69,9 +63,9 @@ const ViewControllerModal = ({ parameters }: ViewControllerModalProps) => {
className="h-[calc(100vh-110px)] overflow-y-scroll relative"
onScroll={handleOnScroll}
>
<LayoutSlider sliders={sliders} />
<Parameters parameters={parameters} />
<ImageSlider parameters={parameters} />
<LayoutSlider />
<Parameters />
<ImageSlider />
</div>
<div className="px-6 py-4 mt-auto border">
<button
-29
View File
@@ -1,29 +0,0 @@
import { Parameters } from "../types/appartment";
const A1MViewParams: Parameters = {
type: "A1M",
plotArea: "1080 Sq.m",
totalBuildUpArea: "472 Sq.m",
totalCountBedroms: 5,
villaTheme: "Modern",
sliders: [
{
title: "Ground Floor",
image: "/images/apartment/A1M/A1M_GF.png",
},
{
title: "First Floor",
image: "/images/apartment/A1M/A1M_1F.png",
},
{
title: "Parking",
image: "/images/apartment/A1M/A1M_P.png",
},
],
perspectiveWorkings: [
"/images/apartment/A1M/perspectiveWorking/009_Villa_A1MO_CAM_01_R05.jpg",
"/images/apartment/A1M/perspectiveWorking/009_Villa_A1MO_CAM_02_R05.jpg",
],
};
export { A1MViewParams };
+37
View File
@@ -0,0 +1,37 @@
import { Villa } from "../types/appartment";
const villaA1M: Villa = {
type: "A1M",
plotArea: "1080 Sq.m",
totalBuildUpArea: "472 Sq.m",
totalCountBedroms: 5,
villaTheme: "Modern",
sliders: [
{
id: 1,
image: "/images/apartment/A1M/A1M_GF.png",
},
{
id: 2,
image: "/images/apartment/A1M/A1M_1F.png",
},
{
id: 3,
image: "/images/apartment/A1M/A1M_P.png",
},
],
perspectiveWorkings: [
{
id: 1,
image: "/images/apartment/A1M/perspectiveWorking/009_Villa_A1MO_CAM_01_R05.jpg",
},
{
id: 2,
image: "/images/apartment/A1M/perspectiveWorking/009_Villa_A1MO_CAM_02_R05.jpg",
},
],
};
const villas =[villaA1M]
export { villaA1M, villas };
+15
View File
@@ -0,0 +1,15 @@
import { useParams } from "react-router-dom";
import { villas } from "../consts/villas";
import { villaA1M } from "../consts/villas";
function useVilla() {
const { villaTitle } = useParams();
const currentVilla = villas.find((villa) => villa.type === villaTitle);
if (!currentVilla) {
return { villa: villaA1M };
}
return { villa: currentVilla };
}
export { useVilla };
+1 -1
View File
@@ -5,7 +5,7 @@ import "./index.css";
const router = createBrowserRouter([
{
path: "/",
path: "/villa/:villaTitle",
element: <App />,
},
]);
-72
View File
@@ -1,72 +0,0 @@
import { Unity, useUnityContext } from "react-unity-webgl";
import { useEffect } from "react";
import { ReactUnityEventParameter } from "react-unity-webgl/distribution/types/react-unity-event-parameters";
import useStore from "../../store/store";
import LoaderModal from "../../components/LoaderModal";
import ButtonPanel from "../../components/desktop/ButtonPanel";
import ParameterDescription from "../../components/desktop/ParameterDescription";
import { A1MViewParams } from "../../consts/viewParams";
import LayoutsButtonContainer from "../../components/desktop/LayoutsButtonContainer";
const Apartment = () => {
const { loader, setLoader, setSendMessageToUnity } = useStore();
const {
unityProvider,
isLoaded,
sendMessage,
addEventListener,
removeEventListener,
} = useUnityContext({
loaderUrl: "builds/estate-webgl.loader.js",
dataUrl: "builds/estate-webgl.data.unityweb",
frameworkUrl: "builds/estate-webgl.framework.js.unityweb",
codeUrl: "builds/estate-webgl.wasm.unityweb",
streamingAssetsUrl: "StreamingAssets",
});
const handleSetLoaded = (isSceneLoaded: ReactUnityEventParameter) => {
if (isSceneLoaded === 0) {
setLoader(<LoaderModal />);
} else {
setLoader(null);
}
};
useEffect(() => {
addEventListener("SetLoaded", handleSetLoaded);
return () => {
removeEventListener("SetLoaded", handleSetLoaded);
};
}, []);
useEffect(() => {
if (!isLoaded) {
setLoader(<LoaderModal />);
} else {
setLoader(null);
setSendMessageToUnity(sendMessage);
sendMessage("LevelSwitcher", "LoadSceneSingle", "Outdoor/A1");
}
}, [isLoaded]);
return (
<>
{loader}
<div className="pt-[101px] pb-[63px] px-[215px] relative">
<div className="relative">
<ButtonPanel />
<Unity
unityProvider={unityProvider}
style={{ width: "100%", height: "100%" }}
/>
<LayoutsButtonContainer />
</div>
<ParameterDescription params={A1MViewParams} />
</div>
</>
);
};
export default Apartment;
+66 -2
View File
@@ -1,7 +1,71 @@
import Apartment from "./Apartment";
import { Unity, useUnityContext } from "react-unity-webgl";
import { useEffect } from "react";
import { ReactUnityEventParameter } from "react-unity-webgl/distribution/types/react-unity-event-parameters";
import useStore from "../../store/store";
import LoaderModal from "../../components/LoaderModal";
import ButtonPanel from "../../components/desktop/ButtonPanel";
import ParameterDescription from "../../components/desktop/ParameterDescription";
import LayoutsButtonContainer from "../../components/desktop/LayoutsButtonContainer";
const Desktop = () => {
return <Apartment />;
const { loader, setLoader, setSendMessageToUnity } = useStore();
const {
unityProvider,
isLoaded,
sendMessage,
addEventListener,
removeEventListener,
} = useUnityContext({
loaderUrl: "builds/estate-webgl.loader.js",
dataUrl: "builds/estate-webgl.data.unityweb",
frameworkUrl: "builds/estate-webgl.framework.js.unityweb",
codeUrl: "builds/estate-webgl.wasm.unityweb",
streamingAssetsUrl: "StreamingAssets",
});
const handleSetLoaded = (isSceneLoaded: ReactUnityEventParameter) => {
if (isSceneLoaded === 0) {
setLoader(<LoaderModal />);
} else {
setLoader(null);
}
};
useEffect(() => {
addEventListener("SetLoaded", handleSetLoaded);
return () => {
removeEventListener("SetLoaded", handleSetLoaded);
};
}, []);
useEffect(() => {
if (!isLoaded) {
setLoader(<LoaderModal />);
} else {
setLoader(null);
setSendMessageToUnity(sendMessage);
sendMessage("LevelSwitcher", "LoadSceneSingle", "Outdoor/A1");
}
}, [isLoaded]);
return (
<>
{loader}
<div className="pt-[101px] pb-[63px] px-[215px] relative">
<div className="relative">
<ButtonPanel />
<Unity
unityProvider={unityProvider}
style={{ width: "100%", height: "100%" }}
/>
<LayoutsButtonContainer />
</div>
<ParameterDescription />
</div>
</>
);
};
export default Desktop;
-94
View File
@@ -1,94 +0,0 @@
import { useEffect } from "react";
import { ReactUnityEventParameter } from "react-unity-webgl/distribution/types/react-unity-event-parameters";
import { Unity, useUnityContext } from "react-unity-webgl";
import useStore from "../../store/store";
import LoaderModal from "../../components/LoaderModal";
import ButtonPanel from "../../components/mobile/Appartment/ButtonPanel";
import ViewControllerModal from "../../components/mobile/Appartment/ViewControllerModal";
import { A1MViewParams } from "../../consts/viewParams";
const Apartment = () => {
const {
modal,
loader,
panel,
setCurrentView,
setModal,
setPanel,
setSendMessageToUnity,
setLoader,
} = useStore();
const {
unityProvider,
isLoaded,
sendMessage,
addEventListener,
removeEventListener,
} = useUnityContext({
loaderUrl: "builds/estate-webgl.loader.js",
dataUrl: "builds/estate-webgl.data.unityweb",
frameworkUrl: "builds/estate-webgl.framework.js.unityweb",
codeUrl: "builds/estate-webgl.wasm.unityweb",
streamingAssetsUrl: "StreamingAssets",
});
const handleSetLoaded = (isSceneLoaded: ReactUnityEventParameter) => {
if (isSceneLoaded === 0) {
setLoader(<LoaderModal />);
} else {
setModal(<ViewControllerModal parameters={A1MViewParams} />);
setLoader(null);
}
};
const handleSetView = (view: ReactUnityEventParameter) => {
setCurrentView(view as number);
};
useEffect(() => {
addEventListener("SetLoaded", handleSetLoaded);
addEventListener("SetView", handleSetView);
return () => {
removeEventListener("SetLoaded", handleSetLoaded);
removeEventListener("SetView", handleSetView);
};
}, []);
useEffect(() => {
if (!isLoaded) {
setLoader(<LoaderModal />);
} else {
setLoader(null);
sendMessage("LevelSwitcher", "LoadSceneSingle", "Outdoor/A1");
setSendMessageToUnity(sendMessage);
setModal(<ViewControllerModal parameters={A1MViewParams} />);
setPanel(<ButtonPanel />);
}
}, [
isLoaded,
sendMessage,
setLoader,
setModal,
setPanel,
setSendMessageToUnity,
]);
return (
<>
{modal}
{loader}
<div className="h-screen overflow-hidden relative">
{panel}
<Unity
unityProvider={unityProvider}
style={{ width: "100vw", height: "100vh" }}
/>
</div>
</>
);
};
export default Apartment;
+90 -2
View File
@@ -1,7 +1,95 @@
import Apartment from "./Apartment";
import { useEffect } from "react";
import { ReactUnityEventParameter } from "react-unity-webgl/distribution/types/react-unity-event-parameters";
import { Unity, useUnityContext } from "react-unity-webgl";
import useStore from "../../store/store";
import LoaderModal from "../../components/LoaderModal";
import ButtonPanel from "../../components/mobile/Appartment/ButtonPanel";
import ViewControllerModal from "../../components/mobile/Appartment/ViewControllerModal";
const Mobile = () => {
return <Apartment />;
const {
modal,
loader,
panel,
setCurrentView,
setModal,
setPanel,
currentVilla,
setSendMessageToUnity,
setLoader,
} = useStore();
const {
unityProvider,
isLoaded,
sendMessage,
addEventListener,
removeEventListener,
} = useUnityContext({
loaderUrl: "builds/estate-webgl.loader.js",
dataUrl: "builds/estate-webgl.data.unityweb",
frameworkUrl: "builds/estate-webgl.framework.js.unityweb",
codeUrl: "builds/estate-webgl.wasm.unityweb",
streamingAssetsUrl: "StreamingAssets",
});
const handleSetLoaded = (isSceneLoaded: ReactUnityEventParameter) => {
if (isSceneLoaded === 0) {
setLoader(<LoaderModal />);
} else {
setModal(<ViewControllerModal />);
setLoader(null);
}
};
const handleSetView = (view: ReactUnityEventParameter) => {
setCurrentView(view as number);
};
useEffect(() => {
addEventListener("SetLoaded", handleSetLoaded);
addEventListener("SetView", handleSetView);
return () => {
removeEventListener("SetLoaded", handleSetLoaded);
removeEventListener("SetView", handleSetView);
};
}, []);
useEffect(() => {
if (!isLoaded) {
setLoader(<LoaderModal />);
} else {
setLoader(null);
sendMessage("LevelSwitcher", "LoadSceneSingle", "Outdoor/A1");
setSendMessageToUnity(sendMessage);
setModal(<ViewControllerModal />);
setPanel(<ButtonPanel />);
}
}, [
isLoaded,
sendMessage,
setLoader,
setModal,
setPanel,
setSendMessageToUnity,
currentVilla,
]);
return (
<>
{modal}
{loader}
<div className="h-screen overflow-hidden relative">
{panel}
<Unity
unityProvider={unityProvider}
style={{ width: "100vw", height: "100vh" }}
/>
</div>
</>
);
};
export default Mobile;
+5 -1
View File
@@ -1,14 +1,16 @@
import { ReactUnityEventParameter } from "react-unity-webgl/distribution/types/react-unity-event-parameters";
import { create } from "zustand";
import { Villa } from "../types/appartment";
interface StoreType {
currentVilla: Villa | null;
loader: React.ReactNode | null;
panel: React.ReactNode | null;
modal: React.ReactNode | null;
sendMessageToUnity: ((gameObjectName: string, methodName: string, parameter?: ReactUnityEventParameter) => void) | null;
currentView: number;
setCurrentVilla: (villa: Villa) => void;
setCurrentView: (view: number) => void;
setModal: (modal: React.ReactNode | null) => void;
setPanel: (panel: React.ReactNode | null) => void;
@@ -17,12 +19,14 @@ interface StoreType {
}
const useStore = create<StoreType>((set) => ({
currentVilla: null,
modal: null,
panel: null,
sendMessageToUnity: null,
loader: null,
currentView: 1,
setCurrentVilla: (villa) => set(() => ({currentVilla: villa})),
setCurrentView: (view) => set(() => ({currentView: view})),
setModal: (modal) => set(() => ({ modal: modal })),
setPanel: (panel) => set(() => ({ panel: panel })),
+6 -6
View File
@@ -1,16 +1,16 @@
type Slider = {
title: string;
type SliderImage = {
id: number;
image: string;
}
type Parameters = {
type Villa = {
type: string;
plotArea: string;
totalBuildUpArea: string;
totalCountBedroms: number;
villaTheme: string;
sliders: Slider[];
perspectiveWorkings: string[]
sliders: SliderImage[];
perspectiveWorkings: SliderImage[]
};
type ViewSwitcher ={
@@ -19,4 +19,4 @@ type ViewSwitcher ={
unityHandler: string,
}
export type {Slider as SliderType, Parameters, ViewSwitcher}
export type {SliderImage as SliderType, Villa, ViewSwitcher}