@mieweb/ui 0.3.0-dev.67 → 0.3.0-dev.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-B43FRU5R.js → chunk-DLLVXNAQ.js} +3 -3
- package/dist/{chunk-B43FRU5R.js.map → chunk-DLLVXNAQ.js.map} +1 -1
- package/dist/{chunk-BTJHYGPI.cjs → chunk-L7YQBSEL.cjs} +14 -4
- package/dist/chunk-L7YQBSEL.cjs.map +1 -0
- package/dist/{chunk-7CLHYU4Q.cjs → chunk-N2YMRZ7P.cjs} +9 -9
- package/dist/{chunk-7CLHYU4Q.cjs.map → chunk-N2YMRZ7P.cjs.map} +1 -1
- package/dist/{chunk-CEHWXAAI.js → chunk-ZVPJ2MH6.js} +14 -4
- package/dist/chunk-ZVPJ2MH6.js.map +1 -0
- package/dist/components/PhoneInput/index.cjs +4 -4
- package/dist/components/PhoneInput/index.js +2 -2
- package/dist/index.cjs +8 -8
- package/dist/index.js +2 -2
- package/dist/utils/index.cjs +5 -5
- package/dist/utils/index.d.cts +7 -1
- package/dist/utils/index.d.ts +7 -1
- package/dist/utils/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-BTJHYGPI.cjs.map +0 -1
- package/dist/chunk-CEHWXAAI.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Input } from './chunk-XHESCAUE.js';
|
|
2
|
-
import { formatPhoneNumber, unformatPhoneNumber, isValidPhoneNumber } from './chunk-
|
|
2
|
+
import { formatPhoneNumber, unformatPhoneNumber, isValidPhoneNumber } from './chunk-ZVPJ2MH6.js';
|
|
3
3
|
import { cn } from './chunk-F3SOEIN2.js';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -243,5 +243,5 @@ function PhoneInputGroup({
|
|
|
243
243
|
PhoneInputGroup.displayName = "PhoneInputGroup";
|
|
244
244
|
|
|
245
245
|
export { PhoneInput, PhoneInputGroup };
|
|
246
|
-
//# sourceMappingURL=chunk-
|
|
247
|
-
//# sourceMappingURL=chunk-
|
|
246
|
+
//# sourceMappingURL=chunk-DLLVXNAQ.js.map
|
|
247
|
+
//# sourceMappingURL=chunk-DLLVXNAQ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/PhoneInput/PhoneInput.tsx"],"names":[],"mappings":"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","file":"chunk-B43FRU5R.js","sourcesContent":["import * as React from 'react';\nimport { cn } from '../../utils/cn';\nimport {\n formatPhoneNumber,\n unformatPhoneNumber,\n isValidPhoneNumber,\n} from '../../utils/phone';\nimport { Input, type InputProps } from '../Input';\n\nexport interface PhoneInputProps extends Omit<\n InputProps,\n 'type' | 'onChange' | 'value'\n> {\n /** The phone number value (can be formatted or unformatted) */\n value?: string;\n /** Callback fired when the value changes, receives the unformatted value */\n onChange?: (value: string) => void;\n /** Callback fired when the formatted value changes */\n onFormattedChange?: (formattedValue: string) => void;\n /** Whether to validate and show error state for incomplete phone numbers */\n validateOnBlur?: boolean;\n}\n\n/**\n * A phone number input that automatically formats to US format: (XXX) XXX-XXXX\n *\n * @example\n * ```tsx\n * const [phone, setPhone] = useState('');\n * <PhoneInput\n * label=\"Phone Number\"\n * value={phone}\n * onChange={setPhone}\n * validateOnBlur\n * />\n * ```\n */\nconst PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(\n (\n {\n value = '',\n onChange,\n onFormattedChange,\n validateOnBlur,\n className,\n onBlur,\n hasError,\n error,\n ...props\n },\n ref\n ) => {\n const [displayValue, setDisplayValue] = React.useState(() =>\n formatPhoneNumber(value)\n );\n const [localError, setLocalError] = React.useState<string | undefined>();\n\n // Sync external value changes\n React.useEffect(() => {\n setDisplayValue(formatPhoneNumber(value));\n }, [value]);\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const formatted = formatPhoneNumber(e.target.value);\n setDisplayValue(formatted);\n\n const unformatted = unformatPhoneNumber(formatted);\n onChange?.(unformatted);\n onFormattedChange?.(formatted);\n\n // Clear error when user starts typing again\n if (localError) {\n setLocalError(undefined);\n }\n };\n\n const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {\n onBlur?.(e);\n\n if (validateOnBlur) {\n const unformatted = unformatPhoneNumber(displayValue);\n if (unformatted.length > 0 && !isValidPhoneNumber(displayValue)) {\n setLocalError('Please enter a valid 10-digit phone number');\n } else {\n setLocalError(undefined);\n }\n }\n };\n\n return (\n <Input\n ref={ref}\n type=\"tel\"\n inputMode=\"numeric\"\n autoComplete=\"tel-national\"\n placeholder=\"(555) 555-5555\"\n value={displayValue}\n onChange={handleChange}\n onBlur={handleBlur}\n hasError={hasError || !!localError}\n error={error || localError}\n className={cn(className)}\n {...props}\n />\n );\n }\n);\n\nPhoneInput.displayName = 'PhoneInput';\n\n// =============================================================================\n// Phone Types\n// =============================================================================\n\nexport type PhoneType = 'cell' | 'landline' | 'home' | 'work' | 'fax';\n\nexport interface PhoneEntry {\n number: string;\n type: PhoneType;\n}\n\nconst PHONE_TYPES: { value: PhoneType; label: string }[] = [\n { value: 'cell', label: 'Cell' },\n { value: 'landline', label: 'Landline' },\n { value: 'home', label: 'Home' },\n { value: 'work', label: 'Work' },\n { value: 'fax', label: 'Fax' },\n];\n\n// =============================================================================\n// PhoneInputGroup\n// =============================================================================\n\nexport interface PhoneInputGroupProps {\n /** Array of phone entries */\n value: PhoneEntry[];\n /** Callback when phone entries change */\n onChange: (phones: PhoneEntry[]) => void;\n /** Minimum number of phone entries (default: 1) */\n minEntries?: number;\n /** Maximum number of phone entries (default: 5) */\n maxEntries?: number;\n /** Whether the first entry is required */\n required?: boolean;\n /** Whether all inputs are disabled */\n disabled?: boolean;\n /** Validate on blur */\n validateOnBlur?: boolean;\n /** Label for the phone input */\n label?: string;\n /** Labels for type options (for i18n) */\n typeLabels?: Record<PhoneType, string>;\n /** Custom className */\n className?: string;\n}\n\n/**\n * A group of phone inputs with type selection and add/remove functionality.\n *\n * @example\n * ```tsx\n * const [phones, setPhones] = useState<PhoneEntry[]>([\n * { number: '', type: 'cell' }\n * ]);\n *\n * <PhoneInputGroup\n * value={phones}\n * onChange={setPhones}\n * required\n * />\n * ```\n */\nfunction PhoneInputGroup({\n value,\n onChange,\n minEntries = 1,\n maxEntries = 5,\n required = false,\n disabled = false,\n validateOnBlur = false,\n label,\n typeLabels,\n className,\n}: PhoneInputGroupProps) {\n // Ensure we always have at least minEntries\n const phones = React.useMemo(() => {\n if (value.length >= minEntries) return value;\n const padding: PhoneEntry[] = Array(minEntries - value.length)\n .fill(null)\n .map(() => ({ number: '', type: 'cell' as PhoneType }));\n return [...value, ...padding];\n }, [value, minEntries]);\n\n const handlePhoneChange = (index: number, number: string) => {\n const updated = [...phones];\n updated[index] = { ...updated[index], number };\n onChange(updated);\n };\n\n const handleTypeChange = (index: number, type: PhoneType) => {\n const updated = [...phones];\n updated[index] = { ...updated[index], type };\n onChange(updated);\n };\n\n const handleAdd = () => {\n if (phones.length < maxEntries) {\n onChange([...phones, { number: '', type: 'cell' }]);\n }\n };\n\n const handleRemove = (index: number) => {\n if (phones.length > minEntries) {\n onChange(phones.filter((_, i) => i !== index));\n }\n };\n\n const getTypeLabel = (type: PhoneType): string => {\n if (typeLabels?.[type]) return typeLabels[type];\n return PHONE_TYPES.find((t) => t.value === type)?.label || type;\n };\n\n const canAdd = phones.length < maxEntries;\n const canRemove = phones.length > minEntries;\n\n return (\n <div data-slot=\"phone-group\" className={cn('space-y-3', className)}>\n {phones.map((phone, index) => (\n <div\n key={index}\n data-slot=\"phone-row\"\n className=\"flex items-start gap-2\"\n >\n {/* Phone number input */}\n <div className=\"flex-1\">\n <PhoneInput\n label={index === 0 ? label : undefined}\n value={phone.number}\n onChange={(num) => handlePhoneChange(index, num)}\n disabled={disabled}\n validateOnBlur={validateOnBlur}\n required={required && index === 0}\n />\n </div>\n\n {/* Type selector */}\n <div className=\"w-24 shrink-0 sm:w-32\">\n <label htmlFor={`phone-type-${index}`} className=\"sr-only\">\n Phone type\n </label>\n <select\n id={`phone-type-${index}`}\n data-slot=\"phone-type-select\"\n value={phone.type}\n onChange={(e) =>\n handleTypeChange(index, e.target.value as PhoneType)\n }\n disabled={disabled}\n className={cn(\n 'h-10 w-full rounded-md border px-3 py-2 text-sm',\n 'border-gray-300 bg-white text-gray-900',\n 'focus:border-brand-500 focus:ring-brand-500/20 focus:ring-2 focus:outline-none',\n 'disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500',\n 'dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100',\n 'dark:focus:border-brand-400 dark:focus:ring-brand-400/20',\n index === 0 && label ? 'mt-[26px]' : ''\n )}\n >\n {PHONE_TYPES.map((type) => (\n <option key={type.value} value={type.value}>\n {getTypeLabel(type.value)}\n </option>\n ))}\n </select>\n </div>\n\n {/* Add/Remove buttons */}\n <div\n data-slot=\"phone-action\"\n className={cn(\n 'flex shrink-0 items-center',\n index === 0 && label ? 'mt-[26px]' : ''\n )}\n >\n {index === 0 ? (\n <button\n type=\"button\"\n data-slot=\"phone-add-btn\"\n onClick={handleAdd}\n disabled={disabled || !canAdd}\n className={cn(\n 'rounded-full p-2 transition-colors',\n 'text-brand-600 hover:bg-brand-50',\n 'disabled:cursor-not-allowed disabled:text-gray-300 disabled:hover:bg-transparent',\n 'dark:text-brand-400 dark:hover:bg-brand-900/20',\n 'dark:disabled:text-gray-600'\n )}\n aria-label=\"Add phone number\"\n >\n <svg\n data-slot=\"phone-add-icon\"\n className=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 4v16m8-8H4\"\n />\n </svg>\n </button>\n ) : (\n <button\n type=\"button\"\n data-slot=\"phone-remove-btn\"\n onClick={() => handleRemove(index)}\n disabled={disabled || !canRemove}\n className={cn(\n 'rounded-full p-2 transition-colors',\n 'text-red-600 hover:bg-red-50',\n 'disabled:cursor-not-allowed disabled:text-gray-300 disabled:hover:bg-transparent',\n 'dark:text-red-400 dark:hover:bg-red-900/20',\n 'dark:disabled:text-gray-600'\n )}\n aria-label=\"Remove phone number\"\n >\n <svg\n data-slot=\"phone-remove-icon\"\n className=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20 12H4\"\n />\n </svg>\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nPhoneInputGroup.displayName = 'PhoneInputGroup';\n\nexport { PhoneInput, PhoneInputGroup };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/PhoneInput/PhoneInput.tsx"],"names":[],"mappings":"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","file":"chunk-DLLVXNAQ.js","sourcesContent":["import * as React from 'react';\nimport { cn } from '../../utils/cn';\nimport {\n formatPhoneNumber,\n unformatPhoneNumber,\n isValidPhoneNumber,\n} from '../../utils/phone';\nimport { Input, type InputProps } from '../Input';\n\nexport interface PhoneInputProps extends Omit<\n InputProps,\n 'type' | 'onChange' | 'value'\n> {\n /** The phone number value (can be formatted or unformatted) */\n value?: string;\n /** Callback fired when the value changes, receives the unformatted value */\n onChange?: (value: string) => void;\n /** Callback fired when the formatted value changes */\n onFormattedChange?: (formattedValue: string) => void;\n /** Whether to validate and show error state for incomplete phone numbers */\n validateOnBlur?: boolean;\n}\n\n/**\n * A phone number input that automatically formats to US format: (XXX) XXX-XXXX\n *\n * @example\n * ```tsx\n * const [phone, setPhone] = useState('');\n * <PhoneInput\n * label=\"Phone Number\"\n * value={phone}\n * onChange={setPhone}\n * validateOnBlur\n * />\n * ```\n */\nconst PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(\n (\n {\n value = '',\n onChange,\n onFormattedChange,\n validateOnBlur,\n className,\n onBlur,\n hasError,\n error,\n ...props\n },\n ref\n ) => {\n const [displayValue, setDisplayValue] = React.useState(() =>\n formatPhoneNumber(value)\n );\n const [localError, setLocalError] = React.useState<string | undefined>();\n\n // Sync external value changes\n React.useEffect(() => {\n setDisplayValue(formatPhoneNumber(value));\n }, [value]);\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const formatted = formatPhoneNumber(e.target.value);\n setDisplayValue(formatted);\n\n const unformatted = unformatPhoneNumber(formatted);\n onChange?.(unformatted);\n onFormattedChange?.(formatted);\n\n // Clear error when user starts typing again\n if (localError) {\n setLocalError(undefined);\n }\n };\n\n const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {\n onBlur?.(e);\n\n if (validateOnBlur) {\n const unformatted = unformatPhoneNumber(displayValue);\n if (unformatted.length > 0 && !isValidPhoneNumber(displayValue)) {\n setLocalError('Please enter a valid 10-digit phone number');\n } else {\n setLocalError(undefined);\n }\n }\n };\n\n return (\n <Input\n ref={ref}\n type=\"tel\"\n inputMode=\"numeric\"\n autoComplete=\"tel-national\"\n placeholder=\"(555) 555-5555\"\n value={displayValue}\n onChange={handleChange}\n onBlur={handleBlur}\n hasError={hasError || !!localError}\n error={error || localError}\n className={cn(className)}\n {...props}\n />\n );\n }\n);\n\nPhoneInput.displayName = 'PhoneInput';\n\n// =============================================================================\n// Phone Types\n// =============================================================================\n\nexport type PhoneType = 'cell' | 'landline' | 'home' | 'work' | 'fax';\n\nexport interface PhoneEntry {\n number: string;\n type: PhoneType;\n}\n\nconst PHONE_TYPES: { value: PhoneType; label: string }[] = [\n { value: 'cell', label: 'Cell' },\n { value: 'landline', label: 'Landline' },\n { value: 'home', label: 'Home' },\n { value: 'work', label: 'Work' },\n { value: 'fax', label: 'Fax' },\n];\n\n// =============================================================================\n// PhoneInputGroup\n// =============================================================================\n\nexport interface PhoneInputGroupProps {\n /** Array of phone entries */\n value: PhoneEntry[];\n /** Callback when phone entries change */\n onChange: (phones: PhoneEntry[]) => void;\n /** Minimum number of phone entries (default: 1) */\n minEntries?: number;\n /** Maximum number of phone entries (default: 5) */\n maxEntries?: number;\n /** Whether the first entry is required */\n required?: boolean;\n /** Whether all inputs are disabled */\n disabled?: boolean;\n /** Validate on blur */\n validateOnBlur?: boolean;\n /** Label for the phone input */\n label?: string;\n /** Labels for type options (for i18n) */\n typeLabels?: Record<PhoneType, string>;\n /** Custom className */\n className?: string;\n}\n\n/**\n * A group of phone inputs with type selection and add/remove functionality.\n *\n * @example\n * ```tsx\n * const [phones, setPhones] = useState<PhoneEntry[]>([\n * { number: '', type: 'cell' }\n * ]);\n *\n * <PhoneInputGroup\n * value={phones}\n * onChange={setPhones}\n * required\n * />\n * ```\n */\nfunction PhoneInputGroup({\n value,\n onChange,\n minEntries = 1,\n maxEntries = 5,\n required = false,\n disabled = false,\n validateOnBlur = false,\n label,\n typeLabels,\n className,\n}: PhoneInputGroupProps) {\n // Ensure we always have at least minEntries\n const phones = React.useMemo(() => {\n if (value.length >= minEntries) return value;\n const padding: PhoneEntry[] = Array(minEntries - value.length)\n .fill(null)\n .map(() => ({ number: '', type: 'cell' as PhoneType }));\n return [...value, ...padding];\n }, [value, minEntries]);\n\n const handlePhoneChange = (index: number, number: string) => {\n const updated = [...phones];\n updated[index] = { ...updated[index], number };\n onChange(updated);\n };\n\n const handleTypeChange = (index: number, type: PhoneType) => {\n const updated = [...phones];\n updated[index] = { ...updated[index], type };\n onChange(updated);\n };\n\n const handleAdd = () => {\n if (phones.length < maxEntries) {\n onChange([...phones, { number: '', type: 'cell' }]);\n }\n };\n\n const handleRemove = (index: number) => {\n if (phones.length > minEntries) {\n onChange(phones.filter((_, i) => i !== index));\n }\n };\n\n const getTypeLabel = (type: PhoneType): string => {\n if (typeLabels?.[type]) return typeLabels[type];\n return PHONE_TYPES.find((t) => t.value === type)?.label || type;\n };\n\n const canAdd = phones.length < maxEntries;\n const canRemove = phones.length > minEntries;\n\n return (\n <div data-slot=\"phone-group\" className={cn('space-y-3', className)}>\n {phones.map((phone, index) => (\n <div\n key={index}\n data-slot=\"phone-row\"\n className=\"flex items-start gap-2\"\n >\n {/* Phone number input */}\n <div className=\"flex-1\">\n <PhoneInput\n label={index === 0 ? label : undefined}\n value={phone.number}\n onChange={(num) => handlePhoneChange(index, num)}\n disabled={disabled}\n validateOnBlur={validateOnBlur}\n required={required && index === 0}\n />\n </div>\n\n {/* Type selector */}\n <div className=\"w-24 shrink-0 sm:w-32\">\n <label htmlFor={`phone-type-${index}`} className=\"sr-only\">\n Phone type\n </label>\n <select\n id={`phone-type-${index}`}\n data-slot=\"phone-type-select\"\n value={phone.type}\n onChange={(e) =>\n handleTypeChange(index, e.target.value as PhoneType)\n }\n disabled={disabled}\n className={cn(\n 'h-10 w-full rounded-md border px-3 py-2 text-sm',\n 'border-gray-300 bg-white text-gray-900',\n 'focus:border-brand-500 focus:ring-brand-500/20 focus:ring-2 focus:outline-none',\n 'disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500',\n 'dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100',\n 'dark:focus:border-brand-400 dark:focus:ring-brand-400/20',\n index === 0 && label ? 'mt-[26px]' : ''\n )}\n >\n {PHONE_TYPES.map((type) => (\n <option key={type.value} value={type.value}>\n {getTypeLabel(type.value)}\n </option>\n ))}\n </select>\n </div>\n\n {/* Add/Remove buttons */}\n <div\n data-slot=\"phone-action\"\n className={cn(\n 'flex shrink-0 items-center',\n index === 0 && label ? 'mt-[26px]' : ''\n )}\n >\n {index === 0 ? (\n <button\n type=\"button\"\n data-slot=\"phone-add-btn\"\n onClick={handleAdd}\n disabled={disabled || !canAdd}\n className={cn(\n 'rounded-full p-2 transition-colors',\n 'text-brand-600 hover:bg-brand-50',\n 'disabled:cursor-not-allowed disabled:text-gray-300 disabled:hover:bg-transparent',\n 'dark:text-brand-400 dark:hover:bg-brand-900/20',\n 'dark:disabled:text-gray-600'\n )}\n aria-label=\"Add phone number\"\n >\n <svg\n data-slot=\"phone-add-icon\"\n className=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 4v16m8-8H4\"\n />\n </svg>\n </button>\n ) : (\n <button\n type=\"button\"\n data-slot=\"phone-remove-btn\"\n onClick={() => handleRemove(index)}\n disabled={disabled || !canRemove}\n className={cn(\n 'rounded-full p-2 transition-colors',\n 'text-red-600 hover:bg-red-50',\n 'disabled:cursor-not-allowed disabled:text-gray-300 disabled:hover:bg-transparent',\n 'dark:text-red-400 dark:hover:bg-red-900/20',\n 'dark:disabled:text-gray-600'\n )}\n aria-label=\"Remove phone number\"\n >\n <svg\n data-slot=\"phone-remove-icon\"\n className=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20 12H4\"\n />\n </svg>\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nPhoneInputGroup.displayName = 'PhoneInputGroup';\n\nexport { PhoneInput, PhoneInputGroup };\n"]}
|
|
@@ -2,8 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
// src/utils/phone.ts
|
|
4
4
|
function formatPhoneNumber(value) {
|
|
5
|
-
const
|
|
6
|
-
if (
|
|
5
|
+
const allDigits = value.replace(/\D/g, "");
|
|
6
|
+
if (allDigits.length === 0) return "";
|
|
7
|
+
if (allDigits.length === 11 && allDigits.startsWith("1")) {
|
|
8
|
+
const rest = allDigits.slice(1);
|
|
9
|
+
return `+1 (${rest.slice(0, 3)}) ${rest.slice(3, 6)}-${rest.slice(6)}`;
|
|
10
|
+
}
|
|
11
|
+
if (allDigits.length > 11) {
|
|
12
|
+
const cc = allDigits.slice(0, allDigits.length - 10);
|
|
13
|
+
const nat = allDigits.slice(-10);
|
|
14
|
+
return `+${cc} (${nat.slice(0, 3)}) ${nat.slice(3, 6)}-${nat.slice(6)}`;
|
|
15
|
+
}
|
|
16
|
+
const digits = allDigits.slice(0, 10);
|
|
7
17
|
if (digits.length <= 3) return `(${digits}`;
|
|
8
18
|
if (digits.length <= 6) return `(${digits.slice(0, 3)}) ${digits.slice(3)}`;
|
|
9
19
|
return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
|
|
@@ -23,5 +33,5 @@ exports.formatPhoneNumber = formatPhoneNumber;
|
|
|
23
33
|
exports.isPhoneNumberEmpty = isPhoneNumberEmpty;
|
|
24
34
|
exports.isValidPhoneNumber = isValidPhoneNumber;
|
|
25
35
|
exports.unformatPhoneNumber = unformatPhoneNumber;
|
|
26
|
-
//# sourceMappingURL=chunk-
|
|
27
|
-
//# sourceMappingURL=chunk-
|
|
36
|
+
//# sourceMappingURL=chunk-L7YQBSEL.cjs.map
|
|
37
|
+
//# sourceMappingURL=chunk-L7YQBSEL.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/phone.ts"],"names":[],"mappings":";;;AAaO,SAAS,kBAAkB,KAAA,EAAuB;AACvD,EAAA,MAAM,SAAA,GAAY,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AACzC,EAAA,IAAI,SAAA,CAAU,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AAInC,EAAA,IAAI,UAAU,MAAA,KAAW,EAAA,IAAM,SAAA,CAAU,UAAA,CAAW,GAAG,CAAA,EAAG;AACxD,IAAA,MAAM,IAAA,GAAO,SAAA,CAAU,KAAA,CAAM,CAAC,CAAA;AAC9B,IAAA,OAAO,OAAO,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,EAAA,EAAK,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA,EAAI,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AAAA,EACtE;AAKA,EAAA,IAAI,SAAA,CAAU,SAAS,EAAA,EAAI;AACzB,IAAA,MAAM,KAAK,SAAA,CAAU,KAAA,CAAM,CAAA,EAAG,SAAA,CAAU,SAAS,EAAE,CAAA;AACnD,IAAA,MAAM,GAAA,GAAM,SAAA,CAAU,KAAA,CAAM,GAAG,CAAA;AAC/B,IAAA,OAAO,IAAI,EAAE,CAAA,EAAA,EAAK,IAAI,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,EAAA,EAAK,GAAA,CAAI,KAAA,CAAM,GAAG,CAAC,CAAC,IAAI,GAAA,CAAI,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AAAA,EACvE;AAGA,EAAA,MAAM,MAAA,GAAS,SAAA,CAAU,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AACpC,EAAA,IAAI,MAAA,CAAO,MAAA,IAAU,CAAA,EAAG,OAAO,IAAI,MAAM,CAAA,CAAA;AACzC,EAAA,IAAI,MAAA,CAAO,MAAA,IAAU,CAAA,EAAG,OAAO,IAAI,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,EAAA,EAAK,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACzE,EAAA,OAAO,IAAI,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,EAAA,EAAK,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA,EAAI,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACzE;AAKO,SAAS,oBAAoB,KAAA,EAAuB;AACzD,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAChC;AAKO,SAAS,mBAAmB,KAAA,EAAwB;AACzD,EAAA,MAAM,MAAA,GAAS,oBAAoB,KAAK,CAAA;AACxC,EAAA,OAAO,OAAO,MAAA,KAAW,EAAA;AAC3B;AAKO,SAAS,mBAAmB,KAAA,EAAwB;AACzD,EAAA,OAAO,mBAAA,CAAoB,KAAK,CAAA,CAAE,MAAA,KAAW,CAAA;AAC/C","file":"chunk-L7YQBSEL.cjs","sourcesContent":["/**\n * Phone number formatting utilities\n */\n\n/**\n * Formats a phone number string to US format.\n *\n * Behavior:\n * - 10 digits → `(XXX) XXX-XXXX`\n * - 11 digits w/ `1` → `+1 (XXX) XXX-XXXX` (E.164 US/Canada)\n * - 12+ digits → `+CC (XXX) XXX-XXXX...` (best-effort intl)\n * - partial (typing) → progressive `(`, `(XXX`, `(XXX) X`, etc.\n */\nexport function formatPhoneNumber(value: string): string {\n const allDigits = value.replace(/\\D/g, '');\n if (allDigits.length === 0) return '';\n\n // E.164 with US/Canada country code (1XXXXXXXXXX). Show the country code\n // explicitly so phone numbers aren't shifted by one digit when displayed.\n if (allDigits.length === 11 && allDigits.startsWith('1')) {\n const rest = allDigits.slice(1);\n return `+1 (${rest.slice(0, 3)}) ${rest.slice(3, 6)}-${rest.slice(6)}`;\n }\n\n // Other international numbers: keep the country code prefix and format the\n // trailing 10 digits as a US-style block. This isn't locale-perfect but\n // it's better than silently dropping the country code.\n if (allDigits.length > 11) {\n const cc = allDigits.slice(0, allDigits.length - 10);\n const nat = allDigits.slice(-10);\n return `+${cc} (${nat.slice(0, 3)}) ${nat.slice(3, 6)}-${nat.slice(6)}`;\n }\n\n // Domestic / partial input — progressive formatting as the user types.\n const digits = allDigits.slice(0, 10);\n if (digits.length <= 3) return `(${digits}`;\n if (digits.length <= 6) return `(${digits.slice(0, 3)}) ${digits.slice(3)}`;\n return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;\n}\n\n/**\n * Removes formatting from a phone number, returning only digits\n */\nexport function unformatPhoneNumber(value: string): string {\n return value.replace(/\\D/g, '');\n}\n\n/**\n * Validates if a phone number has exactly 10 digits\n */\nexport function isValidPhoneNumber(value: string): boolean {\n const digits = unformatPhoneNumber(value);\n return digits.length === 10;\n}\n\n/**\n * Checks if a phone number string is empty (no digits)\n */\nexport function isPhoneNumberEmpty(value: string): boolean {\n return unformatPhoneNumber(value).length === 0;\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chunk3XK5GENF_cjs = require('./chunk-3XK5GENF.cjs');
|
|
4
|
-
var
|
|
4
|
+
var chunkL7YQBSEL_cjs = require('./chunk-L7YQBSEL.cjs');
|
|
5
5
|
var chunkOR5DRJCW_cjs = require('./chunk-OR5DRJCW.cjs');
|
|
6
6
|
var React = require('react');
|
|
7
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -39,16 +39,16 @@ var PhoneInput = React__namespace.forwardRef(
|
|
|
39
39
|
...props
|
|
40
40
|
}, ref) => {
|
|
41
41
|
const [displayValue, setDisplayValue] = React__namespace.useState(
|
|
42
|
-
() =>
|
|
42
|
+
() => chunkL7YQBSEL_cjs.formatPhoneNumber(value)
|
|
43
43
|
);
|
|
44
44
|
const [localError, setLocalError] = React__namespace.useState();
|
|
45
45
|
React__namespace.useEffect(() => {
|
|
46
|
-
setDisplayValue(
|
|
46
|
+
setDisplayValue(chunkL7YQBSEL_cjs.formatPhoneNumber(value));
|
|
47
47
|
}, [value]);
|
|
48
48
|
const handleChange = (e) => {
|
|
49
|
-
const formatted =
|
|
49
|
+
const formatted = chunkL7YQBSEL_cjs.formatPhoneNumber(e.target.value);
|
|
50
50
|
setDisplayValue(formatted);
|
|
51
|
-
const unformatted =
|
|
51
|
+
const unformatted = chunkL7YQBSEL_cjs.unformatPhoneNumber(formatted);
|
|
52
52
|
onChange?.(unformatted);
|
|
53
53
|
onFormattedChange?.(formatted);
|
|
54
54
|
if (localError) {
|
|
@@ -58,8 +58,8 @@ var PhoneInput = React__namespace.forwardRef(
|
|
|
58
58
|
const handleBlur = (e) => {
|
|
59
59
|
onBlur?.(e);
|
|
60
60
|
if (validateOnBlur) {
|
|
61
|
-
const unformatted =
|
|
62
|
-
if (unformatted.length > 0 && !
|
|
61
|
+
const unformatted = chunkL7YQBSEL_cjs.unformatPhoneNumber(displayValue);
|
|
62
|
+
if (unformatted.length > 0 && !chunkL7YQBSEL_cjs.isValidPhoneNumber(displayValue)) {
|
|
63
63
|
setLocalError("Please enter a valid 10-digit phone number");
|
|
64
64
|
} else {
|
|
65
65
|
setLocalError(void 0);
|
|
@@ -266,5 +266,5 @@ PhoneInputGroup.displayName = "PhoneInputGroup";
|
|
|
266
266
|
|
|
267
267
|
exports.PhoneInput = PhoneInput;
|
|
268
268
|
exports.PhoneInputGroup = PhoneInputGroup;
|
|
269
|
-
//# sourceMappingURL=chunk-
|
|
270
|
-
//# sourceMappingURL=chunk-
|
|
269
|
+
//# sourceMappingURL=chunk-N2YMRZ7P.cjs.map
|
|
270
|
+
//# sourceMappingURL=chunk-N2YMRZ7P.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/PhoneInput/PhoneInput.tsx"],"names":["React","formatPhoneNumber","unformatPhoneNumber","isValidPhoneNumber","jsx","Input","cn","jsxs"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,IAAM,UAAA,GAAmBA,gBAAA,CAAA,UAAA;AAAA,EACvB,CACE;AAAA,IACE,KAAA,GAAQ,EAAA;AAAA,IACR,QAAA;AAAA,IACA,iBAAA;AAAA,IACA,cAAA;AAAA,IACA,SAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,KAAA;AAAA,IACA,GAAG;AAAA,KAEL,GAAA,KACG;AACH,IAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAUA,gBAAA,CAAA,QAAA;AAAA,MAAS,MACrDC,oCAAkB,KAAK;AAAA,KACzB;AACA,IAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAUD,gBAAA,CAAA,QAAA,EAA6B;AAGvE,IAAMA,2BAAU,MAAM;AACpB,MAAA,eAAA,CAAgBC,mCAAA,CAAkB,KAAK,CAAC,CAAA;AAAA,IAC1C,CAAA,EAAG,CAAC,KAAK,CAAC,CAAA;AAEV,IAAA,MAAM,YAAA,GAAe,CAAC,CAAA,KAA2C;AAC/D,MAAA,MAAM,SAAA,GAAYA,mCAAA,CAAkB,CAAA,CAAE,MAAA,CAAO,KAAK,CAAA;AAClD,MAAA,eAAA,CAAgB,SAAS,CAAA;AAEzB,MAAA,MAAM,WAAA,GAAcC,sCAAoB,SAAS,CAAA;AACjD,MAAA,QAAA,GAAW,WAAW,CAAA;AACtB,MAAA,iBAAA,GAAoB,SAAS,CAAA;AAG7B,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,aAAA,CAAc,MAAS,CAAA;AAAA,MACzB;AAAA,IACF,CAAA;AAEA,IAAA,MAAM,UAAA,GAAa,CAAC,CAAA,KAA0C;AAC5D,MAAA,MAAA,GAAS,CAAC,CAAA;AAEV,MAAA,IAAI,cAAA,EAAgB;AAClB,QAAA,MAAM,WAAA,GAAcA,sCAAoB,YAAY,CAAA;AACpD,QAAA,IAAI,YAAY,MAAA,GAAS,CAAA,IAAK,CAACC,oCAAA,CAAmB,YAAY,CAAA,EAAG;AAC/D,UAAA,aAAA,CAAc,4CAA4C,CAAA;AAAA,QAC5D,CAAA,MAAO;AACL,UAAA,aAAA,CAAc,MAAS,CAAA;AAAA,QACzB;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,uBACEC,cAAA;AAAA,MAACC,uBAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,IAAA,EAAK,KAAA;AAAA,QACL,SAAA,EAAU,SAAA;AAAA,QACV,YAAA,EAAa,cAAA;AAAA,QACb,WAAA,EAAY,gBAAA;AAAA,QACZ,KAAA,EAAO,YAAA;AAAA,QACP,QAAA,EAAU,YAAA;AAAA,QACV,MAAA,EAAQ,UAAA;AAAA,QACR,QAAA,EAAU,QAAA,IAAY,CAAC,CAAC,UAAA;AAAA,QACxB,OAAO,KAAA,IAAS,UAAA;AAAA,QAChB,SAAA,EAAWC,qBAAG,SAAS,CAAA;AAAA,QACtB,GAAG;AAAA;AAAA,KACN;AAAA,EAEJ;AACF;AAEA,UAAA,CAAW,WAAA,GAAc,YAAA;AAazB,IAAM,WAAA,GAAqD;AAAA,EACzD,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,UAAA,EAAY,KAAA,EAAO,UAAA,EAAW;AAAA,EACvC,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,MAAA,EAAO;AAAA,EAC/B,EAAE,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,KAAA;AACzB,CAAA;AA6CA,SAAS,eAAA,CAAgB;AAAA,EACvB,KAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA,GAAa,CAAA;AAAA,EACb,UAAA,GAAa,CAAA;AAAA,EACb,QAAA,GAAW,KAAA;AAAA,EACX,QAAA,GAAW,KAAA;AAAA,EACX,cAAA,GAAiB,KAAA;AAAA,EACjB,KAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF,CAAA,EAAyB;AAEvB,EAAA,MAAM,MAAA,GAAeN,yBAAQ,MAAM;AACjC,IAAA,IAAI,KAAA,CAAM,MAAA,IAAU,UAAA,EAAY,OAAO,KAAA;AACvC,IAAA,MAAM,UAAwB,KAAA,CAAM,UAAA,GAAa,KAAA,CAAM,MAAM,EAC1D,IAAA,CAAK,IAAI,CAAA,CACT,GAAA,CAAI,OAAO,EAAE,MAAA,EAAQ,EAAA,EAAI,IAAA,EAAM,QAAoB,CAAE,CAAA;AACxD,IAAA,OAAO,CAAC,GAAG,KAAA,EAAO,GAAG,OAAO,CAAA;AAAA,EAC9B,CAAA,EAAG,CAAC,KAAA,EAAO,UAAU,CAAC,CAAA;AAEtB,EAAA,MAAM,iBAAA,GAAoB,CAAC,KAAA,EAAe,MAAA,KAAmB;AAC3D,IAAA,MAAM,OAAA,GAAU,CAAC,GAAG,MAAM,CAAA;AAC1B,IAAA,OAAA,CAAQ,KAAK,CAAA,GAAI,EAAE,GAAG,OAAA,CAAQ,KAAK,GAAG,MAAA,EAAO;AAC7C,IAAA,QAAA,CAAS,OAAO,CAAA;AAAA,EAClB,CAAA;AAEA,EAAA,MAAM,gBAAA,GAAmB,CAAC,KAAA,EAAe,IAAA,KAAoB;AAC3D,IAAA,MAAM,OAAA,GAAU,CAAC,GAAG,MAAM,CAAA;AAC1B,IAAA,OAAA,CAAQ,KAAK,CAAA,GAAI,EAAE,GAAG,OAAA,CAAQ,KAAK,GAAG,IAAA,EAAK;AAC3C,IAAA,QAAA,CAAS,OAAO,CAAA;AAAA,EAClB,CAAA;AAEA,EAAA,MAAM,YAAY,MAAM;AACtB,IAAA,IAAI,MAAA,CAAO,SAAS,UAAA,EAAY;AAC9B,MAAA,QAAA,CAAS,CAAC,GAAG,MAAA,EAAQ,EAAE,QAAQ,EAAA,EAAI,IAAA,EAAM,MAAA,EAAQ,CAAC,CAAA;AAAA,IACpD;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,CAAC,KAAA,KAAkB;AACtC,IAAA,IAAI,MAAA,CAAO,SAAS,UAAA,EAAY;AAC9B,MAAA,QAAA,CAAS,OAAO,MAAA,CAAO,CAAC,GAAG,CAAA,KAAM,CAAA,KAAM,KAAK,CAAC,CAAA;AAAA,IAC/C;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,CAAC,IAAA,KAA4B;AAChD,IAAA,IAAI,UAAA,GAAa,IAAI,CAAA,EAAG,OAAO,WAAW,IAAI,CAAA;AAC9C,IAAA,OAAO,WAAA,CAAY,KAAK,CAAC,CAAA,KAAM,EAAE,KAAA,KAAU,IAAI,GAAG,KAAA,IAAS,IAAA;AAAA,EAC7D,CAAA;AAEA,EAAA,MAAM,MAAA,GAAS,OAAO,MAAA,GAAS,UAAA;AAC/B,EAAA,MAAM,SAAA,GAAY,OAAO,MAAA,GAAS,UAAA;AAElC,EAAA,uBACEI,cAAA,CAAC,KAAA,EAAA,EAAI,WAAA,EAAU,aAAA,EAAc,SAAA,EAAWE,oBAAA,CAAG,WAAA,EAAa,SAAS,CAAA,EAC9D,QAAA,EAAA,MAAA,CAAO,GAAA,CAAI,CAAC,OAAO,KAAA,qBAClBC,eAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MAEC,WAAA,EAAU,WAAA;AAAA,MACV,SAAA,EAAU,wBAAA;AAAA,MAGV,QAAA,EAAA;AAAA,wBAAAH,cAAA,CAAC,KAAA,EAAA,EAAI,WAAU,QAAA,EACb,QAAA,kBAAAA,cAAA;AAAA,UAAC,UAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAO,KAAA,KAAU,CAAA,GAAI,KAAA,GAAQ,MAAA;AAAA,YAC7B,OAAO,KAAA,CAAM,MAAA;AAAA,YACb,QAAA,EAAU,CAAC,GAAA,KAAQ,iBAAA,CAAkB,OAAO,GAAG,CAAA;AAAA,YAC/C,QAAA;AAAA,YACA,cAAA;AAAA,YACA,QAAA,EAAU,YAAY,KAAA,KAAU;AAAA;AAAA,SAClC,EACF,CAAA;AAAA,wBAGAG,eAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uBAAA,EACb,QAAA,EAAA;AAAA,0BAAAH,cAAA,CAAC,WAAM,OAAA,EAAS,CAAA,WAAA,EAAc,KAAK,CAAA,CAAA,EAAI,SAAA,EAAU,WAAU,QAAA,EAAA,YAAA,EAE3D,CAAA;AAAA,0BACAA,cAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,EAAA,EAAI,cAAc,KAAK,CAAA,CAAA;AAAA,cACvB,WAAA,EAAU,mBAAA;AAAA,cACV,OAAO,KAAA,CAAM,IAAA;AAAA,cACb,UAAU,CAAC,CAAA,KACT,iBAAiB,KAAA,EAAO,CAAA,CAAE,OAAO,KAAkB,CAAA;AAAA,cAErD,QAAA;AAAA,cACA,SAAA,EAAWE,oBAAA;AAAA,gBACT,iDAAA;AAAA,gBACA,wCAAA;AAAA,gBACA,gFAAA;AAAA,gBACA,wEAAA;AAAA,gBACA,0DAAA;AAAA,gBACA,0DAAA;AAAA,gBACA,KAAA,KAAU,CAAA,IAAK,KAAA,GAAQ,WAAA,GAAc;AAAA,eACvC;AAAA,cAEC,QAAA,EAAA,WAAA,CAAY,GAAA,CAAI,CAAC,IAAA,oCACf,QAAA,EAAA,EAAwB,KAAA,EAAO,IAAA,CAAK,KAAA,EAClC,uBAAa,IAAA,CAAK,KAAK,CAAA,EAAA,EADb,IAAA,CAAK,KAElB,CACD;AAAA;AAAA;AACH,SAAA,EACF,CAAA;AAAA,wBAGAF,cAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,WAAA,EAAU,cAAA;AAAA,YACV,SAAA,EAAWE,oBAAA;AAAA,cACT,4BAAA;AAAA,cACA,KAAA,KAAU,CAAA,IAAK,KAAA,GAAQ,WAAA,GAAc;AAAA,aACvC;AAAA,YAEC,oBAAU,CAAA,mBACTF,cAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,WAAA,EAAU,eAAA;AAAA,gBACV,OAAA,EAAS,SAAA;AAAA,gBACT,QAAA,EAAU,YAAY,CAAC,MAAA;AAAA,gBACvB,SAAA,EAAWE,oBAAA;AAAA,kBACT,oCAAA;AAAA,kBACA,kCAAA;AAAA,kBACA,kFAAA;AAAA,kBACA,gDAAA;AAAA,kBACA;AAAA,iBACF;AAAA,gBACA,YAAA,EAAW,kBAAA;AAAA,gBAEX,QAAA,kBAAAF,cAAA;AAAA,kBAAC,KAAA;AAAA,kBAAA;AAAA,oBACC,WAAA,EAAU,gBAAA;AAAA,oBACV,SAAA,EAAU,SAAA;AAAA,oBACV,IAAA,EAAK,MAAA;AAAA,oBACL,OAAA,EAAQ,WAAA;AAAA,oBACR,MAAA,EAAO,cAAA;AAAA,oBAEP,QAAA,kBAAAA,cAAA;AAAA,sBAAC,MAAA;AAAA,sBAAA;AAAA,wBACC,aAAA,EAAc,OAAA;AAAA,wBACd,cAAA,EAAe,OAAA;AAAA,wBACf,WAAA,EAAa,CAAA;AAAA,wBACb,CAAA,EAAE;AAAA;AAAA;AACJ;AAAA;AACF;AAAA,aACF,mBAEAA,cAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,WAAA,EAAU,kBAAA;AAAA,gBACV,OAAA,EAAS,MAAM,YAAA,CAAa,KAAK,CAAA;AAAA,gBACjC,QAAA,EAAU,YAAY,CAAC,SAAA;AAAA,gBACvB,SAAA,EAAWE,oBAAA;AAAA,kBACT,oCAAA;AAAA,kBACA,8BAAA;AAAA,kBACA,kFAAA;AAAA,kBACA,4CAAA;AAAA,kBACA;AAAA,iBACF;AAAA,gBACA,YAAA,EAAW,qBAAA;AAAA,gBAEX,QAAA,kBAAAF,cAAA;AAAA,kBAAC,KAAA;AAAA,kBAAA;AAAA,oBACC,WAAA,EAAU,mBAAA;AAAA,oBACV,SAAA,EAAU,SAAA;AAAA,oBACV,IAAA,EAAK,MAAA;AAAA,oBACL,OAAA,EAAQ,WAAA;AAAA,oBACR,MAAA,EAAO,cAAA;AAAA,oBAEP,QAAA,kBAAAA,cAAA;AAAA,sBAAC,MAAA;AAAA,sBAAA;AAAA,wBACC,aAAA,EAAc,OAAA;AAAA,wBACd,cAAA,EAAe,OAAA;AAAA,wBACf,WAAA,EAAa,CAAA;AAAA,wBACb,CAAA,EAAE;AAAA;AAAA;AACJ;AAAA;AACF;AAAA;AACF;AAAA;AAEJ;AAAA,KAAA;AAAA,IApHK;AAAA,GAsHR,CAAA,EACH,CAAA;AAEJ;AAEA,eAAA,CAAgB,WAAA,GAAc,iBAAA","file":"chunk-7CLHYU4Q.cjs","sourcesContent":["import * as React from 'react';\nimport { cn } from '../../utils/cn';\nimport {\n formatPhoneNumber,\n unformatPhoneNumber,\n isValidPhoneNumber,\n} from '../../utils/phone';\nimport { Input, type InputProps } from '../Input';\n\nexport interface PhoneInputProps extends Omit<\n InputProps,\n 'type' | 'onChange' | 'value'\n> {\n /** The phone number value (can be formatted or unformatted) */\n value?: string;\n /** Callback fired when the value changes, receives the unformatted value */\n onChange?: (value: string) => void;\n /** Callback fired when the formatted value changes */\n onFormattedChange?: (formattedValue: string) => void;\n /** Whether to validate and show error state for incomplete phone numbers */\n validateOnBlur?: boolean;\n}\n\n/**\n * A phone number input that automatically formats to US format: (XXX) XXX-XXXX\n *\n * @example\n * ```tsx\n * const [phone, setPhone] = useState('');\n * <PhoneInput\n * label=\"Phone Number\"\n * value={phone}\n * onChange={setPhone}\n * validateOnBlur\n * />\n * ```\n */\nconst PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(\n (\n {\n value = '',\n onChange,\n onFormattedChange,\n validateOnBlur,\n className,\n onBlur,\n hasError,\n error,\n ...props\n },\n ref\n ) => {\n const [displayValue, setDisplayValue] = React.useState(() =>\n formatPhoneNumber(value)\n );\n const [localError, setLocalError] = React.useState<string | undefined>();\n\n // Sync external value changes\n React.useEffect(() => {\n setDisplayValue(formatPhoneNumber(value));\n }, [value]);\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const formatted = formatPhoneNumber(e.target.value);\n setDisplayValue(formatted);\n\n const unformatted = unformatPhoneNumber(formatted);\n onChange?.(unformatted);\n onFormattedChange?.(formatted);\n\n // Clear error when user starts typing again\n if (localError) {\n setLocalError(undefined);\n }\n };\n\n const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {\n onBlur?.(e);\n\n if (validateOnBlur) {\n const unformatted = unformatPhoneNumber(displayValue);\n if (unformatted.length > 0 && !isValidPhoneNumber(displayValue)) {\n setLocalError('Please enter a valid 10-digit phone number');\n } else {\n setLocalError(undefined);\n }\n }\n };\n\n return (\n <Input\n ref={ref}\n type=\"tel\"\n inputMode=\"numeric\"\n autoComplete=\"tel-national\"\n placeholder=\"(555) 555-5555\"\n value={displayValue}\n onChange={handleChange}\n onBlur={handleBlur}\n hasError={hasError || !!localError}\n error={error || localError}\n className={cn(className)}\n {...props}\n />\n );\n }\n);\n\nPhoneInput.displayName = 'PhoneInput';\n\n// =============================================================================\n// Phone Types\n// =============================================================================\n\nexport type PhoneType = 'cell' | 'landline' | 'home' | 'work' | 'fax';\n\nexport interface PhoneEntry {\n number: string;\n type: PhoneType;\n}\n\nconst PHONE_TYPES: { value: PhoneType; label: string }[] = [\n { value: 'cell', label: 'Cell' },\n { value: 'landline', label: 'Landline' },\n { value: 'home', label: 'Home' },\n { value: 'work', label: 'Work' },\n { value: 'fax', label: 'Fax' },\n];\n\n// =============================================================================\n// PhoneInputGroup\n// =============================================================================\n\nexport interface PhoneInputGroupProps {\n /** Array of phone entries */\n value: PhoneEntry[];\n /** Callback when phone entries change */\n onChange: (phones: PhoneEntry[]) => void;\n /** Minimum number of phone entries (default: 1) */\n minEntries?: number;\n /** Maximum number of phone entries (default: 5) */\n maxEntries?: number;\n /** Whether the first entry is required */\n required?: boolean;\n /** Whether all inputs are disabled */\n disabled?: boolean;\n /** Validate on blur */\n validateOnBlur?: boolean;\n /** Label for the phone input */\n label?: string;\n /** Labels for type options (for i18n) */\n typeLabels?: Record<PhoneType, string>;\n /** Custom className */\n className?: string;\n}\n\n/**\n * A group of phone inputs with type selection and add/remove functionality.\n *\n * @example\n * ```tsx\n * const [phones, setPhones] = useState<PhoneEntry[]>([\n * { number: '', type: 'cell' }\n * ]);\n *\n * <PhoneInputGroup\n * value={phones}\n * onChange={setPhones}\n * required\n * />\n * ```\n */\nfunction PhoneInputGroup({\n value,\n onChange,\n minEntries = 1,\n maxEntries = 5,\n required = false,\n disabled = false,\n validateOnBlur = false,\n label,\n typeLabels,\n className,\n}: PhoneInputGroupProps) {\n // Ensure we always have at least minEntries\n const phones = React.useMemo(() => {\n if (value.length >= minEntries) return value;\n const padding: PhoneEntry[] = Array(minEntries - value.length)\n .fill(null)\n .map(() => ({ number: '', type: 'cell' as PhoneType }));\n return [...value, ...padding];\n }, [value, minEntries]);\n\n const handlePhoneChange = (index: number, number: string) => {\n const updated = [...phones];\n updated[index] = { ...updated[index], number };\n onChange(updated);\n };\n\n const handleTypeChange = (index: number, type: PhoneType) => {\n const updated = [...phones];\n updated[index] = { ...updated[index], type };\n onChange(updated);\n };\n\n const handleAdd = () => {\n if (phones.length < maxEntries) {\n onChange([...phones, { number: '', type: 'cell' }]);\n }\n };\n\n const handleRemove = (index: number) => {\n if (phones.length > minEntries) {\n onChange(phones.filter((_, i) => i !== index));\n }\n };\n\n const getTypeLabel = (type: PhoneType): string => {\n if (typeLabels?.[type]) return typeLabels[type];\n return PHONE_TYPES.find((t) => t.value === type)?.label || type;\n };\n\n const canAdd = phones.length < maxEntries;\n const canRemove = phones.length > minEntries;\n\n return (\n <div data-slot=\"phone-group\" className={cn('space-y-3', className)}>\n {phones.map((phone, index) => (\n <div\n key={index}\n data-slot=\"phone-row\"\n className=\"flex items-start gap-2\"\n >\n {/* Phone number input */}\n <div className=\"flex-1\">\n <PhoneInput\n label={index === 0 ? label : undefined}\n value={phone.number}\n onChange={(num) => handlePhoneChange(index, num)}\n disabled={disabled}\n validateOnBlur={validateOnBlur}\n required={required && index === 0}\n />\n </div>\n\n {/* Type selector */}\n <div className=\"w-24 shrink-0 sm:w-32\">\n <label htmlFor={`phone-type-${index}`} className=\"sr-only\">\n Phone type\n </label>\n <select\n id={`phone-type-${index}`}\n data-slot=\"phone-type-select\"\n value={phone.type}\n onChange={(e) =>\n handleTypeChange(index, e.target.value as PhoneType)\n }\n disabled={disabled}\n className={cn(\n 'h-10 w-full rounded-md border px-3 py-2 text-sm',\n 'border-gray-300 bg-white text-gray-900',\n 'focus:border-brand-500 focus:ring-brand-500/20 focus:ring-2 focus:outline-none',\n 'disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500',\n 'dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100',\n 'dark:focus:border-brand-400 dark:focus:ring-brand-400/20',\n index === 0 && label ? 'mt-[26px]' : ''\n )}\n >\n {PHONE_TYPES.map((type) => (\n <option key={type.value} value={type.value}>\n {getTypeLabel(type.value)}\n </option>\n ))}\n </select>\n </div>\n\n {/* Add/Remove buttons */}\n <div\n data-slot=\"phone-action\"\n className={cn(\n 'flex shrink-0 items-center',\n index === 0 && label ? 'mt-[26px]' : ''\n )}\n >\n {index === 0 ? (\n <button\n type=\"button\"\n data-slot=\"phone-add-btn\"\n onClick={handleAdd}\n disabled={disabled || !canAdd}\n className={cn(\n 'rounded-full p-2 transition-colors',\n 'text-brand-600 hover:bg-brand-50',\n 'disabled:cursor-not-allowed disabled:text-gray-300 disabled:hover:bg-transparent',\n 'dark:text-brand-400 dark:hover:bg-brand-900/20',\n 'dark:disabled:text-gray-600'\n )}\n aria-label=\"Add phone number\"\n >\n <svg\n data-slot=\"phone-add-icon\"\n className=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 4v16m8-8H4\"\n />\n </svg>\n </button>\n ) : (\n <button\n type=\"button\"\n data-slot=\"phone-remove-btn\"\n onClick={() => handleRemove(index)}\n disabled={disabled || !canRemove}\n className={cn(\n 'rounded-full p-2 transition-colors',\n 'text-red-600 hover:bg-red-50',\n 'disabled:cursor-not-allowed disabled:text-gray-300 disabled:hover:bg-transparent',\n 'dark:text-red-400 dark:hover:bg-red-900/20',\n 'dark:disabled:text-gray-600'\n )}\n aria-label=\"Remove phone number\"\n >\n <svg\n data-slot=\"phone-remove-icon\"\n className=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20 12H4\"\n />\n </svg>\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nPhoneInputGroup.displayName = 'PhoneInputGroup';\n\nexport { PhoneInput, PhoneInputGroup };\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/PhoneInput/PhoneInput.tsx"],"names":["React","formatPhoneNumber","unformatPhoneNumber","isValidPhoneNumber","jsx","Input","cn","jsxs"],"mappings":"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","file":"chunk-N2YMRZ7P.cjs","sourcesContent":["import * as React from 'react';\nimport { cn } from '../../utils/cn';\nimport {\n formatPhoneNumber,\n unformatPhoneNumber,\n isValidPhoneNumber,\n} from '../../utils/phone';\nimport { Input, type InputProps } from '../Input';\n\nexport interface PhoneInputProps extends Omit<\n InputProps,\n 'type' | 'onChange' | 'value'\n> {\n /** The phone number value (can be formatted or unformatted) */\n value?: string;\n /** Callback fired when the value changes, receives the unformatted value */\n onChange?: (value: string) => void;\n /** Callback fired when the formatted value changes */\n onFormattedChange?: (formattedValue: string) => void;\n /** Whether to validate and show error state for incomplete phone numbers */\n validateOnBlur?: boolean;\n}\n\n/**\n * A phone number input that automatically formats to US format: (XXX) XXX-XXXX\n *\n * @example\n * ```tsx\n * const [phone, setPhone] = useState('');\n * <PhoneInput\n * label=\"Phone Number\"\n * value={phone}\n * onChange={setPhone}\n * validateOnBlur\n * />\n * ```\n */\nconst PhoneInput = React.forwardRef<HTMLInputElement, PhoneInputProps>(\n (\n {\n value = '',\n onChange,\n onFormattedChange,\n validateOnBlur,\n className,\n onBlur,\n hasError,\n error,\n ...props\n },\n ref\n ) => {\n const [displayValue, setDisplayValue] = React.useState(() =>\n formatPhoneNumber(value)\n );\n const [localError, setLocalError] = React.useState<string | undefined>();\n\n // Sync external value changes\n React.useEffect(() => {\n setDisplayValue(formatPhoneNumber(value));\n }, [value]);\n\n const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const formatted = formatPhoneNumber(e.target.value);\n setDisplayValue(formatted);\n\n const unformatted = unformatPhoneNumber(formatted);\n onChange?.(unformatted);\n onFormattedChange?.(formatted);\n\n // Clear error when user starts typing again\n if (localError) {\n setLocalError(undefined);\n }\n };\n\n const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {\n onBlur?.(e);\n\n if (validateOnBlur) {\n const unformatted = unformatPhoneNumber(displayValue);\n if (unformatted.length > 0 && !isValidPhoneNumber(displayValue)) {\n setLocalError('Please enter a valid 10-digit phone number');\n } else {\n setLocalError(undefined);\n }\n }\n };\n\n return (\n <Input\n ref={ref}\n type=\"tel\"\n inputMode=\"numeric\"\n autoComplete=\"tel-national\"\n placeholder=\"(555) 555-5555\"\n value={displayValue}\n onChange={handleChange}\n onBlur={handleBlur}\n hasError={hasError || !!localError}\n error={error || localError}\n className={cn(className)}\n {...props}\n />\n );\n }\n);\n\nPhoneInput.displayName = 'PhoneInput';\n\n// =============================================================================\n// Phone Types\n// =============================================================================\n\nexport type PhoneType = 'cell' | 'landline' | 'home' | 'work' | 'fax';\n\nexport interface PhoneEntry {\n number: string;\n type: PhoneType;\n}\n\nconst PHONE_TYPES: { value: PhoneType; label: string }[] = [\n { value: 'cell', label: 'Cell' },\n { value: 'landline', label: 'Landline' },\n { value: 'home', label: 'Home' },\n { value: 'work', label: 'Work' },\n { value: 'fax', label: 'Fax' },\n];\n\n// =============================================================================\n// PhoneInputGroup\n// =============================================================================\n\nexport interface PhoneInputGroupProps {\n /** Array of phone entries */\n value: PhoneEntry[];\n /** Callback when phone entries change */\n onChange: (phones: PhoneEntry[]) => void;\n /** Minimum number of phone entries (default: 1) */\n minEntries?: number;\n /** Maximum number of phone entries (default: 5) */\n maxEntries?: number;\n /** Whether the first entry is required */\n required?: boolean;\n /** Whether all inputs are disabled */\n disabled?: boolean;\n /** Validate on blur */\n validateOnBlur?: boolean;\n /** Label for the phone input */\n label?: string;\n /** Labels for type options (for i18n) */\n typeLabels?: Record<PhoneType, string>;\n /** Custom className */\n className?: string;\n}\n\n/**\n * A group of phone inputs with type selection and add/remove functionality.\n *\n * @example\n * ```tsx\n * const [phones, setPhones] = useState<PhoneEntry[]>([\n * { number: '', type: 'cell' }\n * ]);\n *\n * <PhoneInputGroup\n * value={phones}\n * onChange={setPhones}\n * required\n * />\n * ```\n */\nfunction PhoneInputGroup({\n value,\n onChange,\n minEntries = 1,\n maxEntries = 5,\n required = false,\n disabled = false,\n validateOnBlur = false,\n label,\n typeLabels,\n className,\n}: PhoneInputGroupProps) {\n // Ensure we always have at least minEntries\n const phones = React.useMemo(() => {\n if (value.length >= minEntries) return value;\n const padding: PhoneEntry[] = Array(minEntries - value.length)\n .fill(null)\n .map(() => ({ number: '', type: 'cell' as PhoneType }));\n return [...value, ...padding];\n }, [value, minEntries]);\n\n const handlePhoneChange = (index: number, number: string) => {\n const updated = [...phones];\n updated[index] = { ...updated[index], number };\n onChange(updated);\n };\n\n const handleTypeChange = (index: number, type: PhoneType) => {\n const updated = [...phones];\n updated[index] = { ...updated[index], type };\n onChange(updated);\n };\n\n const handleAdd = () => {\n if (phones.length < maxEntries) {\n onChange([...phones, { number: '', type: 'cell' }]);\n }\n };\n\n const handleRemove = (index: number) => {\n if (phones.length > minEntries) {\n onChange(phones.filter((_, i) => i !== index));\n }\n };\n\n const getTypeLabel = (type: PhoneType): string => {\n if (typeLabels?.[type]) return typeLabels[type];\n return PHONE_TYPES.find((t) => t.value === type)?.label || type;\n };\n\n const canAdd = phones.length < maxEntries;\n const canRemove = phones.length > minEntries;\n\n return (\n <div data-slot=\"phone-group\" className={cn('space-y-3', className)}>\n {phones.map((phone, index) => (\n <div\n key={index}\n data-slot=\"phone-row\"\n className=\"flex items-start gap-2\"\n >\n {/* Phone number input */}\n <div className=\"flex-1\">\n <PhoneInput\n label={index === 0 ? label : undefined}\n value={phone.number}\n onChange={(num) => handlePhoneChange(index, num)}\n disabled={disabled}\n validateOnBlur={validateOnBlur}\n required={required && index === 0}\n />\n </div>\n\n {/* Type selector */}\n <div className=\"w-24 shrink-0 sm:w-32\">\n <label htmlFor={`phone-type-${index}`} className=\"sr-only\">\n Phone type\n </label>\n <select\n id={`phone-type-${index}`}\n data-slot=\"phone-type-select\"\n value={phone.type}\n onChange={(e) =>\n handleTypeChange(index, e.target.value as PhoneType)\n }\n disabled={disabled}\n className={cn(\n 'h-10 w-full rounded-md border px-3 py-2 text-sm',\n 'border-gray-300 bg-white text-gray-900',\n 'focus:border-brand-500 focus:ring-brand-500/20 focus:ring-2 focus:outline-none',\n 'disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500',\n 'dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100',\n 'dark:focus:border-brand-400 dark:focus:ring-brand-400/20',\n index === 0 && label ? 'mt-[26px]' : ''\n )}\n >\n {PHONE_TYPES.map((type) => (\n <option key={type.value} value={type.value}>\n {getTypeLabel(type.value)}\n </option>\n ))}\n </select>\n </div>\n\n {/* Add/Remove buttons */}\n <div\n data-slot=\"phone-action\"\n className={cn(\n 'flex shrink-0 items-center',\n index === 0 && label ? 'mt-[26px]' : ''\n )}\n >\n {index === 0 ? (\n <button\n type=\"button\"\n data-slot=\"phone-add-btn\"\n onClick={handleAdd}\n disabled={disabled || !canAdd}\n className={cn(\n 'rounded-full p-2 transition-colors',\n 'text-brand-600 hover:bg-brand-50',\n 'disabled:cursor-not-allowed disabled:text-gray-300 disabled:hover:bg-transparent',\n 'dark:text-brand-400 dark:hover:bg-brand-900/20',\n 'dark:disabled:text-gray-600'\n )}\n aria-label=\"Add phone number\"\n >\n <svg\n data-slot=\"phone-add-icon\"\n className=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 4v16m8-8H4\"\n />\n </svg>\n </button>\n ) : (\n <button\n type=\"button\"\n data-slot=\"phone-remove-btn\"\n onClick={() => handleRemove(index)}\n disabled={disabled || !canRemove}\n className={cn(\n 'rounded-full p-2 transition-colors',\n 'text-red-600 hover:bg-red-50',\n 'disabled:cursor-not-allowed disabled:text-gray-300 disabled:hover:bg-transparent',\n 'dark:text-red-400 dark:hover:bg-red-900/20',\n 'dark:disabled:text-gray-600'\n )}\n aria-label=\"Remove phone number\"\n >\n <svg\n data-slot=\"phone-remove-icon\"\n className=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M20 12H4\"\n />\n </svg>\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nPhoneInputGroup.displayName = 'PhoneInputGroup';\n\nexport { PhoneInput, PhoneInputGroup };\n"]}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
// src/utils/phone.ts
|
|
2
2
|
function formatPhoneNumber(value) {
|
|
3
|
-
const
|
|
4
|
-
if (
|
|
3
|
+
const allDigits = value.replace(/\D/g, "");
|
|
4
|
+
if (allDigits.length === 0) return "";
|
|
5
|
+
if (allDigits.length === 11 && allDigits.startsWith("1")) {
|
|
6
|
+
const rest = allDigits.slice(1);
|
|
7
|
+
return `+1 (${rest.slice(0, 3)}) ${rest.slice(3, 6)}-${rest.slice(6)}`;
|
|
8
|
+
}
|
|
9
|
+
if (allDigits.length > 11) {
|
|
10
|
+
const cc = allDigits.slice(0, allDigits.length - 10);
|
|
11
|
+
const nat = allDigits.slice(-10);
|
|
12
|
+
return `+${cc} (${nat.slice(0, 3)}) ${nat.slice(3, 6)}-${nat.slice(6)}`;
|
|
13
|
+
}
|
|
14
|
+
const digits = allDigits.slice(0, 10);
|
|
5
15
|
if (digits.length <= 3) return `(${digits}`;
|
|
6
16
|
if (digits.length <= 6) return `(${digits.slice(0, 3)}) ${digits.slice(3)}`;
|
|
7
17
|
return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
|
|
@@ -18,5 +28,5 @@ function isPhoneNumberEmpty(value) {
|
|
|
18
28
|
}
|
|
19
29
|
|
|
20
30
|
export { formatPhoneNumber, isPhoneNumberEmpty, isValidPhoneNumber, unformatPhoneNumber };
|
|
21
|
-
//# sourceMappingURL=chunk-
|
|
22
|
-
//# sourceMappingURL=chunk-
|
|
31
|
+
//# sourceMappingURL=chunk-ZVPJ2MH6.js.map
|
|
32
|
+
//# sourceMappingURL=chunk-ZVPJ2MH6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/phone.ts"],"names":[],"mappings":";AAaO,SAAS,kBAAkB,KAAA,EAAuB;AACvD,EAAA,MAAM,SAAA,GAAY,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AACzC,EAAA,IAAI,SAAA,CAAU,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AAInC,EAAA,IAAI,UAAU,MAAA,KAAW,EAAA,IAAM,SAAA,CAAU,UAAA,CAAW,GAAG,CAAA,EAAG;AACxD,IAAA,MAAM,IAAA,GAAO,SAAA,CAAU,KAAA,CAAM,CAAC,CAAA;AAC9B,IAAA,OAAO,OAAO,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,EAAA,EAAK,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA,EAAI,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AAAA,EACtE;AAKA,EAAA,IAAI,SAAA,CAAU,SAAS,EAAA,EAAI;AACzB,IAAA,MAAM,KAAK,SAAA,CAAU,KAAA,CAAM,CAAA,EAAG,SAAA,CAAU,SAAS,EAAE,CAAA;AACnD,IAAA,MAAM,GAAA,GAAM,SAAA,CAAU,KAAA,CAAM,GAAG,CAAA;AAC/B,IAAA,OAAO,IAAI,EAAE,CAAA,EAAA,EAAK,IAAI,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,EAAA,EAAK,GAAA,CAAI,KAAA,CAAM,GAAG,CAAC,CAAC,IAAI,GAAA,CAAI,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AAAA,EACvE;AAGA,EAAA,MAAM,MAAA,GAAS,SAAA,CAAU,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AACpC,EAAA,IAAI,MAAA,CAAO,MAAA,IAAU,CAAA,EAAG,OAAO,IAAI,MAAM,CAAA,CAAA;AACzC,EAAA,IAAI,MAAA,CAAO,MAAA,IAAU,CAAA,EAAG,OAAO,IAAI,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,EAAA,EAAK,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACzE,EAAA,OAAO,IAAI,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,EAAA,EAAK,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA,EAAI,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACzE;AAKO,SAAS,oBAAoB,KAAA,EAAuB;AACzD,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAChC;AAKO,SAAS,mBAAmB,KAAA,EAAwB;AACzD,EAAA,MAAM,MAAA,GAAS,oBAAoB,KAAK,CAAA;AACxC,EAAA,OAAO,OAAO,MAAA,KAAW,EAAA;AAC3B;AAKO,SAAS,mBAAmB,KAAA,EAAwB;AACzD,EAAA,OAAO,mBAAA,CAAoB,KAAK,CAAA,CAAE,MAAA,KAAW,CAAA;AAC/C","file":"chunk-ZVPJ2MH6.js","sourcesContent":["/**\n * Phone number formatting utilities\n */\n\n/**\n * Formats a phone number string to US format.\n *\n * Behavior:\n * - 10 digits → `(XXX) XXX-XXXX`\n * - 11 digits w/ `1` → `+1 (XXX) XXX-XXXX` (E.164 US/Canada)\n * - 12+ digits → `+CC (XXX) XXX-XXXX...` (best-effort intl)\n * - partial (typing) → progressive `(`, `(XXX`, `(XXX) X`, etc.\n */\nexport function formatPhoneNumber(value: string): string {\n const allDigits = value.replace(/\\D/g, '');\n if (allDigits.length === 0) return '';\n\n // E.164 with US/Canada country code (1XXXXXXXXXX). Show the country code\n // explicitly so phone numbers aren't shifted by one digit when displayed.\n if (allDigits.length === 11 && allDigits.startsWith('1')) {\n const rest = allDigits.slice(1);\n return `+1 (${rest.slice(0, 3)}) ${rest.slice(3, 6)}-${rest.slice(6)}`;\n }\n\n // Other international numbers: keep the country code prefix and format the\n // trailing 10 digits as a US-style block. This isn't locale-perfect but\n // it's better than silently dropping the country code.\n if (allDigits.length > 11) {\n const cc = allDigits.slice(0, allDigits.length - 10);\n const nat = allDigits.slice(-10);\n return `+${cc} (${nat.slice(0, 3)}) ${nat.slice(3, 6)}-${nat.slice(6)}`;\n }\n\n // Domestic / partial input — progressive formatting as the user types.\n const digits = allDigits.slice(0, 10);\n if (digits.length <= 3) return `(${digits}`;\n if (digits.length <= 6) return `(${digits.slice(0, 3)}) ${digits.slice(3)}`;\n return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;\n}\n\n/**\n * Removes formatting from a phone number, returning only digits\n */\nexport function unformatPhoneNumber(value: string): string {\n return value.replace(/\\D/g, '');\n}\n\n/**\n * Validates if a phone number has exactly 10 digits\n */\nexport function isValidPhoneNumber(value: string): boolean {\n const digits = unformatPhoneNumber(value);\n return digits.length === 10;\n}\n\n/**\n * Checks if a phone number string is empty (no digits)\n */\nexport function isPhoneNumberEmpty(value: string): boolean {\n return unformatPhoneNumber(value).length === 0;\n}\n"]}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkN2YMRZ7P_cjs = require('../../chunk-N2YMRZ7P.cjs');
|
|
4
4
|
require('../../chunk-3XK5GENF.cjs');
|
|
5
|
-
require('../../chunk-
|
|
5
|
+
require('../../chunk-L7YQBSEL.cjs');
|
|
6
6
|
require('../../chunk-OR5DRJCW.cjs');
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
Object.defineProperty(exports, "PhoneInput", {
|
|
11
11
|
enumerable: true,
|
|
12
|
-
get: function () { return
|
|
12
|
+
get: function () { return chunkN2YMRZ7P_cjs.PhoneInput; }
|
|
13
13
|
});
|
|
14
14
|
Object.defineProperty(exports, "PhoneInputGroup", {
|
|
15
15
|
enumerable: true,
|
|
16
|
-
get: function () { return
|
|
16
|
+
get: function () { return chunkN2YMRZ7P_cjs.PhoneInputGroup; }
|
|
17
17
|
});
|
|
18
18
|
//# sourceMappingURL=index.cjs.map
|
|
19
19
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { PhoneInput, PhoneInputGroup } from '../../chunk-
|
|
1
|
+
export { PhoneInput, PhoneInputGroup } from '../../chunk-DLLVXNAQ.js';
|
|
2
2
|
import '../../chunk-XHESCAUE.js';
|
|
3
|
-
import '../../chunk-
|
|
3
|
+
import '../../chunk-ZVPJ2MH6.js';
|
|
4
4
|
import '../../chunk-F3SOEIN2.js';
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -28,7 +28,7 @@ var chunkYR365F2H_cjs = require('./chunk-YR365F2H.cjs');
|
|
|
28
28
|
var chunkQMQE4PDD_cjs = require('./chunk-QMQE4PDD.cjs');
|
|
29
29
|
var chunkAWUADXYI_cjs = require('./chunk-AWUADXYI.cjs');
|
|
30
30
|
var chunkQEAIFTUL_cjs = require('./chunk-QEAIFTUL.cjs');
|
|
31
|
-
var
|
|
31
|
+
var chunkN2YMRZ7P_cjs = require('./chunk-N2YMRZ7P.cjs');
|
|
32
32
|
var chunk3XK5GENF_cjs = require('./chunk-3XK5GENF.cjs');
|
|
33
33
|
var chunkEMMQPDOY_cjs = require('./chunk-EMMQPDOY.cjs');
|
|
34
34
|
var chunkND75VHB7_cjs = require('./chunk-ND75VHB7.cjs');
|
|
@@ -49,7 +49,7 @@ var chunkIKMR2ADM_cjs = require('./chunk-IKMR2ADM.cjs');
|
|
|
49
49
|
var chunkFHY3K6PL_cjs = require('./chunk-FHY3K6PL.cjs');
|
|
50
50
|
require('./chunk-74K3RRU7.cjs');
|
|
51
51
|
var chunkCW75IKA6_cjs = require('./chunk-CW75IKA6.cjs');
|
|
52
|
-
var
|
|
52
|
+
var chunkL7YQBSEL_cjs = require('./chunk-L7YQBSEL.cjs');
|
|
53
53
|
var chunkSCV7C55E_cjs = require('./chunk-SCV7C55E.cjs');
|
|
54
54
|
var chunkG7ZHQA4O_cjs = require('./chunk-G7ZHQA4O.cjs');
|
|
55
55
|
var chunkWGPMTW36_cjs = require('./chunk-WGPMTW36.cjs');
|
|
@@ -39051,11 +39051,11 @@ Object.defineProperty(exports, "paginationButtonVariants", {
|
|
|
39051
39051
|
});
|
|
39052
39052
|
Object.defineProperty(exports, "PhoneInput", {
|
|
39053
39053
|
enumerable: true,
|
|
39054
|
-
get: function () { return
|
|
39054
|
+
get: function () { return chunkN2YMRZ7P_cjs.PhoneInput; }
|
|
39055
39055
|
});
|
|
39056
39056
|
Object.defineProperty(exports, "PhoneInputGroup", {
|
|
39057
39057
|
enumerable: true,
|
|
39058
|
-
get: function () { return
|
|
39058
|
+
get: function () { return chunkN2YMRZ7P_cjs.PhoneInputGroup; }
|
|
39059
39059
|
});
|
|
39060
39060
|
Object.defineProperty(exports, "Input", {
|
|
39061
39061
|
enumerable: true,
|
|
@@ -39347,19 +39347,19 @@ Object.defineProperty(exports, "parseDateValue", {
|
|
|
39347
39347
|
});
|
|
39348
39348
|
Object.defineProperty(exports, "formatPhoneNumber", {
|
|
39349
39349
|
enumerable: true,
|
|
39350
|
-
get: function () { return
|
|
39350
|
+
get: function () { return chunkL7YQBSEL_cjs.formatPhoneNumber; }
|
|
39351
39351
|
});
|
|
39352
39352
|
Object.defineProperty(exports, "isPhoneNumberEmpty", {
|
|
39353
39353
|
enumerable: true,
|
|
39354
|
-
get: function () { return
|
|
39354
|
+
get: function () { return chunkL7YQBSEL_cjs.isPhoneNumberEmpty; }
|
|
39355
39355
|
});
|
|
39356
39356
|
Object.defineProperty(exports, "isValidPhoneNumber", {
|
|
39357
39357
|
enumerable: true,
|
|
39358
|
-
get: function () { return
|
|
39358
|
+
get: function () { return chunkL7YQBSEL_cjs.isValidPhoneNumber; }
|
|
39359
39359
|
});
|
|
39360
39360
|
Object.defineProperty(exports, "unformatPhoneNumber", {
|
|
39361
39361
|
enumerable: true,
|
|
39362
|
-
get: function () { return
|
|
39362
|
+
get: function () { return chunkL7YQBSEL_cjs.unformatPhoneNumber; }
|
|
39363
39363
|
});
|
|
39364
39364
|
Object.defineProperty(exports, "isStorybookDocsMode", {
|
|
39365
39365
|
enumerable: true,
|
package/dist/index.js
CHANGED
|
@@ -39,7 +39,7 @@ export { Dropdown, DropdownContent, DropdownHeader, DropdownItem, DropdownLabel,
|
|
|
39
39
|
import { Modal, ModalHeader, ModalTitle, ModalClose, ModalBody, ModalFooter } from './chunk-WTDCNXZO.js';
|
|
40
40
|
export { Modal, ModalBody, ModalClose, ModalFooter, ModalHeader, ModalTitle, modalContentVariants, modalOverlayVariants } from './chunk-WTDCNXZO.js';
|
|
41
41
|
export { Pagination, SimplePagination, paginationButtonVariants } from './chunk-V2O636JO.js';
|
|
42
|
-
export { PhoneInput, PhoneInputGroup } from './chunk-
|
|
42
|
+
export { PhoneInput, PhoneInputGroup } from './chunk-DLLVXNAQ.js';
|
|
43
43
|
import { Input } from './chunk-XHESCAUE.js';
|
|
44
44
|
export { Input, inputVariants } from './chunk-XHESCAUE.js';
|
|
45
45
|
import { Progress } from './chunk-6EN6ZIW3.js';
|
|
@@ -72,7 +72,7 @@ export { useEscapeKey } from './chunk-T4ME7QCT.js';
|
|
|
72
72
|
import './chunk-V2DF2GUE.js';
|
|
73
73
|
import { dateToDisplayFormat, isValidDate, displayFormatToDateString } from './chunk-RC2YMOMS.js';
|
|
74
74
|
export { calculateAge, dateToDisplayFormat, displayFormatToDateString, formatDateValue, isDateEmpty, isDateInFuture, isDateInPast, isValidDate, isValidDrivingAge, parseDateValue } from './chunk-RC2YMOMS.js';
|
|
75
|
-
export { formatPhoneNumber, isPhoneNumberEmpty, isValidPhoneNumber, unformatPhoneNumber } from './chunk-
|
|
75
|
+
export { formatPhoneNumber, isPhoneNumberEmpty, isValidPhoneNumber, unformatPhoneNumber } from './chunk-ZVPJ2MH6.js';
|
|
76
76
|
import { isStorybookDocsMode } from './chunk-VSQF22GL.js';
|
|
77
77
|
export { isStorybookDocsMode } from './chunk-VSQF22GL.js';
|
|
78
78
|
export { miewebUIPreset, miewebUISafelist } from './chunk-LUVSO5LK.js';
|
package/dist/utils/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require('../chunk-74K3RRU7.cjs');
|
|
4
4
|
var chunkCW75IKA6_cjs = require('../chunk-CW75IKA6.cjs');
|
|
5
|
-
var
|
|
5
|
+
var chunkL7YQBSEL_cjs = require('../chunk-L7YQBSEL.cjs');
|
|
6
6
|
var chunkSCV7C55E_cjs = require('../chunk-SCV7C55E.cjs');
|
|
7
7
|
var chunkOR5DRJCW_cjs = require('../chunk-OR5DRJCW.cjs');
|
|
8
8
|
|
|
@@ -50,19 +50,19 @@ Object.defineProperty(exports, "parseDateValue", {
|
|
|
50
50
|
});
|
|
51
51
|
Object.defineProperty(exports, "formatPhoneNumber", {
|
|
52
52
|
enumerable: true,
|
|
53
|
-
get: function () { return
|
|
53
|
+
get: function () { return chunkL7YQBSEL_cjs.formatPhoneNumber; }
|
|
54
54
|
});
|
|
55
55
|
Object.defineProperty(exports, "isPhoneNumberEmpty", {
|
|
56
56
|
enumerable: true,
|
|
57
|
-
get: function () { return
|
|
57
|
+
get: function () { return chunkL7YQBSEL_cjs.isPhoneNumberEmpty; }
|
|
58
58
|
});
|
|
59
59
|
Object.defineProperty(exports, "isValidPhoneNumber", {
|
|
60
60
|
enumerable: true,
|
|
61
|
-
get: function () { return
|
|
61
|
+
get: function () { return chunkL7YQBSEL_cjs.isValidPhoneNumber; }
|
|
62
62
|
});
|
|
63
63
|
Object.defineProperty(exports, "unformatPhoneNumber", {
|
|
64
64
|
enumerable: true,
|
|
65
|
-
get: function () { return
|
|
65
|
+
get: function () { return chunkL7YQBSEL_cjs.unformatPhoneNumber; }
|
|
66
66
|
});
|
|
67
67
|
Object.defineProperty(exports, "isStorybookDocsMode", {
|
|
68
68
|
enumerable: true,
|
package/dist/utils/index.d.cts
CHANGED
|
@@ -16,7 +16,13 @@ declare function cn(...inputs: ClassValue[]): string;
|
|
|
16
16
|
* Phone number formatting utilities
|
|
17
17
|
*/
|
|
18
18
|
/**
|
|
19
|
-
* Formats a phone number string to US format
|
|
19
|
+
* Formats a phone number string to US format.
|
|
20
|
+
*
|
|
21
|
+
* Behavior:
|
|
22
|
+
* - 10 digits → `(XXX) XXX-XXXX`
|
|
23
|
+
* - 11 digits w/ `1` → `+1 (XXX) XXX-XXXX` (E.164 US/Canada)
|
|
24
|
+
* - 12+ digits → `+CC (XXX) XXX-XXXX...` (best-effort intl)
|
|
25
|
+
* - partial (typing) → progressive `(`, `(XXX`, `(XXX) X`, etc.
|
|
20
26
|
*/
|
|
21
27
|
declare function formatPhoneNumber(value: string): string;
|
|
22
28
|
/**
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -16,7 +16,13 @@ declare function cn(...inputs: ClassValue[]): string;
|
|
|
16
16
|
* Phone number formatting utilities
|
|
17
17
|
*/
|
|
18
18
|
/**
|
|
19
|
-
* Formats a phone number string to US format
|
|
19
|
+
* Formats a phone number string to US format.
|
|
20
|
+
*
|
|
21
|
+
* Behavior:
|
|
22
|
+
* - 10 digits → `(XXX) XXX-XXXX`
|
|
23
|
+
* - 11 digits w/ `1` → `+1 (XXX) XXX-XXXX` (E.164 US/Canada)
|
|
24
|
+
* - 12+ digits → `+CC (XXX) XXX-XXXX...` (best-effort intl)
|
|
25
|
+
* - partial (typing) → progressive `(`, `(XXX`, `(XXX) X`, etc.
|
|
20
26
|
*/
|
|
21
27
|
declare function formatPhoneNumber(value: string): string;
|
|
22
28
|
/**
|
package/dist/utils/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../chunk-V2DF2GUE.js';
|
|
2
2
|
export { calculateAge, dateToDisplayFormat, displayFormatToDateString, formatDateValue, isDateEmpty, isDateInFuture, isDateInPast, isValidDate, isValidDrivingAge, parseDateValue } from '../chunk-RC2YMOMS.js';
|
|
3
|
-
export { formatPhoneNumber, isPhoneNumberEmpty, isValidPhoneNumber, unformatPhoneNumber } from '../chunk-
|
|
3
|
+
export { formatPhoneNumber, isPhoneNumberEmpty, isValidPhoneNumber, unformatPhoneNumber } from '../chunk-ZVPJ2MH6.js';
|
|
4
4
|
export { isStorybookDocsMode } from '../chunk-VSQF22GL.js';
|
|
5
5
|
export { cn } from '../chunk-F3SOEIN2.js';
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/phone.ts"],"names":[],"mappings":";;;AAOO,SAAS,kBAAkB,KAAA,EAAuB;AACvD,EAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,CAAE,KAAA,CAAM,GAAG,EAAE,CAAA;AACnD,EAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AAChC,EAAA,IAAI,MAAA,CAAO,MAAA,IAAU,CAAA,EAAG,OAAO,IAAI,MAAM,CAAA,CAAA;AACzC,EAAA,IAAI,MAAA,CAAO,MAAA,IAAU,CAAA,EAAG,OAAO,IAAI,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,EAAA,EAAK,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACzE,EAAA,OAAO,IAAI,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,EAAA,EAAK,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA,EAAI,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACzE;AAKO,SAAS,oBAAoB,KAAA,EAAuB;AACzD,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAChC;AAKO,SAAS,mBAAmB,KAAA,EAAwB;AACzD,EAAA,MAAM,MAAA,GAAS,oBAAoB,KAAK,CAAA;AACxC,EAAA,OAAO,OAAO,MAAA,KAAW,EAAA;AAC3B;AAKO,SAAS,mBAAmB,KAAA,EAAwB;AACzD,EAAA,OAAO,mBAAA,CAAoB,KAAK,CAAA,CAAE,MAAA,KAAW,CAAA;AAC/C","file":"chunk-BTJHYGPI.cjs","sourcesContent":["/**\n * Phone number formatting utilities\n */\n\n/**\n * Formats a phone number string to US format: (XXX) XXX-XXXX\n */\nexport function formatPhoneNumber(value: string): string {\n const digits = value.replace(/\\D/g, '').slice(0, 10);\n if (digits.length === 0) return '';\n if (digits.length <= 3) return `(${digits}`;\n if (digits.length <= 6) return `(${digits.slice(0, 3)}) ${digits.slice(3)}`;\n return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;\n}\n\n/**\n * Removes formatting from a phone number, returning only digits\n */\nexport function unformatPhoneNumber(value: string): string {\n return value.replace(/\\D/g, '');\n}\n\n/**\n * Validates if a phone number has exactly 10 digits\n */\nexport function isValidPhoneNumber(value: string): boolean {\n const digits = unformatPhoneNumber(value);\n return digits.length === 10;\n}\n\n/**\n * Checks if a phone number string is empty (no digits)\n */\nexport function isPhoneNumberEmpty(value: string): boolean {\n return unformatPhoneNumber(value).length === 0;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/phone.ts"],"names":[],"mappings":";AAOO,SAAS,kBAAkB,KAAA,EAAuB;AACvD,EAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,CAAE,KAAA,CAAM,GAAG,EAAE,CAAA;AACnD,EAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,EAAA;AAChC,EAAA,IAAI,MAAA,CAAO,MAAA,IAAU,CAAA,EAAG,OAAO,IAAI,MAAM,CAAA,CAAA;AACzC,EAAA,IAAI,MAAA,CAAO,MAAA,IAAU,CAAA,EAAG,OAAO,IAAI,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,EAAA,EAAK,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACzE,EAAA,OAAO,IAAI,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,EAAA,EAAK,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA,EAAI,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AACzE;AAKO,SAAS,oBAAoB,KAAA,EAAuB;AACzD,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAChC;AAKO,SAAS,mBAAmB,KAAA,EAAwB;AACzD,EAAA,MAAM,MAAA,GAAS,oBAAoB,KAAK,CAAA;AACxC,EAAA,OAAO,OAAO,MAAA,KAAW,EAAA;AAC3B;AAKO,SAAS,mBAAmB,KAAA,EAAwB;AACzD,EAAA,OAAO,mBAAA,CAAoB,KAAK,CAAA,CAAE,MAAA,KAAW,CAAA;AAC/C","file":"chunk-CEHWXAAI.js","sourcesContent":["/**\n * Phone number formatting utilities\n */\n\n/**\n * Formats a phone number string to US format: (XXX) XXX-XXXX\n */\nexport function formatPhoneNumber(value: string): string {\n const digits = value.replace(/\\D/g, '').slice(0, 10);\n if (digits.length === 0) return '';\n if (digits.length <= 3) return `(${digits}`;\n if (digits.length <= 6) return `(${digits.slice(0, 3)}) ${digits.slice(3)}`;\n return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;\n}\n\n/**\n * Removes formatting from a phone number, returning only digits\n */\nexport function unformatPhoneNumber(value: string): string {\n return value.replace(/\\D/g, '');\n}\n\n/**\n * Validates if a phone number has exactly 10 digits\n */\nexport function isValidPhoneNumber(value: string): boolean {\n const digits = unformatPhoneNumber(value);\n return digits.length === 10;\n}\n\n/**\n * Checks if a phone number string is empty (no digits)\n */\nexport function isPhoneNumberEmpty(value: string): boolean {\n return unformatPhoneNumber(value).length === 0;\n}\n"]}
|