Refactor ProjectSelect and UnitTypeCard components; enhance styling and add new unit types to projects data

This commit is contained in:
2025-04-28 16:51:05 +05:00
parent d43ef88e15
commit a44f2fbb98
18 changed files with 263 additions and 63 deletions
+34 -32
View File
@@ -1,18 +1,8 @@
/* eslint-disable react-hooks/exhaustive-deps */
import clsx from "clsx";
import { useEffect, useState } from "react";
interface Project {
title: string;
img: string;
types: {
name: string;
img: string;
wings: string;
floors: string;
area: string;
}[];
}
import Project from "../types/Project";
import Select from "./ui/Select";
function ProjectSelect({
projects,
@@ -28,26 +18,38 @@ function ProjectSelect({
}, [selectedProject]);
return (
<div className="flex 2xl:gap-[0.556vw] gap-2">
{projects.map((project) => (
<div
className={clsx(
"2xl:rounded-[2.778vw] rounded-[40px] 2xl:p-[0.278vw] p-1 flex items-center 2xl:gap-[0.556vw] gap-2 text-s 2xl:outline-[0.069vw] outline transition-colors duration-300 cursor-pointer",
project.title === selectedProject.title
? "outline-[#00BED7]"
: "outline-[#E2E2DC]"
)}
onClick={() => setSelectedProject(project)}
>
<img
src={project.img}
alt={project.title}
className="object-cover 2xl:w-[2.778vw] w-10 aspect-square rounded-full"
/>
<p className="2xl:mr-[1.111vw] mr-6">{project.title}</p>
</div>
))}
</div>
<>
<div className="flex 2xl:gap-[0.556vw] gap-2 max-md:hidden">
{projects.map((project) => (
<div
className={clsx(
"2xl:rounded-[2.778vw] rounded-[40px] 2xl:p-[0.278vw] p-1 flex items-center 2xl:gap-[0.556vw] gap-2 text-s 2xl:outline-[0.069vw] outline transition-colors duration-300 cursor-pointer",
project.title === selectedProject.title
? "outline-[#00BED7]"
: "outline-[#E2E2DC]"
)}
onClick={() => setSelectedProject(project)}
>
<img
src={project.img}
alt={project.title}
className="object-cover 2xl:w-[2.778vw] w-10 aspect-square rounded-full"
/>
<p className="2xl:mr-[1.111vw] mr-6">{project.title}</p>
</div>
))}
</div>
<Select
options={projects.map((project) => project.title)}
onSelect={(option) =>
setSelectedProject(
projects.find((project) => project.title === option) || projects[0]
)
}
className="md:hidden"
/>
</>
);
}
+19 -13
View File
@@ -1,19 +1,25 @@
interface UnitType {
name: string;
img: string;
wings: string;
floors: string;
area: string;
}
import UnitType from "../types/UnitType";
function UnitTypeCard({ project, type }: { project: string; type: UnitType }) {
return (
<div className="bg-white p-4 rounded-2xl">
<p className="text-2xl font-semibold leading-[135%]">{project}</p>
<p className="text-2xl font-semibold leading-[135%]">{type.name}</p>
<p className="text-2xl font-semibold leading-[135%]">{type.wings}</p>
<p className="text-2xl font-semibold leading-[135%]">{type.floors}</p>
<p className="text-2xl font-semibold leading-[135%]">{type.area}</p>
<div className="bg-white p-4 2xl:p-[1.111vw] rounded-2xl 2xl:rounded-[1.111vw] space-y-4 2xl:space-y-[1.111vw]">
<div className="space-y-1 2xl:space-y-[0.278vw]">
<p className="text-s">{project}</p>
<div className="flex items-center gap-2 2xl:gap-[0.556vw]">
{type.wings && (
<>
<p className="text-caption-m text-[#0D1922]/70">{type.wings}</p>
<div className="w-1 h-1 bg-[#E2E2DC] rounded-full"></div>
</>
)}
<p className="text-caption-m text-[#0D1922]/70">{type.floors}</p>
</div>
</div>
<img src={type.img} alt="" />
<div className="space-y-1 2xl:space-y-[0.278vw]">
<p className="text-s text-[#0D1922]/70">{type.area}</p>
<p className="text-subheadline-s font-medium">{type.name}</p>
</div>
</div>
);
}
+20
View File
@@ -0,0 +1,20 @@
function CheckIcon(props: React.SVGProps<SVGSVGElement>) {
return (
<svg
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="m5 11 5 5 9-8"
stroke="currentColor"
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export default CheckIcon;
+20
View File
@@ -0,0 +1,20 @@
function ChevronDownIcon(props: React.SVGProps<SVGSVGElement>) {
return (
<svg
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
d="m17 10-5 5-5-5"
stroke="currentColor"
strokeWidth={1.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export default ChevronDownIcon;
+81
View File
@@ -0,0 +1,81 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { motion } from "motion/react";
import { AnimatePresence } from "motion/react";
import { useEffect, useState } from "react";
import { useClickAway } from "@uidotdev/usehooks";
import clsx from "clsx";
import ChevronDownIcon from "../icons/ChevronDownIcon";
import CheckIcon from "../icons/CheckIcon";
function Select({
options,
onSelect,
className = "",
}: {
options: string[];
onSelect: (option: string) => void;
className?: string;
}) {
const [isShow, setIsShow] = useState(false);
const [selectedOption, setSelectedOption] = useState(options[0]);
const ref = useClickAway<HTMLDivElement>(() => setIsShow(false));
useEffect(() => {
onSelect(selectedOption);
}, [selectedOption]);
return (
<div ref={ref} className={clsx("relative", className)}>
<button
className={clsx(
"px-4 py-[14px] rounded-xl ring-1 transition-[box-shadow] w-full text-left flex items-center justify-between group",
isShow ? " ring-[#00BED7]" : "ring-[#E2E2DC]"
)}
onClick={() => setIsShow(!isShow)}
>
<p className="text-s">{selectedOption}</p>
<ChevronDownIcon
className={clsx(
"w-6 h-6 flex-shrink-0 group-hover:text-[#00BED7] transition-[color,rotate]",
isShow && "rotate-180"
)}
/>
</button>
<AnimatePresence>
{isShow && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
className="absolute mt-1 p-1 space-y-0.5 shadow-[0px_2px_8px_rgba(0,0,0,0.15)] rounded-xl bg-white w-full z-10"
>
{options.map((option, index) => (
<button
key={index}
onClick={() => {
setSelectedOption(option);
setIsShow(false);
}}
className="px-4 py-[14px] group hover:bg-[#F3F3F2] transition-colors rounded-xl w-full text-left flex items-center justify-between"
>
<p
className={clsx(
"text-s group-hover:text-[#0D1922] transition-colors",
selectedOption !== option && "text-[#0D1922]/70"
)}
>
{option}
</p>
{selectedOption === option && (
<CheckIcon className="w-6 h-6 flex-shrink-0 text-[#00BED7]" />
)}
</button>
))}
</motion.div>
)}
</AnimatePresence>
</div>
);
}
export default Select;