{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "phone-input", "type": "registry:component", "dependencies": [ "react-phone-number-input", "lucide-react" ], "registryDependencies": [ "input", "button", "command", "popover", "scroll-area" ], "files": [ { "path": "components/domain-ui/experimental/phone-input.tsx", "content": "import * as React from \"react\";\nimport { CheckIcon, ChevronsUpDown } from \"lucide-react\";\nimport * as RPNInput from \"react-phone-number-input\";\nimport flags from \"react-phone-number-input/flags\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"@/components/ui/command\";\nimport { Input } from \"@/components/ui/input\";\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\nimport { cn } from \"@/lib/utils\";\n\ntype PhoneInputProps = Omit<\n React.ComponentProps<\"input\">,\n \"onChange\" | \"value\" | \"ref\"\n> &\n Omit, \"onChange\"> & {\n onChange?: (value: RPNInput.Value) => void;\n };\n\nconst PhoneInput: React.ForwardRefExoticComponent =\n React.forwardRef, PhoneInputProps>(\n ({ className, onChange, value, ...props }, ref) => {\n return (\n onChange?.(value || (\"\" as RPNInput.Value))}\n {...props}\n />\n );\n },\n );\nPhoneInput.displayName = \"PhoneInput\";\n\nconst InputComponent = React.forwardRef<\n HTMLInputElement,\n React.ComponentProps<\"input\">\n>(({ className, ...props }, ref) => (\n \n));\nInputComponent.displayName = \"InputComponent\";\n\ntype CountryEntry = { label: string; value: RPNInput.Country | undefined };\n\ntype CountrySelectProps = {\n disabled?: boolean;\n value: RPNInput.Country;\n options: CountryEntry[];\n onChange: (country: RPNInput.Country) => void;\n};\n\nconst CountrySelect = ({\n disabled,\n value: selectedCountry,\n options: countryList,\n onChange,\n}: CountrySelectProps) => {\n const scrollAreaRef = React.useRef(null);\n const [searchValue, setSearchValue] = React.useState(\"\");\n const [isOpen, setIsOpen] = React.useState(false);\n\n return (\n {\n setIsOpen(open);\n open && setSearchValue(\"\");\n }}\n >\n \n \n \n \n \n \n \n \n {\n setSearchValue(value);\n setTimeout(() => {\n if (scrollAreaRef.current) {\n const viewportElement = scrollAreaRef.current.querySelector(\n \"[data-radix-scroll-area-viewport]\",\n );\n if (viewportElement) {\n viewportElement.scrollTop = 0;\n }\n }\n }, 0);\n }}\n placeholder=\"Search country...\"\n />\n \n \n No country found.\n \n {countryList.map(({ value, label }) =>\n value ? (\n setIsOpen(false)}\n />\n ) : null,\n )}\n \n \n \n \n \n \n );\n};\n\ninterface CountrySelectOptionProps extends RPNInput.FlagProps {\n selectedCountry: RPNInput.Country;\n onChange: (country: RPNInput.Country) => void;\n onSelectComplete: () => void;\n}\n\nconst CountrySelectOption = ({\n country,\n countryName,\n selectedCountry,\n onChange,\n onSelectComplete,\n}: CountrySelectOptionProps) => {\n const handleSelect = () => {\n onChange(country);\n onSelectComplete();\n };\n\n return (\n \n \n {countryName}\n {`+${RPNInput.getCountryCallingCode(country)}`}\n \n \n );\n};\n\nconst FlagComponent = ({ country, countryName }: RPNInput.FlagProps) => {\n const Flag = flags[country];\n\n return (\n \n {Flag && }\n \n );\n};\n\nexport { PhoneInput };\n", "type": "registry:component" } ], "meta": { "tags": [ "input", "phone", "international", "country", "validation", "formatting", "experimental", "form", "contact" ] } }