starting dragable bottom panel

This commit is contained in:
2024-02-02 17:14:51 +05:00
parent 2f8aa5c516
commit a244e7b883
26 changed files with 379 additions and 73 deletions
+2
View File
@@ -11,6 +11,8 @@
},
"dependencies": {
"@arcgis/core": "^4.28.10",
"@react-spring/web": "^9.7.3",
"@use-gesture/react": "^10.3.0",
"react": "^18.2.0",
"react-device-detect": "^2.2.3",
"react-dom": "^18.2.0",
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,4 +1,4 @@
import BackIcon from "../../../icons/BackIcon";
import BackIcon from "../icons/BackIcon";
const BackButton = () => {
return (
+15
View File
@@ -0,0 +1,15 @@
import GoBackIcon from "../icons/GoBackIcon";
type GoBackButtonProps = {
onClick?: () => void;
};
const GoBackButton = ({ onClick }: GoBackButtonProps) => {
return (
<button onClick={onClick} className="flex items-center justify-center p-2">
<GoBackIcon />
</button>
);
};
export default GoBackButton;
+20 -12
View File
@@ -2,7 +2,11 @@ import { useEffect, useState } from "react";
import LoadingIcon from "../icons/LoadingIcon";
import { loadingMessages } from "../consts/loading";
const LoaderModal = () => {
type LoaderModalProps = {
isSimleLoader?: boolean;
};
const LoaderModal = ({ isSimleLoader = false }: LoaderModalProps) => {
const [offset, setOffset] = useState(0);
useEffect(() => {
@@ -24,18 +28,22 @@ const LoaderModal = () => {
return (
<div className="bg-[#F3F2F0] h-full w-full flex justify-center items-center flex-col fixed z-30">
<LoadingIcon className="animate-spin w-16" />
<div className="relative h-7 overflow-hidden">
<div
className="flex flex-col items-center duration-300"
style={{ transform: `translateY(${offset * 28}px)` }}
>
{loadingMessages.map((message) => (
<div className="h-7" key={message.id}>
{message.value}
</div>
))}
{!isSimleLoader ? (
<div className="relative h-7 overflow-hidden">
<div
className="flex flex-col items-center duration-300"
style={{ transform: `translateY(${offset * 28}px)` }}
>
{loadingMessages.map((message) => (
<div className="h-7" key={message.id}>
{message.value}
</div>
))}
</div>
</div>
</div>
) : (
<></>
)}
</div>
);
};
+1 -1
View File
@@ -11,7 +11,7 @@ export const MapGraphic = (graphicProperties: __esri.GraphicProperties) => {
const graphicPoint = new Graphic(graphicProperties);
setGraphic(graphicPoint);
}, []);
}, [graphicProperties]);
useEffect(() => {
if (!graphic || !graphicsLayer) return;
@@ -2,7 +2,7 @@ import useStore from "../../../store/store";
import PopupModal from "./PopupModal";
import HelpPanel from "./HelpPanel";
import HelpButton from "../../HelpButton";
import BackButton from "./BackButton";
import BackButton from "../../BackButton";
const ButtonPanel = () => {
const { setModal, setPanel } = useStore();
@@ -9,7 +9,7 @@ const Parameters = () => {
<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">
<div className="w-1/2 text-sm font-medium uppercase">
{currentVilla && currentVilla.type}
</div>
</div>
@@ -7,7 +7,6 @@ import ImageSlider from "./ImageSlider";
import ViewToggle from "../../ViewToggle";
const ViewControllerModal = () => {
// const { sliders } = parameters;
const [offset, setOffset] = useState(1);
const [isActive, setIsActive] = useState(false);
const [isTouchable, setIsTouchable] = useState(true);
@@ -54,25 +53,43 @@ const ViewControllerModal = () => {
};
const handlers = useSwipeable({
onSwiped: handleOnSwiped,
onSwiping: handleOnSwiping,
preventScrollOnSwipe: offset !== 0,
trackTouch: isTouchable,
// onSwiped: handleOnSwiped,
// onSwiping: handleOnSwiping,
onSwiping: () => {},
// onSwipedDown: () => {
// setOffset(1);
// setIsActive(false);
// },
// onSwipedUp: () => {
// setOffset(0);
// setIsActive(true);
// },
// preventScrollOnSwipe: offset !== 0,
// trackTouch: isTouchable,
});
return (
<div className="flex flex-col fixed left-0 top-0 z-20" {...handlers}>
<div
className="flex flex-col fixed left-0 top-0 z-20 select-none"
{...handlers}
>
<div
className={`${
isActive ? "rounded-ss-2xl rounded-se-2xl" : ""
!isActive ? "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))`,
transform: `${
offset === 1
? "translateY(calc(100vh - 64px))"
: "translateY(calc(0vh))"
} `,
}}
>
<ViewToggle offset={offset} />
<div className="mx-auto flex justify-center self-start w-full">
<div
className="mx-auto flex justify-center self-start w-full"
// {...handlers}
>
<ButtonSwipperIcon />
</div>
<div
@@ -0,0 +1,101 @@
import { useDrag } from "@use-gesture/react";
import { a, useSpring, config } from "@react-spring/web";
import styles from "./styles.module.css";
const items = ["save item", "open item", "share item", "delete item", "cancel"];
// const height = items.length * 60 + 80;
const height = items.length * 60 + 80;
export default function ButtomPanel() {
const [{ y }, api] = useSpring(() => ({ y: height }));
const open = ({ canceled }) => {
// when cancel is true, it means that the user passed the upwards threshold
// so we change the spring config to create a nice wobbly effect
api.start({
y: 0,
immediate: false,
config: canceled ? config.wobbly : config.stiff,
});
};
const close = (velocity = 0) => {
api.start({
y: height,
immediate: false,
config: { ...config.stiff, velocity },
});
};
const bind = useDrag(
({
last,
velocity: [, vy],
direction: [, dy],
offset: [, oy],
cancel,
canceled,
}) => {
// if the user drags up passed a threshold, then we cancel
// the drag so that the sheet resets to its open position
if (oy < -70) cancel();
// when the user releases the sheet, we check whether it passed
// the threshold for it to close, or if we reset it to its open positino
if (last) {
oy > height * 0.5 || (vy > 0.5 && dy > 0)
? close(vy)
: open({ canceled });
}
// when the user keeps dragging, we just move the sheet according to
// the cursor position
else api.start({ y: oy, immediate: true });
},
{
from: () => [0, y.get()],
filterTaps: true,
bounds: { top: 0 },
rubberband: true,
}
);
const display = y.to((py) => (py < height ? "block" : "none"));
const bgStyle = {
transform: y.to(
[0, height],
["translateY(-8%) scale(1.16)", "translateY(0px) scale(1.05)"]
),
opacity: y.to([0, height], [0.4, 1], "clamp"),
};
return (
<div className="flex" style={{ overflow: "hidden" }}>
<a.div className={styles.bg} onClick={() => close()} style={bgStyle}>
<img
src="https://images.pexels.com/photos/1239387/pexels-photo-1239387.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
alt=""
/>
<img
src="https://images.pexels.com/photos/5181179/pexels-photo-5181179.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"
alt=""
/>
</a.div>
<div className={styles.actionBtn} onClick={open} />
<a.div
className={styles.sheet}
{...bind()}
style={{ display, bottom: `calc(-100vh + ${height - 100}px)`, y }} //
>
{items.map((entry, i) => (
<div
key={entry}
onClick={() =>
i < items.length - 1 ? alert("clicked on " + entry) : close()
}
children={entry}
/>
))}
</a.div>
</div>
);
}
@@ -0,0 +1,54 @@
.actionBtn {
position: fixed;
z-index: 100;
bottom: 80px;
right: 40px;
height: 48px;
width: 48px;
border-radius: 24px;
background: coral;
box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2),
0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0);
display: flex;
align-items: center;
justify-content: center;
}
.actionBtn:after {
content: " ";
display: block;
background: #fff;
height: 20%;
width: 20%;
border-radius: 50%;
}
.bg {
width: 100%;
}
.bg > img {
width: 100%;
margin: 0;
display: block;
}
.sheet {
z-index: 100;
position: fixed;
height: calc(100vh + 100px);
width: 100vw;
border-radius: 12px 12px 0px;
background: #fff;
touch-action: none;
}
.sheet > div {
height: 60px;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
justify-content: center;
padding: 0 20px;
text-transform: capitalize;
}
@@ -1,7 +1,28 @@
const SelectHousePanel = () => {
import useStore from "../../../store/store";
import GoBackButton from "../../GoBackButton";
import HouseListModal from "./HouseListModal";
type SelectHousePanelProps = {
isWithButton?: boolean;
};
const SelectHousePanel = ({ isWithButton = false }: SelectHousePanelProps) => {
const { setModal } = useStore();
const handleOnClick = () => {
setModal(<HouseListModal />);
};
return (
<div className="bg-[#F3F2F0] flex justify-center py-4 text-lg font-medium">
Select a House
<div className="bg-[#F3F2F0] flex justify-between text-lg font-medium items-center p-2 relative">
<div
className={`${isWithButton ? "absolute left-0 top-0 p-2" : "hidden"}`}
>
<GoBackButton onClick={handleOnClick} />
</div>
<div className="mx-auto py-2">Select a House</div>
{/* <GoBackButton onClick={handleOnClick} /> */}
<div />
</div>
);
};
@@ -11,38 +11,42 @@ const SelectedHouseModal = () => {
};
return (
<div className="absolute top-[72px] z-30 w-full px-6">
<div className="flex flex-col border border-[#DDD7D6] rounded-2xl bg-white">
<div className="flex p-1 h-[107px] w-full text-[12px] font-medium gap-4">
<div className="w-full overflow-hidden rounded-[4px] rounded-ss-xl">
{selectedOnMapVilla?.perspectiveWorkings[0]?.image && (
<img
className="h-full "
src={selectedOnMapVilla.perspectiveWorkings[0].image}
alt=""
/>
)}
</div>
<div className="w-full flex flex-col justify-center gap-1 text-[#666668]">
<p>Type</p>
<p>House Size</p>
<p>Bedrooms</p>
<p>Villa Theme</p>
</div>
<div className="flex flex-col w-[82px] pr-4 justify-center ">
<div className="uppercase">{selectedOnMapVilla?.type}</div>
<div className="w-full">
{selectedOnMapVilla?.totalBuildUpArea} Sq.m
<>
{selectedOnMapVilla && (
<div className="absolute top-[72px] z-30 w-full px-6">
<div className="flex flex-col border border-[#DDD7D6] rounded-2xl bg-white">
<div className="flex p-1 h-[107px] w-full text-[12px] font-medium gap-4">
<div className="w-full overflow-hidden rounded-[4px] rounded-ss-xl">
{selectedOnMapVilla?.perspectiveWorkings[0]?.image && (
<img
className="h-full "
src={selectedOnMapVilla.perspectiveWorkings[0].image}
alt=""
/>
)}
</div>
<div className="w-full flex flex-col justify-center gap-1 text-[#666668]">
<p>Type</p>
<p>House Size</p>
<p>Bedrooms</p>
<p>Villa Theme</p>
</div>
<div className="flex flex-col w-[82px] pr-4 justify-center ">
<div className="uppercase">{selectedOnMapVilla?.type}</div>
<div className="w-full">
{selectedOnMapVilla?.totalBuildUpArea} Sq.m
</div>
<div>{selectedOnMapVilla?.totalCountBedroms}</div>
<div>{selectedOnMapVilla?.villaTheme}</div>
</div>
</div>
<div className="border border-top text-sm flex justify-center">
<KnowMoreButton onClick={handleClickOnKnowMoreBtn} />
</div>
<div>{selectedOnMapVilla?.totalCountBedroms}</div>
<div>{selectedOnMapVilla?.villaTheme}</div>
</div>
</div>
<div className="border border-top text-sm flex">
<KnowMoreButton onClick={handleClickOnKnowMoreBtn} />
</div>
</div>
</div>
)}
</>
);
};
+21
View File
@@ -0,0 +1,21 @@
const GoBackIcon = () => {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 6L8 12L14 18"
stroke="#050409"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
};
export default GoBackIcon;
+3 -1
View File
@@ -4,6 +4,7 @@ import MainPage from "./pages/MainPage";
import App from "./App";
import "./index.css";
import Layout from "./components/Layout";
import ButtomPanel from "../src/components/mobile/Appartment/ViewControllerModal1";
const router = createBrowserRouter([
{
@@ -17,7 +18,8 @@ const router = createBrowserRouter([
},
{
path: "/",
element: <MainPage />,
// element: <MainPage />,
element: <ButtomPanel />,
},
],
},
+16 -9
View File
@@ -7,6 +7,7 @@ import ButtonPanel from "../../components/mobile/Appartment/ButtonPanel";
import ViewControllerModal from "../../components/mobile/Appartment/ViewControllerModal";
import { useUnity } from "../../hooks/useUnity";
import { useVilla } from "../../hooks/useVilla";
import BottomPanel from "../../components/mobile/Appartment/ViewControllerModal1";
const Mobile = () => {
const {
@@ -17,7 +18,8 @@ const Mobile = () => {
currentVilla,
setSendMessageToUnity,
setLoader,
sendMessageToUnity,
setIs3DTour,
is3DTour,
} = useStore();
const { villa } = useVilla();
@@ -31,9 +33,10 @@ const Mobile = () => {
const handleSetLoaded = (isSceneLoaded: ReactUnityEventParameter) => {
if (isSceneLoaded === 0) {
setLoader(<LoaderModal />);
setLoader(<LoaderModal isSimleLoader />);
} else {
setModal(<ViewControllerModal />);
setLoader(null);
}
};
@@ -42,6 +45,11 @@ const Mobile = () => {
if (view === 1 || view === 2 || view === 3) {
setCurrentView(view);
}
if (view === 2 || view === 3) {
setIs3DTour(true);
} else {
setIs3DTour(false);
}
};
useEffect(() => {
@@ -54,22 +62,20 @@ const Mobile = () => {
};
}, []);
// useEffect(() => {
// setSendMessageToUnity(sendMessage);
// }, []);
useEffect(() => {
// setModal(<BottomPanel />);
if (!isLoaded) {
setLoader(<LoaderModal />);
} else {
setLoader(null);
// sendMessage("JsConnector", "SetCurrentVilla", "a1t");
// sendMessage("JsConnector", "SetOutdoor");
setSendMessageToUnity(sendMessage);
sendMessage("SceneContext", "SwitchLevel");
setModal(<ViewControllerModal />);
setPanel(<ButtonPanel />);
if (is3DTour) {
setPanel(<ButtonPanel />);
}
}
}, [
isLoaded,
@@ -80,6 +86,7 @@ const Mobile = () => {
setSendMessageToUnity,
currentVilla,
villa.type,
is3DTour,
]);
return (
+2 -3
View File
@@ -4,7 +4,6 @@ import MapComponent from "../../components/Map/Map";
import useStore from "../../store/store";
import SelectHousePanel from "../../components/mobile/MainPage/SelectHousePanel";
import HouseListModal from "../../components/mobile/MainPage/HouseListModal";
import SelectedHouseModal from "../../components/mobile/MainPage/SelectedHouseModal";
const MobileMainPage = () => {
const { setModal } = useStore();
@@ -15,12 +14,12 @@ const MobileMainPage = () => {
useEffect(() => {
setModal(<HouseListModal />);
}, []);
}, [setModal]);
return (
<>
<div className="h-screen overflow-hidden relative">
<SelectHousePanel />
<SelectHousePanel isWithButton />
<ListButton onClick={handleOnClick} className="right-4 bottom-4 z-20" />
<MapComponent />
+4
View File
@@ -10,7 +10,9 @@ interface StoreType {
modal: React.ReactNode | null;
sendMessageToUnity: ((gameObjectName: string, methodName: string, parameter?: ReactUnityEventParameter) => void) | null;
currentView: number;
is3DTour: boolean;
setIs3DTour: (is3D: boolean) => void;
setCurrentVilla: (villa: Villa) => void;
setSelectedOnMapVilla: (villa: Villa) => void;
setCurrentView: (view: number) => void;
@@ -22,6 +24,7 @@ interface StoreType {
const useStore = create<StoreType>((set) => ({
currentVilla: null,
is3DTour: false,
selectedOnMapVilla: null,
modal: null,
panel: null,
@@ -29,6 +32,7 @@ const useStore = create<StoreType>((set) => ({
loader: null,
currentView: 1,
setIs3DTour: (is3DTour) => set(() => ({is3DTour: is3DTour})),
setSelectedOnMapVilla: (villa) => set(() => ({selectedOnMapVilla: villa})),
setCurrentVilla: (villa) => set(() => ({currentVilla: villa})),
setCurrentView: (view) => set(() => ({currentView: view})),
+51
View File
@@ -315,6 +315,45 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@react-spring/animated@~9.7.3":
version "9.7.3"
resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.7.3.tgz#4211b1a6d48da0ff474a125e93c0f460ff816e0f"
integrity sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==
dependencies:
"@react-spring/shared" "~9.7.3"
"@react-spring/types" "~9.7.3"
"@react-spring/core@~9.7.3":
version "9.7.3"
resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.7.3.tgz#60056bcb397f2c4f371c6c9a5f882db77ae90095"
integrity sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==
dependencies:
"@react-spring/animated" "~9.7.3"
"@react-spring/shared" "~9.7.3"
"@react-spring/types" "~9.7.3"
"@react-spring/shared@~9.7.3":
version "9.7.3"
resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.7.3.tgz#4cf29797847c689912aec4e62e34c99a4d5d9e53"
integrity sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==
dependencies:
"@react-spring/types" "~9.7.3"
"@react-spring/types@~9.7.3":
version "9.7.3"
resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.7.3.tgz#ea78fd447cbc2612c1f5d55852e3c331e8172a0b"
integrity sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==
"@react-spring/web@^9.7.3":
version "9.7.3"
resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.7.3.tgz#d9f4e17fec259f1d65495a19502ada4f5b57fa3d"
integrity sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==
dependencies:
"@react-spring/animated" "~9.7.3"
"@react-spring/core" "~9.7.3"
"@react-spring/shared" "~9.7.3"
"@react-spring/types" "~9.7.3"
"@remix-run/router@1.14.2":
version "1.14.2"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.14.2.tgz#4d58f59908d9197ba3179310077f25c88e49ed17"
@@ -550,6 +589,18 @@
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==
"@use-gesture/core@10.3.0":
version "10.3.0"
resolved "https://registry.yarnpkg.com/@use-gesture/core/-/core-10.3.0.tgz#9afd3777a45b2a08990a5dcfcf8d9ddd55b00db9"
integrity sha512-rh+6MND31zfHcy9VU3dOZCqGY511lvGcfyJenN4cWZe0u1BH6brBpBddLVXhF2r4BMqWbvxfsbL7D287thJU2A==
"@use-gesture/react@^10.3.0":
version "10.3.0"
resolved "https://registry.yarnpkg.com/@use-gesture/react/-/react-10.3.0.tgz#180534c821fd635c2853cbcfa813f92c94f27e3f"
integrity sha512-3zc+Ve99z4usVP6l9knYVbVnZgfqhKah7sIG+PS2w+vpig2v2OLct05vs+ZXMzwxdNCMka8B+8WlOo0z6Pn6DA==
dependencies:
"@use-gesture/core" "10.3.0"
"@vitejs/plugin-react-swc@^3.3.2":
version "3.5.0"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-react-swc/-/plugin-react-swc-3.5.0.tgz#1fadff5148003e8091168c431e44c850f9a39e74"