{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "phone",
  "title": "Phone",
  "author": "vorhdam <https://github.com/vorhdam>",
  "description": "A simple but clean phone input with a region selection so you dont have to worry about messing up.",
  "dependencies": [
    "lucide-react",
    "libphonenumber-js"
  ],
  "registryDependencies": [
    "command",
    "input-group",
    "popover"
  ],
  "files": [
    {
      "path": "src/components/ui/flag.tsx",
      "content": "\"use client\";\r\n\r\nimport * as React from \"react\";\r\n\r\nimport { countryList } from \"@/lib/geo\";\r\n\r\nexport type FlagCode = (typeof countryList)[number];\r\n\r\ninterface FlagProps extends React.SVGProps<SVGSVGElement> {\r\n  code: FlagCode;\r\n}\r\n\r\nconst flagCache = new Map<string, { viewBox: string; content: string }>();\r\n\r\nasync function loadFlag(code: string) {\r\n  const cached = flagCache.get(code);\r\n  if (cached) return cached;\r\n\r\n  const response = await fetch(\r\n    `https://cdn.jsdelivr.net/npm/@nordaun/flags@latest/flags/${code}.svg`,\r\n  );\r\n  const text = await response.text();\r\n  const svg = new DOMParser()\r\n    .parseFromString(text, \"image/svg+xml\")\r\n    .querySelector(\"svg\");\r\n\r\n  if (!svg) throw new Error(`Invalid flag SVG: ${code}`);\r\n\r\n  const parsed = {\r\n    viewBox: svg.getAttribute(\"viewBox\") ?? \"0 0 36 36\",\r\n    content: svg.innerHTML,\r\n  };\r\n\r\n  flagCache.set(code, parsed);\r\n  return parsed;\r\n}\r\n\r\nfunction Flag({ code, className, viewBox, ...props }: FlagProps) {\r\n  const formattedCode = code?.toLowerCase();\r\n  const [flag, setFlag] = React.useState(\r\n    () => flagCache.get(formattedCode) ?? null,\r\n  );\r\n\r\n  React.useEffect(() => {\r\n    let cancelled = false;\r\n\r\n    loadFlag(formattedCode)\r\n      .then((parsed) => {\r\n        if (!cancelled) setFlag(parsed);\r\n      })\r\n      .catch(() => {\r\n        if (!cancelled) setFlag(null);\r\n      });\r\n\r\n    return () => {\r\n      cancelled = true;\r\n    };\r\n  }, [formattedCode]);\r\n\r\n  return (\r\n    <svg\r\n      className={className}\r\n      viewBox={viewBox ?? flag?.viewBox ?? \"0 0 36 36\"}\r\n      aria-hidden\r\n      dangerouslySetInnerHTML={flag ? { __html: flag.content } : undefined}\r\n      {...props}\r\n    />\r\n  );\r\n}\r\n\r\nexport { Flag };\r\n",
      "type": "registry:ui"
    },
    {
      "path": "src/components/ui/phone.tsx",
      "content": "\"use client\";\r\n\r\nimport parsePhoneNumberFromString, {\r\n  CountryCode,\r\n  E164Number,\r\n  getCountries,\r\n  getCountryCallingCode,\r\n} from \"libphonenumber-js\";\r\nimport * as React from \"react\";\r\n\r\nimport {\r\n  Command,\r\n  CommandEmpty,\r\n  CommandGroup,\r\n  CommandInput,\r\n  CommandItem,\r\n  CommandList,\r\n} from \"@/components/ui/command\";\r\nimport { Flag } from \"@/components/ui/flag\";\r\nimport {\r\n  InputGroup,\r\n  InputGroupAddon,\r\n  InputGroupInput,\r\n  InputGroupText,\r\n} from \"@/components/ui/input-group\";\r\nimport {\r\n  Popover,\r\n  PopoverContent,\r\n  PopoverTrigger,\r\n} from \"@/components/ui/popover\";\r\nimport { countryNames } from \"@/lib/geo\";\r\nimport { cn } from \"@/lib/utils\";\r\n\r\nconst codes: CountryCode[] = getCountries();\r\n\r\ntype PhoneProps = React.ComponentProps<\"input\"> & {\r\n  value?: string;\r\n  searchPlaceholder: string;\r\n  emptyPlaceholder: string;\r\n};\r\n\r\nfunction Phone({\r\n  value,\r\n  onChange,\r\n  placeholder,\r\n  searchPlaceholder,\r\n  emptyPlaceholder,\r\n  ...props\r\n}: PhoneProps) {\r\n  const countries = React.useMemo(\r\n    () =>\r\n      [...codes].sort((a, b) => {\r\n        return countryNames[a].localeCompare(countryNames[b]);\r\n      }),\r\n    [],\r\n  );\r\n\r\n  const [country, setCountry] = React.useState<CountryCode>(\"US\");\r\n  const [number, setNumber] = React.useState<string>(\"\");\r\n  const [open, setOpen] = React.useState(false);\r\n\r\n  const inputRef = React.useRef<HTMLInputElement>(null);\r\n\r\n  const handleCountryChange = (newCountry: CountryCode) => {\r\n    setCountry(newCountry);\r\n    setOpen(false);\r\n    const fullNumber = parsePhoneNumberFromString(number, newCountry);\r\n    triggerOnChange(fullNumber?.number);\r\n  };\r\n\r\n  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\r\n    const filteredValue = e.target.value.replace(/[^0-9\\-() ]/g, \"\");\r\n    setNumber(filteredValue);\r\n    const fullNumber = parsePhoneNumberFromString(filteredValue, country);\r\n    triggerOnChange(fullNumber?.number);\r\n  };\r\n\r\n  const triggerOnChange = (newNumber: E164Number | undefined) => {\r\n    if (!onChange || !inputRef.current) return;\r\n    const syntheticEvent = {\r\n      target: {\r\n        ...inputRef.current,\r\n        value: newNumber?.toString(),\r\n      },\r\n      currentTarget: inputRef.current,\r\n    } as React.ChangeEvent<HTMLInputElement>;\r\n    onChange(syntheticEvent);\r\n  };\r\n\r\n  const { name, className, ...groupProps } = props;\r\n\r\n  return (\r\n    <>\r\n      <InputGroup className={cn(className)}>\r\n        <InputGroupAddon\r\n          align=\"inline-start\"\r\n          className=\"pl-1 my-0\"\r\n          onClick={(e) => {\r\n            if (!e.currentTarget.contains(e.target as Node)) return;\r\n          }}\r\n        >\r\n          <Popover open={open} onOpenChange={setOpen}>\r\n            <PopoverTrigger\r\n              aria-expanded={open}\r\n              type=\"button\"\r\n              role=\"combobox\"\r\n              className=\"flex flex-row gap-1.5 h-8 items-center px-2 hover:bg-accent rounded-md cursor-pointer border-0 bg-transparent\"\r\n            >\r\n              <Flag code={country} className=\"size-4\" />\r\n              <InputGroupText>+{getCountryCallingCode(country)}</InputGroupText>\r\n            </PopoverTrigger>\r\n            <PopoverContent className=\"w-[320px] p-0\" align=\"start\">\r\n              <Command>\r\n                <CommandInput placeholder={searchPlaceholder} />\r\n                <CommandList className=\"mt-1\">\r\n                  <CommandEmpty>{emptyPlaceholder}</CommandEmpty>\r\n                  <CommandGroup>\r\n                    {countries.map((countryCode) => (\r\n                      <CommandItem\r\n                        key={countryCode}\r\n                        value={`${countryNames[countryCode]} +${getCountryCallingCode(countryCode)}`}\r\n                        onSelect={() => handleCountryChange(countryCode)}\r\n                        className=\"cursor-pointer\"\r\n                      >\r\n                        <span className=\"flex flex-row gap-1.5 h-5 items-center w-full\">\r\n                          <Flag code={countryCode} className=\"size-5\" />\r\n                          <span className=\"w-10 text-end\">\r\n                            +{getCountryCallingCode(countryCode)}\r\n                          </span>\r\n                          <span className=\"text-muted-foreground flex-1\">\r\n                            {countryNames[countryCode]}\r\n                          </span>\r\n                          {country === countryCode && (\r\n                            <svg\r\n                              className=\"ml-auto h-4 w-4\"\r\n                              fill=\"none\"\r\n                              stroke=\"currentColor\"\r\n                              viewBox=\"0 0 24 24\"\r\n                            >\r\n                              <path\r\n                                strokeLinecap=\"round\"\r\n                                strokeLinejoin=\"round\"\r\n                                strokeWidth={2}\r\n                                d=\"M5 13l4 4L19 7\"\r\n                              />\r\n                            </svg>\r\n                          )}\r\n                        </span>\r\n                      </CommandItem>\r\n                    ))}\r\n                  </CommandGroup>\r\n                </CommandList>\r\n              </Command>\r\n            </PopoverContent>\r\n          </Popover>\r\n        </InputGroupAddon>\r\n        <InputGroupInput\r\n          ref={inputRef}\r\n          type=\"tel\"\r\n          value={number}\r\n          onChange={handleInputChange}\r\n          placeholder={placeholder}\r\n          {...groupProps}\r\n        />\r\n      </InputGroup>\r\n      {name && <input type=\"hidden\" name={name} value={value} />}\r\n    </>\r\n  );\r\n}\r\n\r\nexport { Phone };\r\n",
      "type": "registry:ui"
    },
    {
      "path": "src/lib/geo.ts",
      "content": "const countryNames = {\r\n  AC: \"Ascension Island\",\r\n  AD: \"Andorra\",\r\n  AE: \"United Arab Emirates\",\r\n  AF: \"Afghanistan\",\r\n  AG: \"Antigua and Barbuda\",\r\n  AI: \"Anguilla\",\r\n  AL: \"Albania\",\r\n  AM: \"Armenia\",\r\n  AO: \"Angola\",\r\n  AR: \"Argentina\",\r\n  AS: \"American Samoa\",\r\n  AT: \"Austria\",\r\n  AU: \"Australia\",\r\n  AW: \"Aruba\",\r\n  AX: \"Åland Islands\",\r\n  AZ: \"Azerbaijan\",\r\n  BA: \"Bosnia and Herzegovina\",\r\n  BB: \"Barbados\",\r\n  BD: \"Bangladesh\",\r\n  BE: \"Belgium\",\r\n  BF: \"Burkina Faso\",\r\n  BG: \"Bulgaria\",\r\n  BH: \"Bahrain\",\r\n  BI: \"Burundi\",\r\n  BJ: \"Benin\",\r\n  BL: \"Saint Barthélemy\",\r\n  BM: \"Bermuda\",\r\n  BN: \"Brunei Darussalam\",\r\n  BO: \"Bolivia\",\r\n  BQ: \"Bonaire\",\r\n  BR: \"Brazil\",\r\n  BS: \"Bahamas\",\r\n  BT: \"Bhutan\",\r\n  BW: \"Botswana\",\r\n  BY: \"Belarus\",\r\n  BZ: \"Belize\",\r\n  CA: \"Canada\",\r\n  CC: \"Cocos Islands\",\r\n  CD: \"Republic of Congo\",\r\n  CF: \"Central African Republic\",\r\n  CG: \"Congo\",\r\n  CH: \"Switzerland\",\r\n  CI: \"Côte d'Ivoire\",\r\n  CK: \"Cook Islands\",\r\n  CL: \"Chile\",\r\n  CM: \"Cameroon\",\r\n  CN: \"China\",\r\n  CO: \"Colombia\",\r\n  CR: \"Costa Rica\",\r\n  CU: \"Cuba\",\r\n  CV: \"Cabo Verde\",\r\n  CW: \"Curaçao\",\r\n  CX: \"Christmas Island\",\r\n  CY: \"Cyprus\",\r\n  CZ: \"Czechia\",\r\n  DE: \"Germany\",\r\n  DJ: \"Djibouti\",\r\n  DK: \"Denmark\",\r\n  DM: \"Dominica\",\r\n  DO: \"Dominican Republic\",\r\n  DZ: \"Algeria\",\r\n  EC: \"Ecuador\",\r\n  EE: \"Estonia\",\r\n  EG: \"Egypt\",\r\n  EH: \"Western Sahara\",\r\n  ER: \"Eritrea\",\r\n  ES: \"Spain\",\r\n  ET: \"Ethiopia\",\r\n  FI: \"Finland\",\r\n  FJ: \"Fiji\",\r\n  FK: \"Falkland Islands\",\r\n  FM: \"Micronesia\",\r\n  FO: \"Faroe Islands\",\r\n  FR: \"France\",\r\n  GA: \"Gabon\",\r\n  GB: \"United Kingdom\",\r\n  GD: \"Grenada\",\r\n  GE: \"Georgia\",\r\n  GF: \"French Guiana\",\r\n  GG: \"Guernsey\",\r\n  GH: \"Ghana\",\r\n  GI: \"Gibraltar\",\r\n  GL: \"Greenland\",\r\n  GM: \"Gambia\",\r\n  GN: \"Guinea\",\r\n  GP: \"Guadeloupe\",\r\n  GQ: \"Equatorial Guinea\",\r\n  GR: \"Greece\",\r\n  GT: \"Guatemala\",\r\n  GU: \"Guam\",\r\n  GW: \"Guinea-Bissau\",\r\n  GY: \"Guyana\",\r\n  HK: \"Hong Kong\",\r\n  HN: \"Honduras\",\r\n  HR: \"Croatia\",\r\n  HT: \"Haiti\",\r\n  HU: \"Hungary\",\r\n  ID: \"Indonesia\",\r\n  IE: \"Ireland\",\r\n  IL: \"Israel\",\r\n  IM: \"Isle of Man\",\r\n  IN: \"India\",\r\n  IO: \"British Indian Ocean Territory\",\r\n  IQ: \"Iraq\",\r\n  IR: \"Iran\",\r\n  IS: \"Iceland\",\r\n  IT: \"Italy\",\r\n  JE: \"Jersey\",\r\n  JM: \"Jamaica\",\r\n  JO: \"Jordan\",\r\n  JP: \"Japan\",\r\n  KE: \"Kenya\",\r\n  KG: \"Kyrgyzstan\",\r\n  KH: \"Cambodia\",\r\n  KI: \"Kiribati\",\r\n  KM: \"Comoros\",\r\n  KN: \"Saint Kitts and Nevis\",\r\n  KP: \"North Korea\",\r\n  KR: \"South Korea\",\r\n  KW: \"Kuwait\",\r\n  KY: \"Cayman Islands\",\r\n  KZ: \"Kazakhstan\",\r\n  LA: \"Laos\",\r\n  LB: \"Lebanon\",\r\n  LC: \"Saint Lucia\",\r\n  LI: \"Liechtenstein\",\r\n  LK: \"Sri Lanka\",\r\n  LR: \"Liberia\",\r\n  LS: \"Lesotho\",\r\n  LT: \"Lithuania\",\r\n  LU: \"Luxembourg\",\r\n  LV: \"Latvia\",\r\n  LY: \"Libya\",\r\n  MA: \"Morocco\",\r\n  MC: \"Monaco\",\r\n  MD: \"Moldova\",\r\n  ME: \"Montenegro\",\r\n  MF: \"Saint Martin\",\r\n  MG: \"Madagascar\",\r\n  MH: \"Marshall Islands\",\r\n  MK: \"North Macedonia\",\r\n  ML: \"Mali\",\r\n  MM: \"Myanmar\",\r\n  MN: \"Mongolia\",\r\n  MO: \"Macao\",\r\n  MP: \"Northern Mariana Islands\",\r\n  MQ: \"Martinique\",\r\n  MR: \"Mauritania\",\r\n  MS: \"Montserrat\",\r\n  MT: \"Malta\",\r\n  MU: \"Mauritius\",\r\n  MV: \"Maldives\",\r\n  MW: \"Malawi\",\r\n  MX: \"Mexico\",\r\n  MY: \"Malaysia\",\r\n  MZ: \"Mozambique\",\r\n  NA: \"Namibia\",\r\n  NC: \"New Caledonia\",\r\n  NE: \"Niger\",\r\n  NF: \"Norfolk Island\",\r\n  NG: \"Nigeria\",\r\n  NI: \"Nicaragua\",\r\n  NL: \"Netherlands\",\r\n  NO: \"Norway\",\r\n  NP: \"Nepal\",\r\n  NR: \"Nauru\",\r\n  NU: \"Niue\",\r\n  NZ: \"New Zealand\",\r\n  OM: \"Oman\",\r\n  PA: \"Panama\",\r\n  PE: \"Peru\",\r\n  PF: \"French Polynesia\",\r\n  PG: \"Papua New Guinea\",\r\n  PH: \"Philippines\",\r\n  PK: \"Pakistan\",\r\n  PL: \"Poland\",\r\n  PM: \"Saint Pierre\",\r\n  PR: \"Puerto Rico\",\r\n  PS: \"Palestine\",\r\n  PT: \"Portugal\",\r\n  PW: \"Palau\",\r\n  PY: \"Paraguay\",\r\n  QA: \"Qatar\",\r\n  RE: \"Réunion\",\r\n  RO: \"Romania\",\r\n  RS: \"Serbia\",\r\n  RU: \"Russia\",\r\n  RW: \"Rwanda\",\r\n  SA: \"Saudi Arabia\",\r\n  SB: \"Solomon Islands\",\r\n  SC: \"Seychelles\",\r\n  SD: \"Sudan\",\r\n  SE: \"Sweden\",\r\n  SG: \"Singapore\",\r\n  SH: \"Saint Helena\",\r\n  SI: \"Slovenia\",\r\n  SJ: \"Svalbard\",\r\n  SK: \"Slovakia\",\r\n  SL: \"Sierra Leone\",\r\n  SM: \"San Marino\",\r\n  SN: \"Senegal\",\r\n  SO: \"Somalia\",\r\n  SR: \"Suriname\",\r\n  SS: \"South Sudan\",\r\n  ST: \"Sao Tome and Principe\",\r\n  SV: \"El Salvador\",\r\n  SX: \"Sint Maarten\",\r\n  SY: \"Syria\",\r\n  SZ: \"Eswatini\",\r\n  TA: \"Tristan da Cunha\",\r\n  TC: \"Turks and Caicos\",\r\n  TD: \"Chad\",\r\n  TG: \"Togo\",\r\n  TH: \"Thailand\",\r\n  TJ: \"Tajikistan\",\r\n  TK: \"Tokelau\",\r\n  TL: \"Timor-Leste\",\r\n  TM: \"Turkmenistan\",\r\n  TN: \"Tunisia\",\r\n  TO: \"Tonga\",\r\n  TR: \"Türkiye\",\r\n  TT: \"Trinidad and Tobago\",\r\n  TV: \"Tuvalu\",\r\n  TW: \"Taiwan\",\r\n  TZ: \"Tanzania\",\r\n  UA: \"Ukraine\",\r\n  UG: \"Uganda\",\r\n  US: \"United States\",\r\n  UY: \"Uruguay\",\r\n  UZ: \"Uzbekistan\",\r\n  VA: \"Vatican City\",\r\n  VC: \"Saint Vincent and the Grenadines\",\r\n  VE: \"Venezuela\",\r\n  VG: \"British Virgin Islands\",\r\n  VI: \"US Virgin Islands\",\r\n  VN: \"Vietnam\",\r\n  VU: \"Vanuatu\",\r\n  WF: \"Wallis and Futuna\",\r\n  WS: \"Samoa\",\r\n  XK: \"Kosovo\",\r\n  YE: \"Yemen\",\r\n  YT: \"Mayotte\",\r\n  ZA: \"South Africa\",\r\n  ZM: \"Zambia\",\r\n  ZW: \"Zimbabwe\",\r\n};\r\n\r\nconst countryList = Object.keys(countryNames);\r\n\r\nexport { countryList, countryNames };\r\n",
      "type": "registry:lib"
    }
  ],
  "css": {
    "::-webkit-scrollbar": {
      "width": "1.2rem",
      "min-height": "1.2rem"
    },
    "::-webkit-scrollbar-thumb": {
      "border": "0.4rem solid rgba(0, 0, 0, 0)",
      "background-clip": "padding-box",
      "background-color": "var(--border)",
      "border-radius": "var(--radius)"
    },
    "::-webkit-scrollbar-thumb:hover": {
      "background-color": "var(--ring)"
    }
  },
  "type": "registry:block"
}