starting desktop + apartment
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
import { Outlet } from "react-router-dom";
|
||||
import BackIcon from "../icons/BackIcon";
|
||||
|
||||
const Header = () => {
|
||||
return (
|
||||
<>
|
||||
<header className="fixed left-0 top-0 px-2 w-full bg-[#ddd8d5] z-40 h-11 flex items-center justify-between">
|
||||
<BackIcon />
|
||||
<div className="items-center font-medium text-lg">A1M</div>
|
||||
<div></div>
|
||||
</header>
|
||||
{<Outlet />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
@@ -1,13 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import LoadingIcon from "../icons/LoadingIcon";
|
||||
|
||||
const loadingMessages = [
|
||||
{ id: 1, value: "looking for a villa" },
|
||||
{ id: 2, value: "building the walls" },
|
||||
{ id: 3, value: "installing the roof" },
|
||||
{ id: 4, value: "arranging the furniture" },
|
||||
{ id: 5, value: "preparing the villa for showing" },
|
||||
];
|
||||
import { loadingMessages } from "../consts/loading";
|
||||
|
||||
const LoaderModal = () => {
|
||||
const [offset, setOffset] = useState(0);
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import ButtonSwipperIcon from "../icons/ButtonSwipperIcon";
|
||||
import { Parameters as ParametersType } from "../types/appartment";
|
||||
import Parameters from "./Parameters";
|
||||
import Slider from "./Slider";
|
||||
import { SwipeEventData, useSwipeable } from "react-swipeable";
|
||||
|
||||
type ViewControllerProps = {
|
||||
parameters: ParametersType;
|
||||
};
|
||||
|
||||
const apartmentViews = [
|
||||
{ id: 1, title: "General View" },
|
||||
{ id: 2, title: "Ground Floor" },
|
||||
{ id: 3, title: "First Floor" },
|
||||
];
|
||||
|
||||
const ViewController = ({ parameters }: ViewControllerProps) => {
|
||||
const { sliders } = parameters;
|
||||
const [offset, setOffset] = useState(1);
|
||||
const [selectedViewId, setSelectedViewId] = useState(apartmentViews[0].id);
|
||||
// const [animationFrame, setAnimationFrame] = useState(0)
|
||||
|
||||
const handleOnSwiped = (eventData: SwipeEventData) => {
|
||||
if (eventData.dir === "Down") {
|
||||
setOffset(1);
|
||||
}
|
||||
if (eventData.dir === "Up") {
|
||||
// setTimeout(() => {
|
||||
setOffset(0);
|
||||
// }, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnBackClick = () => {
|
||||
setOffset(1);
|
||||
};
|
||||
|
||||
const handleOnViewClick = (viewId: number) => {
|
||||
return () => setSelectedViewId(viewId);
|
||||
};
|
||||
|
||||
const handlers = useSwipeable({
|
||||
onSwiped: handleOnSwiped,
|
||||
// onSwiping:()=>
|
||||
// onSwipeStart: () => setIsScroll(true),
|
||||
// onSwiped: () => setIsScroll(false),
|
||||
|
||||
// onSwiped: () => console.log("first"),
|
||||
// onTouchEndOrOnMouseUp: handleOnTouchEnd,
|
||||
// swipeDuration: 300,
|
||||
// preventScrollOnSwipe: true,
|
||||
// onSwipedDown: (e) => e.preventDefault(),
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="bg-white flex flex-col transition-all duration-1000 fixed left-0 bottom-28">
|
||||
<div
|
||||
className={`${
|
||||
offset === 1 ? "rounded-ss-2xl rounded-se-2xl" : ""
|
||||
} bg-white w-full h-[calc(100vh)] border flex flex-col transition-all duration-1000 fixed left-0 top-11`}
|
||||
style={{
|
||||
transform: `translateY(calc(${offset * 80}vh))`,
|
||||
}}
|
||||
>
|
||||
<div className="absolute top-[-51px] w-full h-9 px-6 bg">
|
||||
<div
|
||||
className={`even bg-white rounded-[32px] flex text-sm justify-center w-fit border-2 transition-all duration-500 mx-auto`}
|
||||
style={{
|
||||
opacity: offset,
|
||||
pointerEvents: `${offset === 0 ? "none" : "auto"}`,
|
||||
}}
|
||||
>
|
||||
{apartmentViews.map((view) => (
|
||||
<div
|
||||
onClick={handleOnViewClick(view.id)}
|
||||
key={view.id}
|
||||
className={`${
|
||||
selectedViewId === view.id ? "bg-black text-white" : ""
|
||||
} py-2 px-4 w-fit rounded-[32px] `}
|
||||
>
|
||||
{" "}
|
||||
{view.title}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto flex justify-center self-start w-full">
|
||||
<ButtonSwipperIcon />
|
||||
</div>
|
||||
<div className="h-[calc(100vh-110px)] overflow-y-scroll relative">
|
||||
<div
|
||||
className="absolute bg-black z-30 h-[30%] w-full opacity-0"
|
||||
{...handlers}
|
||||
></div>
|
||||
<Slider sliders={sliders} />
|
||||
<Parameters parameters={parameters} />
|
||||
<div className="flex p-6 gap-2 overflow-x-scroll">
|
||||
{parameters.perspectiveWorkings.map((working) => (
|
||||
<img className="rounded-lg" src={working} alt="" key={working} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-6 pt-4 pb-14 mt-auto border">
|
||||
<button
|
||||
className="border flex w-full py-3 justify-center rounded-full"
|
||||
onClick={handleOnBackClick}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
// </div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewController;
|
||||
@@ -0,0 +1,37 @@
|
||||
import BackIcon from "../../../icons/BackIcon";
|
||||
import HelpIcon from "../../../icons/HelpIcon";
|
||||
import useStore from "../../../store/store";
|
||||
import PopupModal from "./PopupModal";
|
||||
import HelpPanel from "./HelpPanel";
|
||||
|
||||
const ButtonPanel = () => {
|
||||
const { setModal, setPanel } = useStore();
|
||||
|
||||
const handleOnHelpClick = () => {
|
||||
setModal(<PopupModal />);
|
||||
setPanel(<HelpPanel />);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{
|
||||
<div className="flex w-full absolute p-4 justify-between top-0 left-0">
|
||||
<button className="flex w-fit items-center gap-1 py-[6px] pl-2 pr-4 bg-white rounded-full text-sm font-medium border border-[#C7BDBA]">
|
||||
<div className="w-5 h-5 flex items-center justify-center">
|
||||
<BackIcon className="w-[5px] h-[10px]" />
|
||||
</div>
|
||||
Back
|
||||
</button>
|
||||
<button
|
||||
className="bg-white border-[#C7BDBA] p-[6px] rounded-full"
|
||||
onClick={handleOnHelpClick}
|
||||
>
|
||||
<HelpIcon />
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ButtonPanel;
|
||||
@@ -0,0 +1,28 @@
|
||||
import { A1MViewParams } from "../../../consts/viewParams";
|
||||
import CrossIcon from "../../../icons/CrossIcon";
|
||||
import useStore from "../../../store/store";
|
||||
import ButtonPanel from "./ButtonPanel";
|
||||
import ViewControllerModal from "./ViewControllerModal";
|
||||
|
||||
const HelpPanel = () => {
|
||||
const { setPanel, setModal } = useStore();
|
||||
|
||||
const handleOnClose = () => {
|
||||
setModal(<ViewControllerModal parameters={A1MViewParams} />);
|
||||
setPanel(<ButtonPanel />);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex bg-[#F3F2F0] w-full absolute px-1 py-2 justify-between h-14 items-center">
|
||||
<div></div>
|
||||
<div className="font-medium text-lg">Control Help</div>
|
||||
<div className="flex p-2 cursor-pointer">
|
||||
<div className="flex p-2 cursor-pointer" onClick={handleOnClose}>
|
||||
<CrossIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HelpPanel;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Parameters as ParametersType } from "../../../types/appartment";
|
||||
|
||||
type ImageSliderProps = {
|
||||
parameters: ParametersType;
|
||||
};
|
||||
|
||||
const ImageSlider = ({ parameters }: ImageSliderProps) => {
|
||||
return (
|
||||
<div className="flex p-6 gap-2 overflow-x-scroll">
|
||||
{parameters.perspectiveWorkings.map((working) => (
|
||||
<img className="rounded-lg" src={working} alt="" key={working} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImageSlider;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Parameters } from "../types/appartment";
|
||||
import { Parameters } from "../../../types/appartment";
|
||||
|
||||
type ParametersProps = {
|
||||
parameters: Parameters;
|
||||
@@ -0,0 +1,59 @@
|
||||
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";
|
||||
|
||||
const PopupModal = () => {
|
||||
const [currentPopupId, setCurrentPopupId] = useState(1);
|
||||
const { setModal, setPanel } = useStore();
|
||||
|
||||
const handleOnNextClick = (popup: number) => {
|
||||
return () => setCurrentPopupId(popup + 1);
|
||||
};
|
||||
|
||||
const handleOnComplete = () => {
|
||||
setModal(<ViewControllerModal parameters={A1MViewParams} />);
|
||||
setPanel(<ButtonPanel />);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{popups.map((popup, index) => (
|
||||
<div
|
||||
key={popup.id}
|
||||
className={`absolute bottom-0 w-full p-4 transition-all duration-300 z-20 ${
|
||||
currentPopupId !== popup.id ? "hidden opacity-0" : "opacity-1"
|
||||
}`}
|
||||
>
|
||||
<div className="bg-white p-4 border rounded-2xl">
|
||||
<div className="flex gap-4">
|
||||
<div className="h-full w-10">{popup.icon}</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="text-sm pr-12">{popup.title}</div>
|
||||
{popups.length === index + 1 ? (
|
||||
<button
|
||||
onClick={handleOnComplete}
|
||||
className="text-white bg-black rounded-full px-5 py-[6px] w-fit"
|
||||
>
|
||||
Complete
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleOnNextClick(popup.id)}
|
||||
className="text-white bg-black rounded-full px-5 py-[6px] w-fit"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PopupModal;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from "react";
|
||||
import { useSwipeable } from "react-swipeable";
|
||||
import { SliderType } from "../types/appartment";
|
||||
import { SliderType } from "../../../types/appartment";
|
||||
|
||||
type SliderProps = {
|
||||
sliders: SliderType[];
|
||||
@@ -0,0 +1,87 @@
|
||||
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 from "./Parameters";
|
||||
import Slider from "./Slider";
|
||||
import ImageSlider from "./ImageSlider";
|
||||
import ViewToggle from "./ViewToggle";
|
||||
|
||||
type ViewControllerModalProps = {
|
||||
parameters: ParametersType;
|
||||
};
|
||||
|
||||
const ViewControllerModal = ({ parameters }: ViewControllerModalProps) => {
|
||||
const { sliders } = parameters;
|
||||
const [offset, setOffset] = useState(1);
|
||||
const [isTouchable, setIsTouchable] = useState(true);
|
||||
const [scrollY, setScrollY] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (offset === 1 || scrollY === 0) {
|
||||
setIsTouchable(true);
|
||||
} else {
|
||||
setIsTouchable(false);
|
||||
}
|
||||
}, [scrollY, offset]);
|
||||
|
||||
const handleOnSwiped = (eventData: SwipeEventData) => {
|
||||
if (eventData.dir === "Down") {
|
||||
setOffset(1);
|
||||
}
|
||||
if (eventData.dir === "Up") {
|
||||
setOffset(0);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnBackClick = () => {
|
||||
setOffset(1);
|
||||
};
|
||||
|
||||
const handleOnScroll = (event: React.UIEvent<HTMLDivElement, UIEvent>) => {
|
||||
setScrollY(event.currentTarget.scrollTop);
|
||||
};
|
||||
|
||||
const handlers = useSwipeable({
|
||||
onSwiped: handleOnSwiped,
|
||||
preventScrollOnSwipe: offset === 1,
|
||||
trackTouch: isTouchable,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className=" flex flex-col fixed left-0 top-0 z-20 bg-black">
|
||||
<div
|
||||
{...handlers}
|
||||
className={`${
|
||||
offset === 1 ? "rounded-ss-2xl rounded-se-2xl" : ""
|
||||
} bg-white w-full h-[calc(100vh)] border flex flex-col transition-all duration-500 fixed left-0 top-0 ease-in-out`}
|
||||
style={{
|
||||
transform: `translateY(calc(${offset * 80}vh))`,
|
||||
}}
|
||||
>
|
||||
<ViewToggle offset={offset} />
|
||||
<div className="mx-auto flex justify-center self-start w-full">
|
||||
<ButtonSwipperIcon />
|
||||
</div>
|
||||
<div
|
||||
className="h-[calc(100vh-110px)] overflow-y-scroll relative"
|
||||
onScroll={handleOnScroll}
|
||||
>
|
||||
<Slider sliders={sliders} />
|
||||
<Parameters parameters={parameters} />
|
||||
<ImageSlider parameters={parameters} />
|
||||
</div>
|
||||
<div className="px-6 py-4 mt-auto border">
|
||||
<button
|
||||
className="border flex w-full py-3 justify-center rounded-full"
|
||||
onClick={handleOnBackClick}
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewControllerModal;
|
||||
@@ -0,0 +1,79 @@
|
||||
import { useState } from "react";
|
||||
import useStore from "../../../store/store";
|
||||
|
||||
type ViewSwitcherProps = {
|
||||
offset?: number;
|
||||
isDesktop?: boolean;
|
||||
};
|
||||
|
||||
const ViewToggle = ({ offset, isDesktop }: ViewSwitcherProps) => {
|
||||
const [selectedViewId, setSelectedViewId] = useState(1);
|
||||
const { sendMessageToUnity } = useStore();
|
||||
|
||||
const handleOnFirstClick = () => {
|
||||
setSelectedViewId(1);
|
||||
if (sendMessageToUnity) {
|
||||
sendMessageToUnity("LevelSwitcher", "LoadSceneSingle", "Outdoor/A1");
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnSecondClick = () => {
|
||||
setSelectedViewId(2);
|
||||
if (sendMessageToUnity) {
|
||||
sendMessageToUnity("LevelSwitcher", "LoadSceneSingle", "Indoor/A1F1");
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnThirdClick = () => {
|
||||
setSelectedViewId(3);
|
||||
if (sendMessageToUnity) {
|
||||
sendMessageToUnity("LevelSwitcher", "LoadSceneSingle", "Indoor/A1F2");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${
|
||||
isDesktop ? "" : "absolute top-[-51px]"
|
||||
} w-full h-9 px-6 bg`}
|
||||
>
|
||||
<div
|
||||
className={`even bg-white rounded-[32px] flex text-sm justify-center w-fit border-2 transition-all duration-300 ease-in-out mx-auto select-none cursor-pointer`}
|
||||
style={{
|
||||
opacity: offset ? offset : 1,
|
||||
pointerEvents: `${offset === 0 ? "none" : "auto"}`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
onClick={handleOnFirstClick}
|
||||
className={`${
|
||||
selectedViewId === 1 ? "bg-black text-white" : ""
|
||||
} py-2 px-4 w-fit rounded-[32px] `}
|
||||
>
|
||||
{" "}
|
||||
General View
|
||||
</div>
|
||||
<div
|
||||
onClick={handleOnSecondClick}
|
||||
className={`${
|
||||
selectedViewId === 2 ? "bg-black text-white" : ""
|
||||
} py-2 px-4 w-fit rounded-[32px] `}
|
||||
>
|
||||
{" "}
|
||||
Ground Floor
|
||||
</div>
|
||||
<div
|
||||
onClick={handleOnThirdClick}
|
||||
className={`${
|
||||
selectedViewId === 3 ? "bg-black text-white" : ""
|
||||
} py-2 px-4 w-fit rounded-[32px] `}
|
||||
>
|
||||
{" "}
|
||||
First Floor
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewToggle;
|
||||
Reference in New Issue
Block a user