@ desource/ phone- mask- react
使用智能口罩和Google libphone数字数据进行电话输入组件和钩子
美丽,可访问,极小和可树可震动的电话回放输入,并带有自动格式化,国家选择器,以及验证功能.
升级到 2. 0. 0 之前,审查 1.7.0的移徙 用于断开更改和需要更新.
* 特性
- 🌐 安保部门支助 —— 说
PhoneInput在服务器上使 HTML 格式化,并在浏览器中使水合物格式化 - 🎨 美丽的UI ——以轻/暗为主题的现代设计
- 🔍 智能国家搜索 ——模糊匹配键盘导航
- 🎭 自动格式化 ——有智能光标的现用类型格式
- ✅ 验证 ——内设有视觉反馈的验证
- 📋 复制按钮 — 一击副本到剪贴板
- 🌐 自动检测 ——地生化和地生化探测
- ♿ 可访问 ——ARIA标签,键盘导航
- 📱 便于移动 - 优化触摸装置
- 🎯 类型脚本 - 全套安全
- 🪝 虎克 API - 用于自定义输入执行
- ⚡ 优化 - 树摇和代码分割
粘贴一个完整的国际编号,从 + 检测其国家并替换全部输入.
检测尝试精确的口罩,然后呼叫代码和数字计数;共享匹配使用调用代码的默认国家.
它使用遮罩数据,而不是分配前缀,所以一个未格式化的加拿大人 +1 数字默认为美国.
没有呼叫代码的碎片保留了选定的国家,并粘贴在护身符上. 这能独立运作 detect,用于控制 GeoIP 和 locale 检测.
* 安装
npm install @desource/phone-mask-react
# or
yarn add @desource/phone-mask-react
# or
pnpm add @desource/phone-mask-react
QQ 快速启动
导入
组件模式 :
import { PhoneInput } from '@desource/phone-mask-react';
import '@desource/phone-mask-react/assets/lib.css'; // Import styles
钩模式 :
import { usePhoneMask } from '@desource/phone-mask-react';
从核心包子路径再出口的核心工具包和掩码数据:
import { getFlagEmoji, formatDigitsWithMap } from '@desource/phone-mask-react/core';
组件模式
import { useState } from 'react';
import { PhoneInput } from '@desource/phone-mask-react';
import '@desource/phone-mask-react/assets/lib.css'; // Import styles
function App() {
const [phone, setPhone] = useState('');
const [isValid, setIsValid] = useState(false);
return (
<>
<PhoneInput value={phone} onChange={setPhone} onValidationChange={setIsValid} country="US" />
{isValid && <p>✓ Valid phone number</p>}
</>
);
}
钩子模式
关于自定义输入执行:
import { useState } from 'react';
import { usePhoneMask } from '@desource/phone-mask-react';
function CustomPhoneInput() {
const [value, setValue] = useState('');
const { ref, digits, full, fullFormatted, isComplete, country, setCountry } = usePhoneMask({
value,
onChange: setValue,
country: 'US',
detect: true
});
return (
<div>
<input ref={ref} type="tel" placeholder="Phone number" />
<p>Formatted: {fullFormatted}</p>
<p>Valid: {isComplete ? 'Yes' : 'No'}</p>
<p>Country: {country.name}</p>
<button onClick={() => setCountry('GB')}>Use UK</button>
</div>
);
}
发送 Raw 数字到后端
import { useState } from 'react';
import { PhoneInput, type PMaskPhoneNumber } from '@desource/phone-mask-react';
function CheckoutForm() {
const [digits, setDigits] = useState('');
const handlePhoneChange = (phone: PMaskPhoneNumber) => {
const payload = {
phoneDigits: phone.digits, // unformatted, backend-friendly
phoneFull: phone.full // optional full number with country code
};
setDigits(phone.digits);
void fetch('/api/profile/phone', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(payload)
});
};
return <PhoneInput value={digits} onChange={setDigits} country="US" onPhoneChange={handlePhoneChange} />;
}
关于国家变化的动态遮罩更新
import { useMemo, useState } from 'react';
import { usePhoneMask, type PCountryKey } from '@desource/phone-mask-react';
function CountryAwareInput() {
const [digits, setDigits] = useState('');
const [country, setCountry] = useState<PCountryKey>('US');
const {
ref,
fullFormatted,
isComplete,
setCountry: setMaskCountry
} = usePhoneMask({
value: digits,
onChange: setDigits,
country
});
const onCountrySelect = (next: PCountryKey) => {
setCountry(next);
setMaskCountry(next); // update formatter immediately
};
const label = useMemo(() => (isComplete ? 'complete' : 'incomplete'), [isComplete]);
return (
<div>
<select value={country} onChange={(e) => onCountrySelect(e.target.value as PCountryKey)}>
<option value="US">US</option>
<option value="GB">GB</option>
<option value="DE">DE</option>
</select>
<input ref={ref} type="tel" />
<p>{fullFormatted}</p>
<p>{label}</p>
</div>
);
}
多租户:租户Id 默认国家+特定租户审定规则
import { useMemo, useState } from 'react';
import { usePhoneMask, type PCountryKey } from '@desource/phone-mask-react';
type TenantPolicy = {
defaultCountry: PCountryKey;
prefixRule?: RegExp;
};
const TENANT_POLICIES: Record<string, TenantPolicy> = {
acme: { defaultCountry: 'US', prefixRule: /^(202|303)\d{7}$/ },
globex: { defaultCountry: 'GB', prefixRule: /^7\d{9}$/ }
};
export function TenantPhoneInput({ tenantId }: { tenantId: string }) {
const policy = TENANT_POLICIES[tenantId] ?? { defaultCountry: 'US' as const };
const [digits, setDigits] = useState('');
const phoneMask = usePhoneMask({
value: digits,
onChange: setDigits,
country: policy.defaultCountry
});
const isTenantValid = useMemo(() => {
if (!phoneMask.isComplete) return false;
return policy.prefixRule ? policy.prefixRule.test(phoneMask.digits) : true;
}, [phoneMask.isComplete, phoneMask.digits, policy.prefixRule]);
return (
<div>
<input ref={phoneMask.ref} type="tel" />
<p>Default country: {policy.defaultCountry}</p>
<p>Tenant validation: {isTenantValid ? 'pass' : 'fail'}</p>
</div>
);
}
++ 显示格式
打电话 formatPhone(value, country?, options?) 直接载于JSX。 它返回文本, 不需要 CSS .
import { formatPhone } from '@desource/phone-mask-react/core';
function Contact({ phone }: { phone: string }) {
return <span>{formatPhone(phone, 'US', { code: false, flag: true })}</span>;
}
// '+12345678888' displays as '🇺🇸 234 567 8888'.
奥米 country 以检测一个完整的国际编号 +; 传递给没有调用代码的号码, 或解析美国/CA等共享调用代码. 可选 PhoneFormatOptions 参数接受 code (默认) true和 flag (默认) false) (中文(简体) ).
该功能在SSF期间有效. 不支持的值保持不变; 空白, 仅空白, null,以及 undefined 值返回空文本。 见 格式规则 用于检测限度和实例.
* 构件 API
道具
说明: 该组件需要受控行为. 两者
value财务报告和审定财务报表onChange需要道具.
interface PhoneInputProps {
// Controlled value (digits only, without country code) - REQUIRED
value: string;
// Optional id/name applied to the underlying <input> for forms/autofill
id?: string;
name?: string;
// Preselected country (ISO 3166-1 alpha-2)
country?: CountryKey;
// Auto-detect country from IP/locale
detect?: boolean; // Default: true
// Locale for country names
locale?: string; // Default: browser language
// Size variant
size?: 'compact' | 'normal' | 'large'; // Default: 'normal'
// Visual theme ("auto" | "light" | "dark")
theme?: 'auto' | 'light' | 'dark'; // Default: 'auto'
// Disabled state
disabled?: boolean; // Default: false
// Readonly state
readonly?: boolean; // Default: false
// Show copy button
showCopy?: boolean; // Default: true
// Show clear button
showClear?: boolean; // Default: false
// Show validation state (borders & outline)
withValidity?: boolean; // Default: true
// Custom search placeholder
searchPlaceholder?: string; // Default: 'Search country or code...'
// Custom no results text
noResultsText?: string; // Default: 'No countries found'
// Custom clear button label
clearButtonLabel?: string; // Default: 'Clear phone number'
// Dropdown menu custom CSS class
dropdownClass?: string;
// Disable default styles
disableDefaultStyles?: boolean; // Default: false
// Callback when the digits value changes - REQUIRED
// Returns only the digits without country code (e.g. '234567890')
onChange: (digits: string) => void;
// Callback when the phone number changes.
// Provides an object with:
// - full: Full phone number with country code (e.g. +1234567890)
// - fullFormatted: Full phone number formatted according to country rules (e.g. +1 234-567-890)
// - digits: Only the digits of the phone number without country code (e.g. 234567890)
onPhoneChange?: (value: PMaskPhoneNumber) => void;
// Callback when the selected country changes
onCountryChange?: (country: MaskFull) => void;
// Callback when the validation state changes
onValidationChange?: (isValid: boolean) => void;
// Callback when the input is focused
onFocus?: (event: FocusEvent<HTMLInputElement>) => void;
// Callback when the input is blurred
onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
// Callback when phone number is copied
onCopy?: (value: string) => void;
// Callback when input is cleared
onClear?: () => void;
// Render custom action buttons before default ones
renderActionsBefore?: () => ReactNode;
// Render custom flag icons in the country list and country selector
renderFlag?: (country: MaskFull) => ReactNode;
// Render custom copy button SVG
renderCopySvg?: (copied: boolean) => ReactNode;
// Render custom clear button SVG
renderClearSvg?: () => ReactNode;
}
查找方法
const phoneInputRef = useRef<PhoneInputRef>(null);
phoneInputRef.current?.focus(); // Focuses the input
phoneInputRef.current?.blur(); // Blurs the input
phoneInputRef.current?.clear(); // Clears the input value
phoneInputRef.current?.selectCountry('GB'); // Programmatically selects a country by ISO code (e.g., 'US', 'DE', 'GB')
phoneInputRef.current?.getFullNumber(); // Returns the full phone number with country code (e.g., +1234567890)
phoneInputRef.current?.getFullFormattedNumber(); // Returns the full phone number formatted according to country rules (e.g., +1 234-567-890)
phoneInputRef.current?.getDigits(); // Returns only the digits of the phone number without country code (e.g., 234567890)
phoneInputRef.current?.isValid(); // Checks if the current phone number is valid
phoneInputRef.current?.isComplete(); // Checks if the current phone number is complete
胡克 API
选项
说明: 钩子需要受控的行为. 两者
value财务报告和审定财务报表onChange需要选项.
interface UsePhoneMaskOptions {
// Controlled value (digits only, without country code) - REQUIRED
value: string;
// Callback when the digits value changes - REQUIRED
onChange: (digits: string) => void;
// Predefined country ISO code (e.g., 'US', 'DE', 'GB')
country?: string;
// Locale for country names (default: navigator.language)
locale?: string;
// Auto-detect country from IP/locale (default: false)
detect?: boolean;
// Callback when the phone changes (full, fullFormatted, digits)
onPhoneChange?: (phone: PMaskPhoneNumber) => void;
// Country change callback
onCountryChange?: (country: MaskFull) => void;
}
返回值
interface UsePhoneMaskReturn {
// Ref to attach to your input element
ref: RefObject<HTMLInputElement | null>;
// Raw digits without formatting (e.g., "1234567890")
digits: string;
// Phone formatter instance
formatter: FormatterHelpers;
// Full phone number with country code (e.g., "+11234567890")
full: string;
// Full phone number formatted (e.g., "+1 123-456-7890")
fullFormatted: string;
// Whether the phone number is complete
isComplete: boolean;
// Whether the input is empty
isEmpty: boolean;
// Whether to show validation warning
shouldShowWarn: boolean;
// Current country data
country: MaskFull;
// Change country programmatically
setCountry: (countryCode: string) => void;
// Clear the input
clear: () => void;
}
组件样式
CSS 自定义属性
通过 CSS 变量自定义颜色 :
.desource-phone-input,
.desource-phone-dropdown {
/* Colors */
--pi-bg: #ffffff;
--pi-fg: #111827;
--pi-muted: #6b7280;
--pi-border: #e5e7eb;
--pi-border-hover: #d1d5db;
--pi-border-focus: #3b82f6;
--pi-focus-ring: 3px solid rgb(59 130 246 / 0.15);
--pi-disabled-bg: #f9fafb;
--pi-disabled-fg: #9ca3af;
/* Sizes */
--pi-font-size: 16px;
--pi-height: 44px;
/* Spacing */
--pi-padding: 12px;
/* Border radius */
--pi-radius: 8px;
/* Shadows */
--pi-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--pi-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -2px rgb(0 0 0 / 0.05);
/* Validation */
--pi-warning: #f59e0b;
--pi-warning-light: #fbbf24;
--pi-success: #10b981;
--pi-focus-ring-warning: 3px solid rgb(245 158 11 / 0.15);
--pi-focus-ring-success: 3px solid rgb(16 185 129 / 0.15);
}
黑暗主题
<PhoneInput value={phone} onChange={setPhone} theme="dark" />
或与CSS: (中文(简体) )
.desource-phone-input.theme-dark {
--pi-bg: #1f2937;
--pi-fg: #f9fafb;
--pi-border: #374151;
}
实例
有验证
import { useState } from 'react';
import { PhoneInput } from '@desource/phone-mask-react';
function Example() {
const [phone, setPhone] = useState('');
const [isValid, setIsValid] = useState(false);
return (
<div>
<PhoneInput value={phone} onChange={setPhone} onValidationChange={setIsValid} />
{isValid && <span>✓ Valid phone number</span>}
</div>
);
}
自动检测国家
import { useState } from 'react';
import { PhoneInput } from '@desource/phone-mask-react';
function Example() {
const [phone, setPhone] = useState('');
const [detectedCountry, setDetectedCountry] = useState('');
return (
<>
<PhoneInput
value={phone}
detect
onChange={setPhone}
onCountryChange={(country) => setDetectedCountry(country.name)}
/>
{detectedCountry && <p>Detected: {detectedCountry}</p>}
</>
);
}
带有窗体库
反应钩形
import { useForm, Controller } from 'react-hook-form';
import { PhoneInput } from '@desource/phone-mask-react';
function Example() {
const { control } = useForm({
defaultValues: { phone: '' }
});
return (
<Controller
name="phone"
control={control}
render={({ field }) => (
<PhoneInput value={field.value} onChange={(digits) => field.onChange(digits)} onBlur={field.onBlur} />
)}
/>
);
}
多个输入
import { useState } from 'react';
import { PhoneInput } from '@desource/phone-mask-react';
function Example() {
const [form, setForm] = useState({ mobile: '', home: '', work: '' });
return (
<div className="form">
<label>
Mobile
<PhoneInput value={form.mobile} onChange={(digits) => setForm({ ...form, mobile: digits })} />
</label>
<label>
Home
<PhoneInput value={form.home} onChange={(digits) => setForm({ ...form, home: digits })} />
</label>
<label>
Work
<PhoneInput value={form.work} onChange={(digits) => setForm({ ...form, work: digits })} />
</label>
</div>
);
}
QQ 浏览器支持
- 克罗梅/第90号+
- Firefox 88+ (英语)
- 萨法里14+
- iOS 萨法里14+
- Chrome 移动
包含什么
@desource/phone-mask-react/
├── dist/
│ ├── index.mjs # Main ESM entry
│ ├── index.cjs # Main CommonJS entry
│ ├── core.mjs # Core kit utilities and mask data subpath
│ ├── core.cjs # Core kit utilities and mask data CJS subpath
│ ├── phone-mask-react.css # Component styles
│ └── *.d.ts, *.d.cts # ESM and CommonJS declarations
├── README.md # This file
└── package.json # Package manifest
有关软件包
- @ desource/ phone- mask (英语) - 核心图书馆
- @ desource/ phone- mask- angular (英语) - 角装订
- @ desource/ phone- massk- svelte (英语) - 丝绒装订
- @ desource/ phone- mask- vue - Vue 3装订
- @ desource/ phone- mask- nuxt - 努克特模块
许可证
麻省理工学院 2026 来源实验室
捐款
见 贡献指南 用于开发、测试和出版检查.