starting dragable bottom panel
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import BackIcon from "../../../icons/BackIcon";
|
||||
import BackIcon from "../icons/BackIcon";
|
||||
|
||||
const BackButton = () => {
|
||||
return (
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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 />,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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})),
|
||||
|
||||
Reference in New Issue
Block a user