حقل إدخال الهاتف
حقل إدخال أرقام الهواتف الدولية مع محدّد الدولة وأعلام الدول الرمزية والبحث عن رمز الاتصال. يدعم 50 دولة مع تنسيق وتحقق مدمجين.
التثبيت
npx shadcn@latest add @babelize/phone-inputالاستخدام
"use client";
import { useState } from "react";
import { PhoneInput } from "@/components/ui/phone-input";
export function PhoneField() {
const [phone, setPhone] = useState("");
const [countryCode, setCountryCode] = useState("US");
return (
<PhoneInput
value={phone} // omit for uncontrolled
country={countryCode} // omit for uncontrolled (or use defaultCountry)
onValueChange={(value, country) => {
setPhone(value);
console.log(country.dialCode);
}}
onCountryChange={(country) => setCountryCode(country.code)}
placeholder="Enter phone number"
/>
);
}التحكم في الدولة
تتبع الدولة نفس اصطلاح التحكم وعدم التحكم المستخدم للرقم: مرّر country + onCountryChange للتحكم بها، أو defaultCountry لتحديد القيمة الافتراضية غير المتحكَّم بها. عندما يكون country متحكَّمًا به، يكون للمكوّن الأب الكلمة الأخيرة — فاحترم onCountryChange إذا أردت أن يبدّل اكتشاف رمز الاتصال (أدناه) الدولَ.
اكتشاف رمز الاتصال
اكتب أو الصق رقمًا يبدأ بـ + فيبدّل المكوّن الدولة لتطابقه، ويحذف رمز الاتصال والفاصل الذي يليه من الحقل — فالبادئة التي يعلنها + تتقدّم على الدولة المختارة يدويًا:
- لصق
+44 20 7946 0958يحدّد GB ويترك20 7946 0958في الحقل. - كتابة
+قبل أرقام موجودة أصلًا في الحقل لا تحذفها أبدًا: لا يُستهلك رمز الاتصال إلا إذا أُدخلت أرقامه بعد+. اكتب+1قبل رقم موجود، فتتبدّل الدولة إلى US بمجرد ظهور1— ويحتفظ الرقم بكل أرقامه التي كانت فيه. - يفوز أطول رمز اتصال مطابق، لذا يختار
+880…بنغلاديش حتى لو تطابق رمز أقصر أيضًا. - رموز الاتصال المشتركة تُبقي الدولة المحددة حاليًا إذا كانت إحدى الدول التي تشترك في الرمز المطابق، وإلا تختار الأولى في القائمة المعروضة — فلا يقفز
+1أبدًا بعيدًا عن US أو CA أثناء الكتابة. تنطبق القاعدة نفسها على أي رمز مشترك في قائمةcountriesمخصصة، مثل+7إذا عرضت RU وKZ معًا. - رمز الاتصال الذي لا يوجد في قائمة
countriesالخاصة بك يُترك كما هو — فالدولة لا تتبدّل إلا إلى دولة تعرضها فعليًا. - بعد التبديل، تعمل قائمة الدول كالمعتاد: اختيار دولة يتجاوز الدولة المكتشفة، ولا يُعاد الاكتشاف حتى المرة التالية التي تبدأ فيها القيمة بـ
+.
الخصائص
| الخاصية | النوع | مطلوب | الوصف |
|---|---|---|---|
value | string | لا | رقم الهاتف — مرّره للتحكم في المكوّن |
defaultValue | string | لا | رقم الهاتف الأولي عند عدم التحكم |
onValueChange | (phone: string, country: Country) => void | لا | دالة استدعاء عند تغيّر رقم الهاتف أو الدولة |
country | string | لا | رمز الدولة المحدد (ISO 3166-1 alpha-2) — مرّره للتحكم في الدولة |
onCountryChange | (country: Country) => void | لا | دالة استدعاء عند تغيّر الدولة المحددة — من القائمة أو من اكتشاف رمز الاتصال |
defaultCountry | string | لا | رمز الدولة الأولي عند عدم التحكم (ISO 3166-1 alpha-2، مثل "US"، "GB"، "IN"؛ الافتراضي: "US") |
countries | Country[] | لا | الدول المعروضة، بترتيب العرض (الافتراضي: COUNTRIES) |
placeholder | string | لا | النص النائب |
disabled | boolean | لا | تعطيل حقل الإدخال |
showFlags | boolean | لا | إظهار علم الدولة بجانب رمز الاتصال (الافتراضي: true) |
size | "sm" | "default" | "lg" | لا | ارتفاع الحقل (الافتراضي: "default") |
align | "start" | "end" | لا | الحافة التي تصطف القائمة معها في الحقل (الافتراضي: "start") |
side | "top" | "bottom" | لا | الجانب المفضل للقائمة؛ يتبدّل عند عدم وجود مساحة (الافتراضي: "bottom") |
searchPlaceholder | string | لا | النص النائب لبحث الدول (الافتراضي: "Search countries...") |
emptyMessage | ReactNode | لا | يُعرض عندما لا يطابق البحث أي نتيجة (الافتراضي: "No countries found.") |
labels | Partial<PhoneInputLabels> | لا | تجاوز نصوص الواجهة الإنجليزية المدمجة (انظر أدناه) |
displayLocale | string | لا | الإعدادات المحلية لأسماء الدول، مثل "fr" (الافتراضي: الأسماء الإنجليزية) |
maxHeight | number | string | لا | أقصى ارتفاع لقائمة الدول القابلة للتمرير (الافتراضي: "15rem") |
label | string | لا | تسمية إمكانية الوصول للحقل |
className | string | لا | الأصناف الخاصة بالغلاف الجذري |
classNames | PhoneInputClassNames | لا | أصناف لكل جزء: root، field، trigger، input، content، search، list، item، empty |
أي خاصية أخرى — name، required، autoComplete، aria-invalid، data-* — تُمرَّر إلى العنصر الأساسي <input>، ويُعاد توجيه المرجع إليه، فيعمل المكوّن داخل نموذج HTML عادي. ضبط aria-invalid يجعل الحقل كله أحمر، مطابقًا لـ Input في shadcn.
كائن الدولة
| الخاصية | النوع | الوصف |
|---|---|---|
code | string | رمز ISO 3166-1 alpha-2 (مثل "US"، "GB"، "IN") |
dialCode | string | رمز الاتصال (مثل "+1"، "+44"، "+91") |
name | string | اسم الدولة |
flag | string | علم الدولة (emoji) |
أمثلة
مع Babelize SDK
اقرن حقل إدخال الهاتف مع Babelize SDK للتحقق المراعي للإعدادات المحلية:
"use client";
import { useEffect, useState } from "react";
import { PhoneInput } from "@/components/ui/phone-input";
import { useBabelize } from "@babelize/sdk/react";
export function ContactPhone() {
const { locale } = useBabelize();
const [phone, setPhone] = useState("");
const [countryCode, setCountryCode] = useState("US");
// The locale picks the country — until the user overrides it through the
// menu or by pasting a `+` number, which `onCountryChange` must honour.
useEffect(() => {
setCountryCode(locale === "en" ? "US" : locale === "de" ? "DE" : "GB");
}, [locale]);
return (
<PhoneInput
value={phone}
country={countryCode}
onCountryChange={(country) => setCountryCode(country.code)}
onValueChange={(value, country) => {
setPhone(value);
// Validate against locale-specific rules
}}
placeholder="Enter phone number"
/>
);
}مع التحقق من النموذج
"use client";
import { useState } from "react";
import { PhoneInput } from "@/components/ui/phone-input";
export default function CheckoutForm() {
const [phone, setPhone] = useState("");
const [error, setError] = useState("");
const validate = (value: string) => {
if (value.length < 10) {
setError("Phone number is too short");
} else {
setError("");
}
};
return (
<div>
<PhoneInput
value={phone}
onValueChange={(value) => {
setPhone(value);
validate(value);
}}
defaultCountry="US"
placeholder="Enter phone number"
aria-invalid={error ? true : undefined}
/>
{error && <p className="mt-1 text-sm text-destructive">{error}</p>}
</div>
);
}مع فلتر الدول
"use client";
import { COUNTRIES, PhoneInput } from "@/components/ui/phone-input";
// Only the markets you ship to, in the order you want them listed.
const SUPPORTED = ["US", "GB", "CA", "AU"].map((code) =>
COUNTRIES.find((country) => country.code === code)!,
);
export default function App() {
return (
<PhoneInput
countries={SUPPORTED}
defaultCountry="US"
onValueChange={(phone, country) => console.log(phone, country)}
/>
);
}تخصيص الأجزاء
يأخذ كل جزء أصنافه الخاصة عبر classNames، وتُدمج بعد القيم الافتراضية:
<PhoneInput
size="lg"
align="end"
classNames={{
field: "rounded-full",
trigger: "ps-4",
content: "w-80",
item: "py-2",
}}
/>نصوص الواجهة المترجمة
مرّر labels لترجمة نص قائمة الدول، وdisplayLocale لعرض أسماء الدول بلغة أخرى. تأتي الأسماء من Intl.DisplayNames في المتصفح، لذا قد تختلف صياغتها قليلًا بين المتصفحات. يطابق البحث الاسم المترجم، بينما يظل onValueChange يُبلغ عن الاسم الإنجليزي للدولة name. تظل الخاصيتان searchPlaceholder وemptyMessage تعملان، وتتغلبان على labels عند ضبطهما معًا.
"use client";
import { PhoneInput } from "@/components/ui/phone-input";
export default function App() {
return (
<PhoneInput
defaultCountry="FR"
displayLocale="fr"
label="Numéro de téléphone"
placeholder="Numéro de téléphone"
labels={{
trigger: "Choisir un pays",
searchPlaceholder: "Rechercher un pays…",
searchLabel: "Rechercher un pays",
clearSearch: "Effacer la recherche",
emptyMessage: "Aucun pays trouvé.",
listbox: "Choisir un pays",
}}
/>
);
}| المفتاح | الافتراضي | يُستخدم في |
|---|---|---|
trigger | "Select country" | تسمية زر فتح قائمة الدول |
searchPlaceholder | "Search countries..." | النص النائب لحقل البحث |
searchLabel | "Search countries" | تسمية إمكانية الوصول لحقل البحث |
clearSearch | "Clear search" | تسمية إمكانية الوصول لزر المسح |
emptyMessage | "No countries found." | يُعرض عند عدم وجود تطابق |
listbox | "Select country" | تسمية إمكانية الوصول لقائمة الدول |
دعم لوحة المفاتيح
تتبع قائمة الدول نمط combobox في WAI-ARIA: يبقى التركيز في حقل البحث، الذي يُعرض كـ role="combobox"، ويُعلَن عن الدولة المميّزة عبر aria-activedescendant.
| المفتاح | الإجراء |
|---|---|
Enter، Space، أو النقر | افتح قائمة الدول من زر العلم |
ArrowDown / ArrowUp (مغلقة) | افتح مع تمييز أول دولة / آخر دولة |
ArrowDown / ArrowUp | انقل التمييز، مع الالتفاف عند الطرفين |
Home / End | مميّز أول دولة / آخر دولة |
Enter | اختر الدولة المميّزة؛ ينتقل التركيز إلى حقل الرقم |
Escape | أغلق القائمة وأعد التركيز إلى زر العلم |
| الكتابة | صفِّ حسب الاسم أو الرمز أو رمز الاتصال؛ يبدأ التمييز من الأعلى مجددًا |
يبقى التمييز على الصف الذي ظهر فيه عند فتح القائمة — وتتمرر القائمة أسفله، كالقائمة الأصلية — ولا تتمرر الصفحة نفسها أبدًا. مرّر maxHeight لتغيير ارتفاع قائمة الدول قبل أن تبدأ بالتمرير.
الميزات
- 50 دولة — محمّلة مسبقًا برموز الاتصال والأعلام والأسماء
- بحث في القائمة — صفِّ الدول حسب الاسم أو رمز الاتصال
- أعلام emoji — تُكتشف تلقائيًا من رمز الدولة، مع مفتاح
showFlags - متاح عبر لوحة المفاتيح — التنقل بمفاتيح الأسهم وHome / End وEnter وEscape
- تسميات ARIA — حقل البحث
role="combobox"معaria-activedescendant، إضافةً إلىaria-expandedوaria-haspopupوaria-selected - قابل للتخصيص بالسمات — منسّق برموز shadcn/ui (
border-input،bg-popover،ring-ring)، فيتبع سمتك ووضع الداكن - قائمة متحركة — تتلاشى ظهورًا واختفاءً عبر
motion، مع احترام إعدادات تقليل الحركة، وتتجه للأعلى عند عدم وجود مساحة أسفلها - متحكَّم به / غير متحكَّم به — يعمل مع
valueوcountryأو بدونهما - اكتشاف رمز الاتصال — كتابة
+44…أو لصقه يبدّل الدولة إلى GB ويحذف البادئة؛ والرموز المشتركة (+1،+7) تُبقي التحديد الحالي
الدول المدعومة
يأتي الحقل مع 50 دولة محمّلة مسبقًا. استخدم بحث القائمة المنسدلة للعثور على أي دولة بالاسم أو رمز الاتصال. تشمل القائمة الكاملة:
| الدولة | الرمز | رمز الاتصال | العلم |
|---|---|---|---|
| الولايات المتحدة | US | +1 | 🇺🇸 |
| المملكة المتحدة | GB | +44 | 🇬🇧 |
| كندا | CA | +1 | 🇨🇦 |
| أستراليا | AU | +61 | 🇦🇺 |
| الهند | IN | +91 | 🇮🇳 |
| ألمانيا | DE | +49 | 🇩🇪 |
| فرنسا | FR | +33 | 🇫🇷 |
| اليابان | JP | +81 | 🇯🇵 |
| البرازيل | BR | +55 | 🇧🇷 |
| كوريا الجنوبية | KR | +82 | 🇰🇷 |
| ...و40 دولة أخرى عبر بحث القائمة المنسدلة |
الاعتماديات
يستخدم هذا المكوّن:
clsxوtailwind-merge(مُضمّنة كاعتماديات)lucide-reactللأيقونات وmotionلحركة القائمة
الاعتماديات النظيرة:
react>= 18react-dom>= 18tailwindcss>= 3
الإسناد
من إنشاء sapatmohit.