search page tablet version + filters modal
This commit is contained in:
@@ -35,7 +35,9 @@ const Checkbox = ({ onClick, checkbox }: CheckboxProps) => {
|
||||
</div>
|
||||
{!checkbox.disabled ? (
|
||||
<div className="bg-[#00BED7] rounded-full text-white w-6 h-6 flex items-center justify-center font-semibold text-caption-s ">
|
||||
<div className="h-fit align-middle text-center flex">8</div>
|
||||
<div className="h-fit align-middle text-center flex items-center justify-center leading-1">
|
||||
8
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-[#E2E2DC] rounded-full text-white w-6 h-6 flex items-center justify-center font-semibold text-caption-s">
|
||||
|
||||
@@ -68,7 +68,7 @@ const MultiRangeSlider = ({
|
||||
}, [multirangeSlider.endValue]);
|
||||
|
||||
return (
|
||||
<div className="px-2">
|
||||
<div className="">
|
||||
<div className="flex justify-between p-3 bg-white rounded-2xl relative flex-col">
|
||||
<div className="flex justify-between">
|
||||
<input
|
||||
|
||||
@@ -91,18 +91,21 @@ const ComplexTopPanel = () => {
|
||||
buttonType="fab"
|
||||
icon={<ActiveResizeIcon />}
|
||||
onClick={handleOnFullScreenClick}
|
||||
isCircleRounded
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
buttonType="fab"
|
||||
icon={<ResizeIcon />}
|
||||
onClick={handleOnFullScreenClick}
|
||||
isCircleRounded
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
buttonType="fab"
|
||||
icon={<HintIcon />}
|
||||
onClick={handleOnHelpClick}
|
||||
isCircleRounded
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import Navbar from "../Navbar/Navbar";
|
||||
import Location from "../Location";
|
||||
|
||||
const DesktopHeader = () => (
|
||||
<header className="bg-white w-full text-white sm:grid grid-cols-6 fixed left-0 top-0 z-50 font-usual text-m">
|
||||
<header className="bg-white w-full text-white sm:grid grid-cols-6 fixed left-0 top-0 z-[99999900] font-usual text-m">
|
||||
<div className="flex gap-4 col-span-2">
|
||||
<Logo />
|
||||
<Location />
|
||||
|
||||
@@ -18,7 +18,7 @@ const MobileHeader = () => {
|
||||
isToggled
|
||||
? "rounded-ee-lg rounded-es-lg shadow-[#00000026] shadow-md"
|
||||
: ""
|
||||
} text-white flex flex-col text-sm items-center transition-all duration-500 ease-in-out absolute top-0 left-0 z-30 overflow-hidden font-usual ${
|
||||
} text-white flex flex-col text-sm items-center transition-all duration-500 ease-in-out absolute top-0 left-0 z-[99999900] overflow-hidden font-usual ${
|
||||
isToggled ? "max-h-[472px]" : "max-h-14"
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -26,7 +26,6 @@ const FilterIcon = () => {
|
||||
rx="2.08333"
|
||||
ry="2.08333"
|
||||
fill="currentColor"
|
||||
// fill="#0D1922"
|
||||
/>
|
||||
<path
|
||||
d="M2.5 10H17.5"
|
||||
|
||||
@@ -10,7 +10,7 @@ const HelpModal = () => {
|
||||
const handleOnCloseClick = () => setModal(null);
|
||||
|
||||
return (
|
||||
<div className="absolute z-50 top-0 left-0 w-screen bg-[#0D192266] h-screen backdrop-blur-[6px] grid grid-cols-12 items-center">
|
||||
<div className="absolute z-[99999901] top-0 left-0 w-screen bg-[#0D192266] h-screen backdrop-blur-[6px] grid grid-cols-12 items-center">
|
||||
<div className="h-full col-span-3 col-start-10 bg-[#F3F3F2] py-6 px-6 flex flex-col justify-between items-center">
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="flex justify-between pb-4 border-b border-[#E2E2DC]">
|
||||
@@ -21,6 +21,7 @@ const HelpModal = () => {
|
||||
icon={<CrossIcon />}
|
||||
buttonType="fab"
|
||||
onClick={handleOnCloseClick}
|
||||
isCircleRounded
|
||||
/>
|
||||
</div>
|
||||
<img
|
||||
|
||||
@@ -65,7 +65,7 @@ const MasterplanFilters = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute z-50 top-0 left-0 w-screen bg-[#0D192266] h-screen backdrop-blur-[6px] items-center ">
|
||||
<div className="absolute z-[99999901] top-0 left-0 w-screen bg-[#0D192266] h-screen backdrop-blur-[6px] items-center ">
|
||||
<div className="h-full bg-[#F3F3F2] flex flex-col items-center justify-between w-[360px]">
|
||||
<div className="w-full py-6 px-6 flex flex-col items-center">
|
||||
<div className="flex justify-between border-b border-[#E2E2DC] w-full pb-4">
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import {
|
||||
SetStateAction,
|
||||
createContext,
|
||||
useEffect,
|
||||
useState,
|
||||
Dispatch,
|
||||
} from "react";
|
||||
|
||||
interface MobileModalWrapperContext {
|
||||
setIsAnimate: Dispatch<SetStateAction<boolean>> | null;
|
||||
isAnimate: boolean | null;
|
||||
}
|
||||
|
||||
export const MobileModalWrapperContext =
|
||||
createContext<MobileModalWrapperContext>({
|
||||
setIsAnimate: null,
|
||||
isAnimate: null,
|
||||
});
|
||||
|
||||
interface MobileModalWrapperProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const MobileModalWrapper = ({ children }: MobileModalWrapperProps) => {
|
||||
const [isAnimate, setIsAnimate] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsAnimate(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`h-screen w-screen pt-20 absolute z-[99999999] top-0 duration-300 ease-in-out ${
|
||||
isAnimate ? "backdrop-blur-sm" : "backdrop-blur-none"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className="h-full w-full transition-transform duration-300 ease-in-out"
|
||||
style={{ transform: `translateY(${isAnimate ? 0 : 100}%)` }}
|
||||
>
|
||||
<MobileModalWrapperContext.Provider
|
||||
value={{ isAnimate: isAnimate, setIsAnimate: setIsAnimate }}
|
||||
>
|
||||
{children}
|
||||
</MobileModalWrapperContext.Provider>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { MobileModalWrapper };
|
||||
@@ -0,0 +1,182 @@
|
||||
import { useContext } from "react";
|
||||
import useModal from "../../../store/useModal";
|
||||
import Button from "../../Button";
|
||||
import CrossIcon from "../../icons/CrossIcon";
|
||||
import { MobileModalWrapperContext } from "./MobileModalWrapper";
|
||||
import {
|
||||
initialApartmentTypeCheckboxes,
|
||||
initialSliders,
|
||||
initialSwitchers,
|
||||
initialRoveHomeCheckboxes,
|
||||
} from "../../consts/initialSearchFilters";
|
||||
import useSearchFilters from "../../../store/useSearchFilters";
|
||||
import Checkbox from "../../Checkbox";
|
||||
import MultiRangeSlider from "../../MultiRangeSlider";
|
||||
import Switch from "../../Switch";
|
||||
import ResetIcon from "../../icons/ResetIcon";
|
||||
|
||||
const SearchFiltersModal = () => {
|
||||
const { setModal } = useModal();
|
||||
const { setIsAnimate } = useContext(MobileModalWrapperContext);
|
||||
const {
|
||||
multirangeSliders,
|
||||
setMultirangeSliders,
|
||||
switchers,
|
||||
setSwitchers,
|
||||
apartmentTypeCheckboxes,
|
||||
setApartmentTypeCheckboxes,
|
||||
roveHomeTypeCheckboxes,
|
||||
setRoveHomeTypeCheckboxes,
|
||||
} = useSearchFilters();
|
||||
|
||||
const handleOnCrossClick = () => {
|
||||
if (setIsAnimate) {
|
||||
setIsAnimate(false);
|
||||
const timeout = setTimeout(() => {
|
||||
setModal(null);
|
||||
clearTimeout(timeout);
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnCheckboxApartmentClick = (checkboxId: string) => {
|
||||
const updatedCheckboxes = apartmentTypeCheckboxes.map((cbox) => {
|
||||
if (checkboxId !== cbox.id) return cbox;
|
||||
const isSelected = !cbox.selected;
|
||||
|
||||
return { ...cbox, selected: isSelected };
|
||||
});
|
||||
|
||||
setApartmentTypeCheckboxes(updatedCheckboxes);
|
||||
};
|
||||
|
||||
const handleOnCheckboxRoveHomeClick = (checkboxId: string) => {
|
||||
const updatedCheckboxes = roveHomeTypeCheckboxes.map((cbox) => {
|
||||
if (checkboxId !== cbox.id) return cbox;
|
||||
const isSelected = !cbox.selected;
|
||||
|
||||
return { ...cbox, selected: isSelected };
|
||||
});
|
||||
|
||||
setRoveHomeTypeCheckboxes(updatedCheckboxes);
|
||||
};
|
||||
|
||||
const handleOnSwitcherClick = (switcherId: string) => {
|
||||
const updatedSwitchers = switchers.map((switcher) => {
|
||||
if (switcherId !== switcher.id) return switcher;
|
||||
const { isSwitched } = switcher;
|
||||
|
||||
return { ...switcher, isSwitched: !isSwitched };
|
||||
});
|
||||
|
||||
setSwitchers(updatedSwitchers);
|
||||
};
|
||||
|
||||
const handleOnSliderValueChange = (
|
||||
sliderId: string,
|
||||
e: [a: number, b: number]
|
||||
) => {
|
||||
const updatedSliders = multirangeSliders.map((slider) => {
|
||||
if (sliderId !== slider.id) return slider;
|
||||
|
||||
return { ...slider, startValue: e[0], endValue: e[1] };
|
||||
});
|
||||
|
||||
setMultirangeSliders(updatedSliders);
|
||||
};
|
||||
|
||||
const handleOnShowApartmentClick = () => {
|
||||
if (setIsAnimate) {
|
||||
setIsAnimate(false);
|
||||
const timeout = setTimeout(() => {
|
||||
setModal(null);
|
||||
clearTimeout(timeout);
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-[#F3F3F2] rounded-ss-lg rounded-se-lg h-full p-6 flex flex-col gap-6">
|
||||
<div className="flex justify-between pb-6 border-b">
|
||||
<h2 className="text-[#0D1922] text-subheadline-m font-semibold">
|
||||
Filters
|
||||
</h2>
|
||||
<Button
|
||||
buttonType="secondary"
|
||||
isCircleRounded
|
||||
icon={<CrossIcon />}
|
||||
className="text-[#0D1922B2]"
|
||||
onClick={handleOnCrossClick}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col w-full">
|
||||
<p className="text-[#0D1922] text-m font-semibold pb-4">Rove Home</p>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{roveHomeTypeCheckboxes.map((checkbox) => (
|
||||
<Checkbox
|
||||
checkbox={checkbox}
|
||||
key={checkbox.id}
|
||||
onClick={handleOnCheckboxRoveHomeClick}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col w-full">
|
||||
<p className="text-[#0D1922] text-m font-semibold pb-4">
|
||||
Apartment type
|
||||
</p>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{apartmentTypeCheckboxes.map((checkbox) => (
|
||||
<Checkbox
|
||||
checkbox={checkbox}
|
||||
key={checkbox.id}
|
||||
onClick={handleOnCheckboxApartmentClick}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-6 border-b pb-12">
|
||||
{multirangeSliders.map((slider) => (
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex justify-between items-center">
|
||||
<p className="text-[#0D1922] text-m font-semibold ">
|
||||
{slider.title}
|
||||
</p>
|
||||
<p className="text-[#73787C] text-m font-semibold">
|
||||
{slider.unit}
|
||||
</p>
|
||||
</div>
|
||||
<MultiRangeSlider
|
||||
onChange={handleOnSliderValueChange}
|
||||
multirangeSlider={slider}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<div className="flex flex-col gap-2 pt-6">
|
||||
{switchers.map((switcher) => (
|
||||
<div key={switcher.id} className="flex justify-between w-full">
|
||||
<p className="text-s text-[#73787C]">{switcher.title}</p>
|
||||
<Switch switcher={switcher} onClick={handleOnSwitcherClick} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<p className="text-[#00BED7] text-m">195 apartments found</p>
|
||||
<Button
|
||||
icon={<ResetIcon />}
|
||||
text="Reset filters"
|
||||
buttonType="tertiary"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
text="Show Apartments"
|
||||
buttonType="cta"
|
||||
className="flex justify-center"
|
||||
onClick={handleOnShowApartmentClick}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchFiltersModal;
|
||||
@@ -5,10 +5,16 @@ import useSearchFilters from "../../store/useSearchFilters";
|
||||
import { initialSortList, layoutsCards } from "../../consts/initialSearchPage";
|
||||
import SearchIcon from "../icons/SearchIcon";
|
||||
import { sortCardBy } from "../../calc/sortCard";
|
||||
import Button from "../Button";
|
||||
import FilterIcon from "../icons/FilterIcon";
|
||||
import useModal from "../../store/useModal";
|
||||
import { MobileModalWrapper } from "../modals/mobile/MobileModalWrapper";
|
||||
import SearchFiltersModal from "../modals/mobile/SearchFiltersModal";
|
||||
|
||||
const LayoutOptions = () => {
|
||||
const [sortList, setSortList] = useState(initialSortList);
|
||||
const [cards, setCards] = useState(layoutsCards);
|
||||
const { setModal } = useModal();
|
||||
const { roveHomeTypeCheckboxes, apartmentTypeCheckboxes, multirangeSliders } =
|
||||
useSearchFilters();
|
||||
|
||||
@@ -20,6 +26,14 @@ const LayoutOptions = () => {
|
||||
setSortList(updatedSortList);
|
||||
};
|
||||
|
||||
const handleOnFilterClick = () => {
|
||||
setModal(
|
||||
<MobileModalWrapper>
|
||||
<SearchFiltersModal />
|
||||
</MobileModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const sortedCards = sortCardBy(sortList, layoutsCards);
|
||||
|
||||
@@ -32,14 +46,24 @@ const LayoutOptions = () => {
|
||||
]);
|
||||
return (
|
||||
<section className="w-full p-6 flex flex-col">
|
||||
<div className="flex justify-between w-full items-center border-b pb-4">
|
||||
<div className="flex gap-4 font-semibold text-subheadline-s leading-7 py-[6px]">
|
||||
<h2 className="text-[#0D1922]">Layout options</h2>
|
||||
<p className="text-[#73787C]">145</p>
|
||||
<div className="flex justify-between items-center border-b">
|
||||
<div className="flex w-full justify-between pb-4 lg:flex-row lg:items-center flex-col">
|
||||
<div className="flex gap-4 font-semibold text-subheadline-s leading-7 py-[6px]">
|
||||
<h2 className="text-[#0D1922]">Units</h2>
|
||||
<p className="text-[#73787C]">145</p>
|
||||
</div>
|
||||
<SortButton sortList={sortList} onClick={handleOnSortClick} />
|
||||
</div>
|
||||
<div className="lg:hidden block">
|
||||
<Button
|
||||
icon={<FilterIcon />}
|
||||
text="Filters"
|
||||
className="text-[#0D1922B2]"
|
||||
onClick={handleOnFilterClick}
|
||||
/>
|
||||
</div>
|
||||
<SortButton sortList={sortList} onClick={handleOnSortClick} />
|
||||
</div>
|
||||
<div className="grid 2xl:grid-cols-4 xl:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-4 pt-6">
|
||||
<div className="grid 2xl:grid-cols-4 xl:grid-cols-3 grid-cols-2 gap-4 pt-6">
|
||||
{cards.map((layoutsCard) => (
|
||||
<LayoutCard layoutCard={layoutsCard} />
|
||||
))}
|
||||
|
||||
@@ -78,7 +78,7 @@ const SidebarFilters = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="h-full w-[360px] bg-[#F3F3F2] flex flex-col items-center justify-between border-r">
|
||||
<div className="h-full w-[360px] bg-[#F3F3F2] flex-col items-center justify-between border-r lg:flex hidden">
|
||||
<div className="w-full py-6 px-6 flex flex-col items-center">
|
||||
<div className="flex justify-between border-b border-[#E2E2DC] w-full pb-4">
|
||||
<h2 className="text-subheadline-m font-semibold">Filters</h2>
|
||||
|
||||
@@ -22,7 +22,7 @@ const VirtualTourSidebar = ({ currentAppartment }: VirtualTourSidebarProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="absolute w-screen h-screen grid z-[99999999] pointer-events-none">
|
||||
<div className="absolute w-screen h-screen grid z-[99999999] pointer-events-none select-none">
|
||||
<div className="h-screen py-[130px] px-3 w-[360px]">
|
||||
<div className="bg-white w-full rounded-lg p-5 flex flex-col relative rounded-ee-none">
|
||||
<div className="flex flex-col gap-1 pb-[18px] border-b">
|
||||
|
||||
@@ -41,11 +41,8 @@ const VirtualTourTopPanel = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="absolute top-0 w-screen z-[99999999] select-none pointer-events-none">
|
||||
<img src="../images/top_shadow.png" className="w-screen" alt="" />
|
||||
</div>
|
||||
<div
|
||||
className={`absolute top-[62px] left-0 z-[99999999] w-full p-4 grid grid-cols-12 select-none touch-none pointer-events-none`}
|
||||
className={`absolute top-[62px] left-0 z-[99999998] w-full p-4 grid grid-cols-12 select-none touch-none pointer-events-none`}
|
||||
>
|
||||
<div className="flex gap-2 col-span-5">
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user