You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
716 lines
26 KiB
716 lines
26 KiB
"use client";
|
|
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import { getCountryList, COUNTRIES_EN, COUNTRIES_FA } from "@/data/countries";
|
|
import type { QuestionField } from "@/data/question-data";
|
|
import { useI18n } from "@/translations/provider";
|
|
import { useQuestionAnswers } from "./question-answer-storage";
|
|
import QuestionTitle from "./question-title";
|
|
|
|
type QuestionBirthplaceProps = {
|
|
question: QuestionField;
|
|
questionIndex: number;
|
|
disabled?: boolean;
|
|
};
|
|
|
|
type BirthplaceValue = {
|
|
country?: string;
|
|
city?: string;
|
|
};
|
|
|
|
function parseValue(rawValue: unknown): { country: string; city: string } {
|
|
if (!rawValue) return { country: "", city: "" };
|
|
|
|
if (typeof rawValue === "object" && rawValue !== null) {
|
|
const obj = rawValue as BirthplaceValue;
|
|
return {
|
|
country: typeof obj.country === "string" ? obj.country : "",
|
|
city: typeof obj.city === "string" ? obj.city : "",
|
|
};
|
|
}
|
|
|
|
if (typeof rawValue === "string") {
|
|
let str = rawValue.trim();
|
|
// Clean legacy country names with parentheses containing commas
|
|
str = str.replace(
|
|
"United Kingdom (UK, England, Wales, Scotland, Northern Ireland)",
|
|
"United Kingdom (UK)",
|
|
);
|
|
str = str.replace("United States (US, USA, America)", "United States (US)");
|
|
str = str.replace(
|
|
"United Arab Emirates (UAE, Dubai, Abu Dhabi)",
|
|
"United Arab Emirates (UAE)",
|
|
);
|
|
str = str.replace("Congo (Congo-Brazzaville)", "Congo");
|
|
str = str.replace("Czechia (Czech Republic)", "Czechia");
|
|
str = str.replace("Myanmar (formerly Burma)", "Myanmar");
|
|
str = str.replace(
|
|
"امارات متحده عربی (دبی، ابوظبی، UAE)",
|
|
"امارات متحده عربی (UAE)",
|
|
);
|
|
str = str.replace(
|
|
"بریتانیا (انگلستان، اسکاتلند، ولز، ایرلند شمالی، UK)",
|
|
"بریتانیا (UK)",
|
|
);
|
|
str = str.replace(
|
|
"ایالات متحده آمریکا (آمریکا، US، USA)",
|
|
"ایالات متحده آمریکا (US)",
|
|
);
|
|
|
|
if (str.includes(",")) {
|
|
const parts = str.split(",").map((s) => s.trim());
|
|
return { city: parts[0] || "", country: parts[1] || "" };
|
|
}
|
|
if (str.includes(" - ")) {
|
|
const parts = str.split(" - ").map((s) => s.trim());
|
|
return { country: parts[0] || "", city: parts[1] || "" };
|
|
}
|
|
const isCountry = COUNTRIES_EN.includes(str) || COUNTRIES_FA.includes(str);
|
|
if (isCountry) {
|
|
return { country: str, city: "" };
|
|
}
|
|
return { country: "", city: str };
|
|
}
|
|
|
|
return { country: "", city: "" };
|
|
}
|
|
|
|
export function QuestionBirthplace({
|
|
question,
|
|
questionIndex,
|
|
disabled,
|
|
}: QuestionBirthplaceProps) {
|
|
const { dictionary: t, locale } = useI18n();
|
|
const { getAnswerValue, setAnswerValue } = useQuestionAnswers();
|
|
const rawValue = getAnswerValue(question, questionIndex);
|
|
|
|
const initial = parseValue(rawValue);
|
|
const [selectedCountry, setSelectedCountry] = useState(initial.country);
|
|
const [cityInput, setCityInput] = useState(initial.city);
|
|
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const [searchQuery, setSearchQuery] = useState("");
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const listRef = useRef<HTMLDivElement>(null);
|
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
|
|
const isResidence =
|
|
question.title.toLowerCase().includes("residence") ||
|
|
question.title.includes("سکونت");
|
|
|
|
const [mode, setMode] = useState<"auto" | "manual">("auto");
|
|
const [isDetecting, setIsDetecting] = useState(false);
|
|
const [detectedLocation, setDetectedLocation] = useState("");
|
|
|
|
const updateAnswers = (country: string, city: string) => {
|
|
const formatted =
|
|
city && country ? `${city}, ${country}` : city || country || null;
|
|
setAnswerValue(question, questionIndex, formatted);
|
|
};
|
|
|
|
// GeoIP detection logic
|
|
const detectLocation = (force = false) => {
|
|
const alreadyChecked =
|
|
typeof window !== "undefined"
|
|
? localStorage.getItem("hasCheckedGeoIPResidence")
|
|
: "true";
|
|
if (alreadyChecked && !force) {
|
|
if (rawValue) {
|
|
const parsed = parseValue(rawValue);
|
|
if (parsed.country && parsed.city) {
|
|
setDetectedLocation(`${parsed.city}, ${parsed.country}`);
|
|
setMode("auto");
|
|
} else {
|
|
setMode("manual");
|
|
}
|
|
} else {
|
|
setMode("auto");
|
|
}
|
|
return;
|
|
}
|
|
|
|
// If there is already a saved value and we are not forcing, do not fetch again
|
|
if (rawValue && !force) {
|
|
const parsed = parseValue(rawValue);
|
|
if (parsed.country && parsed.city) {
|
|
setDetectedLocation(`${parsed.city}, ${parsed.country}`);
|
|
setMode("auto");
|
|
} else {
|
|
setMode("manual");
|
|
}
|
|
return;
|
|
}
|
|
|
|
setIsDetecting(true);
|
|
if (typeof window !== "undefined") {
|
|
localStorage.setItem("hasCheckedGeoIPResidence", "true");
|
|
}
|
|
fetch("https://ipapi.co/json/")
|
|
.then((res) => res.json())
|
|
.then((data) => {
|
|
if (data && data.city && data.country_name) {
|
|
const city = data.city;
|
|
const country = data.country_name;
|
|
setSelectedCountry(country);
|
|
setCityInput(city);
|
|
setDetectedLocation(`${city}, ${country}`);
|
|
updateAnswers(country, city);
|
|
setMode("auto");
|
|
} else {
|
|
throw new Error("Missing data");
|
|
}
|
|
})
|
|
.catch(() => {
|
|
fetch("https://ipwho.is/")
|
|
.then((res) => res.json())
|
|
.then((data) => {
|
|
if (data && data.city && data.country) {
|
|
const city = data.city;
|
|
const country = data.country;
|
|
setSelectedCountry(country);
|
|
setCityInput(city);
|
|
setDetectedLocation(`${city}, ${country}`);
|
|
updateAnswers(country, city);
|
|
setMode("auto");
|
|
} else {
|
|
setMode("manual");
|
|
}
|
|
})
|
|
.catch(() => {
|
|
setMode("manual");
|
|
});
|
|
})
|
|
.finally(() => {
|
|
setIsDetecting(false);
|
|
});
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (isResidence) {
|
|
detectLocation();
|
|
}
|
|
}, [isResidence]);
|
|
|
|
const handleAutoClick = () => {
|
|
setMode("auto");
|
|
detectLocation(true);
|
|
};
|
|
|
|
const handleManualClick = () => {
|
|
if (typeof window !== "undefined") {
|
|
localStorage.setItem("hasCheckedGeoIPResidence", "true");
|
|
}
|
|
setMode("manual");
|
|
setSelectedCountry("");
|
|
setCityInput("");
|
|
updateAnswers("", "");
|
|
};
|
|
|
|
// Synchronize state if rawValue changes externally
|
|
useEffect(() => {
|
|
const updated = parseValue(rawValue);
|
|
if (updated.country !== selectedCountry) {
|
|
setSelectedCountry(updated.country);
|
|
}
|
|
if (updated.city.trim() !== cityInput.trim()) {
|
|
setCityInput(updated.city);
|
|
}
|
|
if (updated.country && updated.city) {
|
|
setDetectedLocation(`${updated.city}, ${updated.country}`);
|
|
}
|
|
}, [rawValue]);
|
|
|
|
// Lock page scroll when dropdown is open
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
document.body.classList.add("dropdown-open");
|
|
document.body.style.overflow = "hidden";
|
|
document.documentElement.style.overflow = "hidden";
|
|
} else {
|
|
document.body.classList.remove("dropdown-open");
|
|
document.body.style.overflow = "";
|
|
document.documentElement.style.overflow = "";
|
|
}
|
|
return () => {
|
|
document.body.classList.remove("dropdown-open");
|
|
document.body.style.overflow = "";
|
|
document.documentElement.style.overflow = "";
|
|
};
|
|
}, [isOpen]);
|
|
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
const timer = setTimeout(() => {
|
|
searchInputRef.current?.focus();
|
|
}, 50);
|
|
return () => clearTimeout(timer);
|
|
}
|
|
}, [isOpen]);
|
|
|
|
const handleListWheel = useCallback((e: React.WheelEvent<HTMLDivElement>) => {
|
|
e.stopPropagation();
|
|
const el = listRef.current;
|
|
if (!el) return;
|
|
|
|
const atTop = el.scrollTop <= 0 && e.deltaY < 0;
|
|
const atBottom =
|
|
el.scrollTop + el.clientHeight >= el.scrollHeight && e.deltaY > 0;
|
|
|
|
if (atTop || atBottom) {
|
|
e.preventDefault();
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
function handleClickOutside(event: MouseEvent) {
|
|
if (
|
|
containerRef.current &&
|
|
!containerRef.current.contains(event.target as Node)
|
|
) {
|
|
setIsOpen(false);
|
|
}
|
|
}
|
|
document.addEventListener("mousedown", handleClickOutside);
|
|
return () => {
|
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
};
|
|
}, []);
|
|
|
|
const options = getCountryList(locale);
|
|
const filteredOptions = options.filter((option) =>
|
|
option.toLowerCase().includes(searchQuery.toLowerCase()),
|
|
);
|
|
|
|
const handleSelectCountry = (country: string) => {
|
|
if (typeof window !== "undefined") {
|
|
localStorage.setItem("hasCheckedGeoIPResidence", "true");
|
|
}
|
|
setSelectedCountry(country);
|
|
setCityInput("");
|
|
setIsOpen(false);
|
|
updateAnswers(country, "");
|
|
};
|
|
|
|
const handleCityChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
if (typeof window !== "undefined") {
|
|
localStorage.setItem("hasCheckedGeoIPResidence", "true");
|
|
}
|
|
const newCity = e.target.value;
|
|
setCityInput(newCity);
|
|
updateAnswers(selectedCountry, newCity);
|
|
};
|
|
|
|
const isRtl = locale === "fa" || locale === "ar" || locale === "ur";
|
|
const selectCountryPlaceholder = isRtl ? "انتخاب کشور" : "Select country";
|
|
const cityPlaceholder =
|
|
question.extras?.placeHolder ||
|
|
(isRtl ? "شهر، منطقه یا محله" : "City, region, or neighborhood");
|
|
|
|
return (
|
|
<div
|
|
ref={containerRef}
|
|
className={[
|
|
"relative flex w-full flex-col gap-3 transition-opacity duration-200",
|
|
disabled ? "pointer-events-none opacity-30" : "",
|
|
].join(" ")}
|
|
>
|
|
<QuestionTitle question={question} />
|
|
|
|
{isResidence ? (
|
|
<>
|
|
{/* GeoIP Auto/Manual selector UI */}
|
|
<div className="flex flex-col gap-3.5 w-full py-1">
|
|
{/* Left Side: Location Info */}
|
|
<div className="flex items-center gap-2.5 min-w-0">
|
|
<svg
|
|
width="16"
|
|
height="20"
|
|
viewBox="0 0 16 20"
|
|
fill="none"
|
|
className="shrink-0 text-[#181818]"
|
|
>
|
|
<path
|
|
d="M8 0C3.58 0 0 3.58 0 8C0 13.54 8 20 8 20C8 20 16 13.54 16 8C16 3.58 12.42 0 8 0ZM8 11C6.34 11 5 9.66 5 8C5 6.34 6.34 5 8 5C9.66 5 11 6.34 11 8C11 9.66 9.66 11 8 11Z"
|
|
fill="currentColor"
|
|
/>
|
|
</svg>
|
|
<span className="text-[16px] font-bold text-[#181818]">
|
|
{isDetecting
|
|
? locale === "fa"
|
|
? "در حال شناسایی..."
|
|
: "Detecting..."
|
|
: detectedLocation ||
|
|
(locale === "fa" ? "نامشخص" : "Unknown")}
|
|
</span>
|
|
</div>
|
|
|
|
{/* Right Side: Toggle Buttons */}
|
|
<div className="flex items-center gap-2 w-full">
|
|
{/* Auto Button */}
|
|
<button
|
|
type="button"
|
|
onClick={handleAutoClick}
|
|
className={[
|
|
"flex flex-1 items-center justify-center gap-1.5 h-[46px] rounded-xl text-[14px] font-bold cursor-pointer transition-all shadow-sm",
|
|
mode === "auto"
|
|
? "bg-[#F2465F] text-white"
|
|
: "bg-white border border-[#D0D5DD] text-[#344054] hover:bg-gray-50",
|
|
].join(" ")}
|
|
>
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 16 16"
|
|
fill="none"
|
|
className="shrink-0"
|
|
>
|
|
<circle
|
|
cx="8"
|
|
cy="8"
|
|
r="6"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
/>
|
|
<circle cx="8" cy="8" r="2" fill="currentColor" />
|
|
<path
|
|
d="M8 0V3M8 13V16M0 8H3M13 8H16"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
/>
|
|
</svg>
|
|
<span>{locale === "fa" ? "خودکار" : "Auto"}</span>
|
|
</button>
|
|
|
|
{/* Manual Button */}
|
|
<button
|
|
type="button"
|
|
onClick={handleManualClick}
|
|
className={[
|
|
"flex flex-1 items-center justify-center gap-1.5 h-[46px] rounded-xl text-[14px] font-bold cursor-pointer transition-all shadow-sm",
|
|
mode === "manual"
|
|
? "bg-[#F2465F] text-white"
|
|
: "bg-white border border-[#D0D5DD] text-[#344054] hover:bg-gray-50",
|
|
].join(" ")}
|
|
>
|
|
<svg
|
|
width="16"
|
|
height="16"
|
|
viewBox="0 0 16 16"
|
|
fill="none"
|
|
className="shrink-0"
|
|
>
|
|
<path
|
|
d="M1 3.5L5 1.5L11 4.5L15 2.5V12.5L11 14.5L5 11.5L1 13.5V3.5Z"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
/>
|
|
<path
|
|
d="M5 1.5V11.5M11 4.5V14.5"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
/>
|
|
</svg>
|
|
<span>{locale === "fa" ? "دستی" : "Manual"}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{mode === "manual" && (
|
|
<div className="flex flex-col gap-3 w-full animate-in fade-in slide-in-from-top-2 duration-200">
|
|
{/* Country Selection Dropdown */}
|
|
<div className="relative w-full">
|
|
<button
|
|
type="button"
|
|
disabled={disabled}
|
|
onClick={() => setIsOpen(!isOpen)}
|
|
className={[
|
|
"flex h-[54px] w-full items-center justify-between rounded-[16px] border bg-white px-4.5 text-start transition-all cursor-pointer outline-none",
|
|
isOpen
|
|
? "border-[#6F6F6F] ring-1 ring-[#6F6F6F]"
|
|
: "border-[#D0D5DD] hover:border-[#98A2B3]",
|
|
].join(" ")}
|
|
>
|
|
<span
|
|
className={[
|
|
"text-[15px] font-medium truncate",
|
|
selectedCountry ? "text-[#181818]" : "text-[#667085]",
|
|
].join(" ")}
|
|
>
|
|
{selectedCountry || selectCountryPlaceholder}
|
|
</span>
|
|
<svg
|
|
width="16"
|
|
height="10"
|
|
viewBox="0 0 16 10"
|
|
fill="none"
|
|
className={[
|
|
"shrink-0 transition-transform duration-200",
|
|
isOpen ? "rotate-180" : "",
|
|
].join(" ")}
|
|
>
|
|
<path
|
|
d="M14.75 1.25L7.75 8.25L0.75 1.25"
|
|
stroke="#344054"
|
|
strokeWidth="1.75"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
|
|
{isOpen && (
|
|
<div className="absolute top-[calc(100%+8px)] left-0 z-50 flex w-full flex-col gap-3.5 rounded-[20px] bg-white p-4.5 shadow-[0_12px_36px_rgba(0,0,0,0.09)] border border-[#EAECF0] animate-in fade-in zoom-in-95 duration-150">
|
|
{options.length > 3 || searchQuery ? (
|
|
<div className="flex h-[46px] w-full items-center gap-2.5 rounded-[12px] bg-[#EFEFEF] px-3.5 transition-colors focus-within:bg-[#E8E8E8]">
|
|
<svg
|
|
width="18"
|
|
height="18"
|
|
viewBox="0 0 18 18"
|
|
fill="none"
|
|
className="shrink-0 text-[#667085]"
|
|
>
|
|
<path
|
|
d="M8.25 14.25C11.5637 14.25 14.25 11.5637 14.25 8.25C14.25 4.93629 11.5637 2.25 8.25 2.25C4.93629 2.25 2.25 4.93629 2.25 8.25C2.25 11.5637 4.93629 14.25 8.25 14.25Z"
|
|
stroke="#667085"
|
|
strokeWidth="1.75"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
/>
|
|
<path
|
|
d="M15.75 15.75L12.5 12.5"
|
|
stroke="#667085"
|
|
strokeWidth="1.75"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
/>
|
|
</svg>
|
|
<input
|
|
ref={searchInputRef}
|
|
type="text"
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
placeholder="Search..."
|
|
className="flex-1 bg-transparent text-[14px] font-medium text-[#181818] outline-none placeholder:text-[#98A2B3]"
|
|
/>
|
|
{searchQuery ? (
|
|
<button
|
|
type="button"
|
|
onClick={() => setSearchQuery("")}
|
|
className="text-[#667085] hover:text-[#181818] text-xs font-semibold"
|
|
>
|
|
✕
|
|
</button>
|
|
) : null}
|
|
</div>
|
|
) : null}
|
|
|
|
<div
|
|
ref={listRef}
|
|
onWheel={handleListWheel}
|
|
onTouchStart={(e) => e.stopPropagation()}
|
|
onTouchMove={(e) => e.stopPropagation()}
|
|
onTouchEnd={(e) => e.stopPropagation()}
|
|
className="flex max-h-[220px] flex-col gap-3.5 overflow-y-auto overscroll-contain pr-1"
|
|
>
|
|
{filteredOptions.length > 0 ? (
|
|
filteredOptions.map((option) => {
|
|
const isSelected = selectedCountry === option;
|
|
|
|
return (
|
|
<button
|
|
key={option}
|
|
type="button"
|
|
onClick={() => handleSelectCountry(option)}
|
|
className="flex w-full items-center gap-3 text-start cursor-pointer group/opt py-0.5"
|
|
>
|
|
<div
|
|
className={[
|
|
"size-[20px] shrink-0 rounded-full transition-all duration-150",
|
|
isSelected
|
|
? "bg-[#F2465F] shadow-xs"
|
|
: "border-[2px] border-[#344054] bg-transparent group-hover/opt:border-[#181818]",
|
|
].join(" ")}
|
|
/>
|
|
<span className="text-[15px] font-bold text-[#181818]">
|
|
{option}
|
|
</span>
|
|
</button>
|
|
);
|
|
})
|
|
) : (
|
|
<span className="py-2 text-[13px] text-[#667085]">
|
|
No options found
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* City Text Input */}
|
|
<div className="w-full">
|
|
<input
|
|
type="text"
|
|
disabled={disabled}
|
|
value={cityInput}
|
|
onChange={handleCityChange}
|
|
placeholder={cityPlaceholder}
|
|
className="h-[54px] w-full rounded-[16px] border border-[#D0D5DD] bg-white px-4.5 text-[15px] font-medium text-[#181818] placeholder:text-[#98A2B3] hover:border-[#98A2B3] focus:border-[#6F6F6F] focus:ring-1 focus:ring-[#6F6F6F] outline-none transition-all"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
) : (
|
|
<>
|
|
{/* 1. Country Selection Dropdown */}
|
|
<div className="relative w-full">
|
|
<button
|
|
type="button"
|
|
disabled={disabled}
|
|
onClick={() => setIsOpen(!isOpen)}
|
|
className={[
|
|
"flex h-[54px] w-full items-center justify-between rounded-[16px] border bg-white px-4.5 text-start transition-all cursor-pointer outline-none",
|
|
isOpen
|
|
? "border-[#6F6F6F] ring-1 ring-[#6F6F6F]"
|
|
: "border-[#D0D5DD] hover:border-[#98A2B3]",
|
|
].join(" ")}
|
|
>
|
|
<span
|
|
className={[
|
|
"text-[15px] font-medium truncate",
|
|
selectedCountry ? "text-[#181818]" : "text-[#667085]",
|
|
].join(" ")}
|
|
>
|
|
{selectedCountry || selectCountryPlaceholder}
|
|
</span>
|
|
<svg
|
|
width="16"
|
|
height="10"
|
|
viewBox="0 0 16 10"
|
|
fill="none"
|
|
className={[
|
|
"shrink-0 transition-transform duration-200",
|
|
isOpen ? "rotate-180" : "",
|
|
].join(" ")}
|
|
>
|
|
<path
|
|
d="M14.75 1.25L7.75 8.25L0.75 1.25"
|
|
stroke="#344054"
|
|
strokeWidth="1.75"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
|
|
{isOpen && (
|
|
<div className="absolute top-[calc(100%+8px)] left-0 z-50 flex w-full flex-col gap-3.5 rounded-[20px] bg-white p-4.5 shadow-[0_12px_36px_rgba(0,0,0,0.09)] border border-[#EAECF0] animate-in fade-in zoom-in-95 duration-150">
|
|
{options.length > 3 || searchQuery ? (
|
|
<div className="flex h-[46px] w-full items-center gap-2.5 rounded-[12px] bg-[#EFEFEF] px-3.5 transition-colors focus-within:bg-[#E8E8E8]">
|
|
<svg
|
|
width="18"
|
|
height="18"
|
|
viewBox="0 0 18 18"
|
|
fill="none"
|
|
className="shrink-0 text-[#667085]"
|
|
>
|
|
<path
|
|
d="M8.25 14.25C11.5637 14.25 14.25 11.5637 14.25 8.25C14.25 4.93629 11.5637 2.25 8.25 2.25C4.93629 2.25 2.25 4.93629 2.25 8.25C2.25 11.5637 4.93629 14.25 8.25 14.25Z"
|
|
stroke="#667085"
|
|
strokeWidth="1.75"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
/>
|
|
<path
|
|
d="M15.75 15.75L12.5 12.5"
|
|
stroke="#667085"
|
|
strokeWidth="1.75"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
/>
|
|
</svg>
|
|
<input
|
|
ref={searchInputRef}
|
|
type="text"
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
placeholder="Search..."
|
|
className="flex-1 bg-transparent text-[14px] font-medium text-[#181818] outline-none placeholder:text-[#98A2B3]"
|
|
/>
|
|
{searchQuery ? (
|
|
<button
|
|
type="button"
|
|
onClick={() => setSearchQuery("")}
|
|
className="text-[#667085] hover:text-[#181818] text-xs font-semibold"
|
|
>
|
|
✕
|
|
</button>
|
|
) : null}
|
|
</div>
|
|
) : null}
|
|
|
|
<div
|
|
ref={listRef}
|
|
onWheel={handleListWheel}
|
|
onTouchStart={(e) => e.stopPropagation()}
|
|
onTouchMove={(e) => e.stopPropagation()}
|
|
onTouchEnd={(e) => e.stopPropagation()}
|
|
className="flex max-h-[220px] flex-col gap-3.5 overflow-y-auto overscroll-contain pr-1"
|
|
>
|
|
{filteredOptions.length > 0 ? (
|
|
filteredOptions.map((option) => {
|
|
const isSelected = selectedCountry === option;
|
|
|
|
return (
|
|
<button
|
|
key={option}
|
|
type="button"
|
|
onClick={() => handleSelectCountry(option)}
|
|
className="flex w-full items-center gap-3 text-start cursor-pointer group/opt py-0.5"
|
|
>
|
|
<div
|
|
className={[
|
|
"size-[20px] shrink-0 rounded-full transition-all duration-150",
|
|
isSelected
|
|
? "bg-[#F2465F] shadow-xs"
|
|
: "border-[2px] border-[#344054] bg-transparent group-hover/opt:border-[#181818]",
|
|
].join(" ")}
|
|
/>
|
|
<span className="text-[15px] font-bold text-[#181818]">
|
|
{option}
|
|
</span>
|
|
</button>
|
|
);
|
|
})
|
|
) : (
|
|
<span className="py-2 text-[13px] text-[#667085]">
|
|
No options found
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* 2. City Text Input */}
|
|
<div className="w-full">
|
|
<input
|
|
type="text"
|
|
disabled={disabled}
|
|
value={cityInput}
|
|
onChange={handleCityChange}
|
|
placeholder={cityPlaceholder}
|
|
className="h-[54px] w-full rounded-[16px] border border-[#D0D5DD] bg-white px-4.5 text-[15px] font-medium text-[#181818] placeholder:text-[#98A2B3] hover:border-[#98A2B3] focus:border-[#6F6F6F] focus:ring-1 focus:ring-[#6F6F6F] outline-none transition-all"
|
|
/>
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default QuestionBirthplace;
|