{
  "$schema": "https://blode.co/ui/schema/registry-item.json",
  "name": "phone-input",
  "title": "Phone Input",
  "author": "Matthew Blode",
  "description": "An international phone number input with country code selector.",
  "dependencies": ["blode-icons-react", "react-phone-number-input"],
  "registryDependencies": ["button", "command", "input", "popover", "scroll-area"],
  "files": [
    {
      "path": "ui/phone-input.tsx",
      "content": "\"use client\";\n\nimport { CheckIcon, ChevronGrabberVerticalIcon, CircleXFilledIcon } from \"blode-icons-react\";\nimport * as React from \"react\";\nimport * as RPNInput from \"react-phone-number-input\";\nimport flags from \"react-phone-number-input/flags\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport { Button } from \"./button\";\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n} from \"./command\";\nimport { Input } from \"./input\";\nimport type { InputProps } from \"./input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./popover\";\nimport { ScrollArea } from \"./scroll-area\";\n\ninterface CountrySelectOption {\n  label: string;\n  value?: RPNInput.Country;\n}\n\ninterface CountrySelectProps {\n  disabled?: boolean;\n  onChange: (value?: RPNInput.Country) => void;\n  options: CountrySelectOption[];\n  value?: RPNInput.Country;\n}\n\nexport type PhoneInputProps = Omit<\n  React.InputHTMLAttributes<HTMLInputElement>,\n  \"onChange\" | \"value\"\n> &\n  Omit<RPNInput.Props<typeof RPNInput.default>, \"onChange\"> & {\n    clearable?: boolean;\n    clearClassName?: string;\n    fallbackCountry?: RPNInput.Country;\n    onChange?: (value: RPNInput.Value | \"\") => void;\n    onClear?: () => void;\n    ref?: React.Ref<React.ComponentRef<typeof RPNInput.default>>;\n  };\n\nconst PhoneNumberInput = RPNInput.default;\n\nconst FlagComponent = ({\n  country,\n  countryName,\n}: {\n  country?: RPNInput.Country;\n  countryName: string;\n}) => {\n  const Flag = country ? flags[country] : undefined;\n\n  return (\n    <span className=\"flex h-4 w-6 overflow-hidden rounded-xs bg-foreground/20 [&_svg]:size-full!\">\n      {Flag ? <Flag title={countryName} /> : null}\n    </span>\n  );\n};\n\nconst InputComponent = ({\n  className,\n  inputClassName,\n  ref,\n  ...props\n}: InputProps & { inputClassName?: string; ref?: React.Ref<HTMLInputElement> }) => (\n  <Input className={cn(\"rounded-s-none!\", className, inputClassName)} ref={ref} {...props} />\n);\n\nconst CountrySelect = ({ disabled, onChange, options, value }: CountrySelectProps) => {\n  const selectedCountryLabel =\n    options.find((option) => option.value === value)?.label ?? \"Select country\";\n\n  return (\n    <Popover>\n      <PopoverTrigger asChild>\n        <Button\n          aria-label={selectedCountryLabel}\n          className=\"phone-input-country-select-button gap-1 rounded-e-none! border-r-0 px-3\"\n          disabled={disabled}\n          size=\"input\"\n          type=\"button\"\n          variant=\"input\"\n        >\n          <FlagComponent country={value} countryName={selectedCountryLabel} />\n          <ChevronGrabberVerticalIcon\n            className={cn(\"-mr-2 h-4 w-4 opacity-50\", disabled ? \"hidden\" : \"opacity-100\")}\n          />\n        </Button>\n      </PopoverTrigger>\n      <PopoverContent className=\"w-[300px] p-0\">\n        <Command>\n          <CommandList>\n            <ScrollArea className=\"h-72\" onWheel={(event) => event.stopPropagation()}>\n              <CommandInput placeholder=\"Search country...\" />\n              <CommandEmpty>No country found.</CommandEmpty>\n              <CommandGroup>\n                {options.map((option) => {\n                  if (!option.value) {\n                    return null;\n                  }\n\n                  return (\n                    <CommandItem\n                      className=\"gap-2\"\n                      key={option.value}\n                      onSelect={() => onChange(option.value)}\n                    >\n                      <FlagComponent country={option.value} countryName={option.label} />\n                      <span className=\"flex-1 text-sm\">{option.label}</span>\n                      <span className=\"text-foreground/50 text-sm\">\n                        +{RPNInput.getCountryCallingCode(option.value)}\n                      </span>\n                      <CheckIcon\n                        className={cn(\n                          \"ml-auto h-4 w-4\",\n                          option.value === value ? \"opacity-100\" : \"opacity-0\",\n                        )}\n                      />\n                    </CommandItem>\n                  );\n                })}\n              </CommandGroup>\n            </ScrollArea>\n          </CommandList>\n        </Command>\n      </PopoverContent>\n    </Popover>\n  );\n};\n\nconst PhoneInput = ({\n  className,\n  clearClassName,\n  clearable,\n  defaultCountry,\n  fallbackCountry = \"US\",\n  onChange,\n  onClear,\n  placeholder = \"Enter phone number\",\n  ref,\n  ...props\n}: PhoneInputProps) => {\n  const hasValue = Boolean(props.value);\n\n  const handleChange = React.useCallback(\n    (value?: RPNInput.Value) => {\n      onChange?.(value ?? \"\");\n    },\n    [onChange],\n  );\n\n  const handleClear = React.useCallback(() => {\n    onClear?.();\n\n    if (!onClear) {\n      onChange?.(\"\");\n    }\n  }, [onClear, onChange]);\n\n  return (\n    <div className=\"relative\">\n      <PhoneNumberInput\n        className=\"flex\"\n        countrySelectComponent={CountrySelect}\n        defaultCountry={defaultCountry ?? fallbackCountry}\n        flagComponent={FlagComponent}\n        inputClassName={className}\n        inputComponent={InputComponent}\n        onChange={handleChange}\n        placeholder={placeholder}\n        ref={ref}\n        {...props}\n      />\n\n      {clearable && hasValue ? (\n        <div className=\"absolute top-0 right-0 flex flex-row gap-1 pr-3\">\n          <button\n            aria-label=\"clear input\"\n            className={cn(\n              \"flex h-[var(--field-height)] cursor-pointer items-center justify-center p-0! text-muted-foreground\",\n              clearClassName,\n            )}\n            onClick={handleClear}\n            tabIndex={-1}\n            type=\"button\"\n          >\n            <CircleXFilledIcon className=\"size-5 text-muted-foreground/50\" />\n          </button>\n        </div>\n      ) : null}\n    </div>\n  );\n};\n\nexport { PhoneInput };\n",
      "type": "registry:ui",
      "target": ""
    }
  ],
  "type": "registry:ui"
}
