{
  "name": "phone-input",
  "type": "registry:ui",
  "dependencies": [
    "radix-ui"
  ],
  "registryDependencies": [
    "command",
    "input",
    "popover",
    "@diceui/use-as-ref",
    "@diceui/use-isomorphic-layout-effect",
    "@diceui/use-lazy-ref"
  ],
  "files": [
    {
      "path": "ui/phone-input.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { Check, ChevronDown } from \"lucide-react\";\nimport { Slot as SlotPrimitive } from \"radix-ui\";\nimport * as React from \"react\";\n\nimport { useComposedRefs } from \"@/lib/compose-refs\";\nimport { cn } from \"@/lib/utils\";\nimport { VisuallyHiddenInput } from \"@/registry/bases/radix/components/visually-hidden-input\";\nimport { useAsRef } from \"@/registry/bases/radix/hooks/use-as-ref\";\nimport { useIsomorphicLayoutEffect } from \"@/registry/bases/radix/hooks/use-isomorphic-layout-effect\";\nimport { useLazyRef } from \"@/registry/bases/radix/hooks/use-lazy-ref\";\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n} from \"@/registry/bases/radix/ui/command\";\nimport { Input } from \"@/registry/bases/radix/ui/input\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/registry/bases/radix/ui/popover\";\n\nconst ROOT_NAME = \"PhoneInput\";\nconst COUNTRY_SELECT_NAME = \"PhoneInputCountrySelect\";\nconst FIELD_NAME = \"PhoneInputField\";\n\n/**\n * @see https://github.com/mukeshsoni/country-telephone-data/blob/master/country_telephone_data.js\n * @format [iso2, dialCode]\n */\nconst COUNTRY_DATA: [string, string][] = [\n  [\"af\", \"93\"],\n  [\"ax\", \"358\"],\n  [\"al\", \"355\"],\n  [\"dz\", \"213\"],\n  [\"as\", \"1684\"],\n  [\"ad\", \"376\"],\n  [\"ao\", \"244\"],\n  [\"ai\", \"1264\"],\n  [\"ag\", \"1268\"],\n  [\"ar\", \"54\"],\n  [\"am\", \"374\"],\n  [\"aw\", \"297\"],\n  [\"au\", \"61\"],\n  [\"at\", \"43\"],\n  [\"az\", \"994\"],\n  [\"bs\", \"1242\"],\n  [\"bh\", \"973\"],\n  [\"bd\", \"880\"],\n  [\"bb\", \"1246\"],\n  [\"by\", \"375\"],\n  [\"be\", \"32\"],\n  [\"bz\", \"501\"],\n  [\"bj\", \"229\"],\n  [\"bm\", \"1441\"],\n  [\"bt\", \"975\"],\n  [\"bo\", \"591\"],\n  [\"ba\", \"387\"],\n  [\"bw\", \"267\"],\n  [\"br\", \"55\"],\n  [\"io\", \"246\"],\n  [\"vg\", \"1284\"],\n  [\"bn\", \"673\"],\n  [\"bg\", \"359\"],\n  [\"bf\", \"226\"],\n  [\"bi\", \"257\"],\n  [\"kh\", \"855\"],\n  [\"cm\", \"237\"],\n  [\"ca\", \"1\"],\n  [\"cv\", \"238\"],\n  [\"bq\", \"599\"],\n  [\"ky\", \"1345\"],\n  [\"cf\", \"236\"],\n  [\"td\", \"235\"],\n  [\"cl\", \"56\"],\n  [\"cn\", \"86\"],\n  [\"co\", \"57\"],\n  [\"km\", \"269\"],\n  [\"cd\", \"243\"],\n  [\"cg\", \"242\"],\n  [\"ck\", \"682\"],\n  [\"cr\", \"506\"],\n  [\"ci\", \"225\"],\n  [\"hr\", \"385\"],\n  [\"cu\", \"53\"],\n  [\"cw\", \"599\"],\n  [\"cy\", \"357\"],\n  [\"cz\", \"420\"],\n  [\"dk\", \"45\"],\n  [\"dj\", \"253\"],\n  [\"dm\", \"1767\"],\n  [\"do\", \"1\"],\n  [\"ec\", \"593\"],\n  [\"eg\", \"20\"],\n  [\"sv\", \"503\"],\n  [\"gq\", \"240\"],\n  [\"er\", \"291\"],\n  [\"ee\", \"372\"],\n  [\"et\", \"251\"],\n  [\"fk\", \"500\"],\n  [\"fo\", \"298\"],\n  [\"fj\", \"679\"],\n  [\"fi\", \"358\"],\n  [\"fr\", \"33\"],\n  [\"gf\", \"594\"],\n  [\"pf\", \"689\"],\n  [\"ga\", \"241\"],\n  [\"gm\", \"220\"],\n  [\"ge\", \"995\"],\n  [\"de\", \"49\"],\n  [\"gh\", \"233\"],\n  [\"gi\", \"350\"],\n  [\"gr\", \"30\"],\n  [\"gl\", \"299\"],\n  [\"gd\", \"1473\"],\n  [\"gp\", \"590\"],\n  [\"gu\", \"1671\"],\n  [\"gt\", \"502\"],\n  [\"gg\", \"44\"],\n  [\"gn\", \"224\"],\n  [\"gw\", \"245\"],\n  [\"gy\", \"592\"],\n  [\"ht\", \"509\"],\n  [\"hn\", \"504\"],\n  [\"hk\", \"852\"],\n  [\"hu\", \"36\"],\n  [\"is\", \"354\"],\n  [\"in\", \"91\"],\n  [\"id\", \"62\"],\n  [\"ir\", \"98\"],\n  [\"iq\", \"964\"],\n  [\"ie\", \"353\"],\n  [\"im\", \"44\"],\n  [\"il\", \"972\"],\n  [\"it\", \"39\"],\n  [\"jm\", \"1876\"],\n  [\"jp\", \"81\"],\n  [\"je\", \"44\"],\n  [\"jo\", \"962\"],\n  [\"kz\", \"7\"],\n  [\"ke\", \"254\"],\n  [\"ki\", \"686\"],\n  [\"xk\", \"383\"],\n  [\"kw\", \"965\"],\n  [\"kg\", \"996\"],\n  [\"la\", \"856\"],\n  [\"lv\", \"371\"],\n  [\"lb\", \"961\"],\n  [\"ls\", \"266\"],\n  [\"lr\", \"231\"],\n  [\"ly\", \"218\"],\n  [\"li\", \"423\"],\n  [\"lt\", \"370\"],\n  [\"lu\", \"352\"],\n  [\"mo\", \"853\"],\n  [\"mk\", \"389\"],\n  [\"mg\", \"261\"],\n  [\"mw\", \"265\"],\n  [\"my\", \"60\"],\n  [\"mv\", \"960\"],\n  [\"ml\", \"223\"],\n  [\"mt\", \"356\"],\n  [\"mh\", \"692\"],\n  [\"mq\", \"596\"],\n  [\"mr\", \"222\"],\n  [\"mu\", \"230\"],\n  [\"mx\", \"52\"],\n  [\"fm\", \"691\"],\n  [\"md\", \"373\"],\n  [\"mc\", \"377\"],\n  [\"mn\", \"976\"],\n  [\"me\", \"382\"],\n  [\"ms\", \"1664\"],\n  [\"ma\", \"212\"],\n  [\"mz\", \"258\"],\n  [\"mm\", \"95\"],\n  [\"na\", \"264\"],\n  [\"nr\", \"674\"],\n  [\"np\", \"977\"],\n  [\"nl\", \"31\"],\n  [\"nc\", \"687\"],\n  [\"nz\", \"64\"],\n  [\"ni\", \"505\"],\n  [\"ne\", \"227\"],\n  [\"ng\", \"234\"],\n  [\"nu\", \"683\"],\n  [\"nf\", \"672\"],\n  [\"kp\", \"850\"],\n  [\"mp\", \"1670\"],\n  [\"no\", \"47\"],\n  [\"om\", \"968\"],\n  [\"pk\", \"92\"],\n  [\"pw\", \"680\"],\n  [\"ps\", \"970\"],\n  [\"pa\", \"507\"],\n  [\"pg\", \"675\"],\n  [\"py\", \"595\"],\n  [\"pe\", \"51\"],\n  [\"ph\", \"63\"],\n  [\"pl\", \"48\"],\n  [\"pt\", \"351\"],\n  [\"pr\", \"1\"],\n  [\"qa\", \"974\"],\n  [\"re\", \"262\"],\n  [\"ro\", \"40\"],\n  [\"ru\", \"7\"],\n  [\"rw\", \"250\"],\n  [\"bl\", \"590\"],\n  [\"sh\", \"290\"],\n  [\"kn\", \"1869\"],\n  [\"lc\", \"1758\"],\n  [\"mf\", \"590\"],\n  [\"pm\", \"508\"],\n  [\"vc\", \"1784\"],\n  [\"ws\", \"685\"],\n  [\"sm\", \"378\"],\n  [\"st\", \"239\"],\n  [\"sa\", \"966\"],\n  [\"sn\", \"221\"],\n  [\"rs\", \"381\"],\n  [\"sc\", \"248\"],\n  [\"sl\", \"232\"],\n  [\"sg\", \"65\"],\n  [\"sx\", \"1721\"],\n  [\"sk\", \"421\"],\n  [\"si\", \"386\"],\n  [\"sb\", \"677\"],\n  [\"so\", \"252\"],\n  [\"za\", \"27\"],\n  [\"kr\", \"82\"],\n  [\"ss\", \"211\"],\n  [\"es\", \"34\"],\n  [\"lk\", \"94\"],\n  [\"sd\", \"249\"],\n  [\"sr\", \"597\"],\n  [\"sz\", \"268\"],\n  [\"se\", \"46\"],\n  [\"ch\", \"41\"],\n  [\"sy\", \"963\"],\n  [\"tw\", \"886\"],\n  [\"tj\", \"992\"],\n  [\"tz\", \"255\"],\n  [\"th\", \"66\"],\n  [\"tl\", \"670\"],\n  [\"tg\", \"228\"],\n  [\"tk\", \"690\"],\n  [\"to\", \"676\"],\n  [\"tt\", \"1868\"],\n  [\"tn\", \"216\"],\n  [\"tr\", \"90\"],\n  [\"tm\", \"993\"],\n  [\"tc\", \"1649\"],\n  [\"tv\", \"688\"],\n  [\"vi\", \"1340\"],\n  [\"ug\", \"256\"],\n  [\"ua\", \"380\"],\n  [\"ae\", \"971\"],\n  [\"gb\", \"44\"],\n  [\"us\", \"1\"],\n  [\"uy\", \"598\"],\n  [\"uz\", \"998\"],\n  [\"vu\", \"678\"],\n  [\"va\", \"39\"],\n  [\"ve\", \"58\"],\n  [\"vn\", \"84\"],\n  [\"wf\", \"681\"],\n  [\"eh\", \"212\"],\n  [\"ye\", \"967\"],\n  [\"zm\", \"260\"],\n  [\"zw\", \"263\"],\n];\n\ninterface Country {\n  code: string;\n  name: string;\n  dialCode: string;\n  flag?: string;\n}\n\nfunction getCountryName(countryCode: string, locale = \"en\"): string {\n  try {\n    const regionNames = new Intl.DisplayNames([locale], { type: \"region\" });\n    return regionNames.of(countryCode) ?? countryCode;\n  } catch {\n    return countryCode;\n  }\n}\n\nfunction getFlagEmoji(countryCode: string): string {\n  const codePoints = countryCode\n    .toUpperCase()\n    .split(\"\")\n    .map((char) => 127397 + char.charCodeAt(0));\n  return String.fromCodePoint(...codePoints);\n}\n\nfunction getCountries(): Country[] {\n  return COUNTRY_DATA.map(([iso2, dialCode]): Country => {\n    const code = iso2.toUpperCase();\n    return {\n      code,\n      name: getCountryName(code),\n      dialCode: `+${dialCode}`,\n      flag: getFlagEmoji(code),\n    };\n  }).sort((a, b) => a.name.localeCompare(b.name));\n}\n\nfunction detectCountryFromNumber(\n  value: string,\n  countries: Country[],\n): Country | undefined {\n  if (!value?.startsWith(\"+\")) return undefined;\n\n  const digits = value.slice(1).replace(/\\D/g, \"\");\n  if (!digits) return undefined;\n\n  const sorted = [...countries].sort(\n    (a, b) => b.dialCode.length - a.dialCode.length,\n  );\n\n  const matches: Country[] = [];\n  for (const country of sorted) {\n    const dialCode = country.dialCode.slice(1);\n    if (digits.startsWith(dialCode)) {\n      matches.push(country);\n    }\n  }\n\n  if (matches.length === 0) return undefined;\n\n  if (matches.length > 1 && matches[0]?.dialCode === \"+1\") {\n    const usCountry = matches.find((c) => c.code === \"US\");\n    if (usCountry) return usCountry;\n  }\n\n  return matches[0];\n}\n\nfunction formatPhoneNumber(value: string, countries: Country[]): string {\n  if (!value) return \"\";\n\n  const normalized = value.startsWith(\"+\") ? value : `+${value}`;\n\n  const digits = normalized.slice(1).replace(/\\D/g, \"\");\n  if (!digits) return \"+\";\n\n  const detected = detectCountryFromNumber(`+${digits}`, countries);\n  const dialCodeLength = detected\n    ? detected.dialCode.slice(1).length\n    : Math.min(digits.length, 3);\n\n  const countryCode = digits.slice(0, dialCodeLength);\n  const rest = digits.slice(dialCodeLength);\n\n  let formatted = `+${countryCode}`;\n\n  if (rest) {\n    formatted += \" \";\n    for (let i = 0; i < rest.length; i++) {\n      if (i > 0 && i % 3 === 0) {\n        formatted += \" \";\n      }\n      formatted += rest[i];\n    }\n  }\n\n  return formatted;\n}\n\ntype RootElement = React.ComponentRef<typeof PhoneInput>;\n\ninterface StoreState {\n  value: string;\n  country: string;\n  open: boolean;\n  startsWithPlus: boolean;\n}\n\ninterface Store {\n  subscribe: (callback: () => void) => () => void;\n  getState: () => StoreState;\n  setState: <K extends keyof StoreState>(key: K, value: StoreState[K]) => void;\n  notify: () => void;\n}\n\nconst StoreContext = React.createContext<Store | null>(null);\n\nfunction useStoreContext(consumerName: string) {\n  const context = React.useContext(StoreContext);\n  if (!context) {\n    throw new Error(`\\`${consumerName}\\` must be used within \\`${ROOT_NAME}\\``);\n  }\n  return context;\n}\n\nfunction useStore<T>(\n  selector: (state: StoreState) => T,\n  ogStore?: Store | null,\n): T {\n  const contextStore = React.useContext(StoreContext);\n\n  const store = ogStore ?? contextStore;\n\n  if (!store) {\n    throw new Error(`\\`useStore\\` must be used within \\`${ROOT_NAME}\\``);\n  }\n\n  const getSnapshot = React.useCallback(\n    () => selector(store.getState()),\n    [store, selector],\n  );\n\n  return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n\ninterface PhoneInputContextValue {\n  rootId: string;\n  countries: Country[];\n  placeholder: string;\n  disabled?: boolean;\n  readOnly?: boolean;\n  required?: boolean;\n  invalid?: boolean;\n  showFlag: boolean;\n  inputRef: React.RefObject<HTMLInputElement | null>;\n}\n\nconst PhoneInputContext = React.createContext<PhoneInputContextValue | null>(\n  null,\n);\n\nfunction usePhoneInputContext(consumerName: string) {\n  const context = React.useContext(PhoneInputContext);\n  if (!context) {\n    throw new Error(`\\`${consumerName}\\` must be used within \\`${ROOT_NAME}\\``);\n  }\n  return context;\n}\n\ninterface PhoneInputProps extends React.ComponentProps<\"div\"> {\n  defaultValue?: string;\n  value?: string;\n  onValueChange?: (value: string) => void;\n  defaultCountry?: string;\n  country?: string;\n  onCountryChange?: (country: string) => void;\n  countries?: Country[];\n  name?: string;\n  placeholder?: string;\n  asChild?: boolean;\n  disabled?: boolean;\n  readOnly?: boolean;\n  required?: boolean;\n  invalid?: boolean;\n  showFlag?: boolean;\n}\n\nfunction PhoneInput(props: PhoneInputProps) {\n  const {\n    value: valueProp,\n    defaultValue,\n    defaultCountry,\n    country: countryProp,\n    onValueChange,\n    onCountryChange,\n    countries = getCountries(),\n    name,\n    placeholder = \"Enter phone number\",\n    asChild,\n    disabled,\n    required,\n    readOnly,\n    invalid,\n    showFlag = true,\n    className,\n    id,\n    ref,\n    ...rootProps\n  } = props;\n\n  const instanceId = React.useId();\n  const rootId = id ?? instanceId;\n\n  const inputRef = React.useRef<HTMLInputElement>(null);\n\n  const [formTrigger, setFormTrigger] = React.useState<RootElement | null>(\n    null,\n  );\n  const composedRef = useComposedRefs(ref, (node) => setFormTrigger(node));\n  const isFormControl = formTrigger ? !!formTrigger.closest(\"form\") : true;\n\n  const listenersRef = useLazyRef(() => new Set<() => void>());\n  const stateRef = useLazyRef<StoreState>(() => {\n    const initialValue = valueProp ?? defaultValue ?? \"\";\n    const initialCountry = countryProp ?? defaultCountry ?? \"\";\n\n    return {\n      value: initialValue,\n      country: initialCountry,\n      open: false,\n      startsWithPlus: initialValue.startsWith(\"+\"),\n    };\n  });\n\n  const propsRef = useAsRef({\n    onValueChange,\n    onCountryChange,\n  });\n\n  const store = React.useMemo<Store>(() => {\n    return {\n      subscribe: (cb) => {\n        listenersRef.current.add(cb);\n        return () => listenersRef.current.delete(cb);\n      },\n      getState: () => stateRef.current,\n      setState: (key, value) => {\n        if (Object.is(stateRef.current[key], value)) return;\n\n        if (key === \"value\" && typeof value === \"string\") {\n          stateRef.current.value = value;\n          propsRef.current.onValueChange?.(value);\n        } else if (key === \"country\" && typeof value === \"string\") {\n          stateRef.current.country = value;\n          propsRef.current.onCountryChange?.(value);\n        } else {\n          stateRef.current[key] = value;\n        }\n\n        store.notify();\n      },\n      notify: () => {\n        for (const cb of listenersRef.current) {\n          cb();\n        }\n      },\n    };\n  }, [listenersRef, stateRef, propsRef]);\n\n  const value = useStore((state) => state.value, store);\n  const country = useStore((state) => state.country, store);\n\n  useIsomorphicLayoutEffect(() => {\n    if (valueProp !== undefined) {\n      store.setState(\"value\", valueProp);\n    }\n  }, [valueProp]);\n\n  useIsomorphicLayoutEffect(() => {\n    if (countryProp !== undefined) {\n      store.setState(\"country\", countryProp);\n    }\n  }, [countryProp]);\n\n  const startsWithPlus = useStore((state) => state.startsWithPlus, store);\n\n  React.useEffect(() => {\n    if (!value) return;\n\n    const digits = value.slice(1).replace(/\\D/g, \"\");\n    const shouldDetect = startsWithPlus || digits.length >= 10;\n\n    if (!shouldDetect) return;\n\n    const detected = detectCountryFromNumber(value, countries);\n    if (detected && detected.code !== country) {\n      store.setState(\"country\", detected.code);\n    }\n  }, [value, countries, country, store, startsWithPlus]);\n\n  const contextValue = React.useMemo<PhoneInputContextValue>(\n    () => ({\n      rootId,\n      countries,\n      placeholder,\n      disabled,\n      readOnly,\n      required,\n      invalid,\n      showFlag,\n      inputRef,\n    }),\n    [\n      rootId,\n      countries,\n      placeholder,\n      disabled,\n      required,\n      readOnly,\n      invalid,\n      showFlag,\n    ],\n  );\n\n  const RootPrimitive = asChild ? SlotPrimitive.Slot : \"div\";\n\n  return (\n    <StoreContext.Provider value={store}>\n      <PhoneInputContext.Provider value={contextValue}>\n        <RootPrimitive\n          role=\"group\"\n          data-slot=\"phone-input\"\n          data-disabled={disabled ? \"\" : undefined}\n          data-invalid={invalid ? \"\" : undefined}\n          data-readonly={readOnly ? \"\" : undefined}\n          id={rootId}\n          {...rootProps}\n          ref={composedRef}\n          className={cn(\n            \"relative flex h-10 w-full items-center rounded-md border border-input bg-background transition-colors has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-[3px] has-[[data-slot][aria-invalid=true]]:ring-destructive/20 data-disabled:cursor-not-allowed data-disabled:opacity-50 dark:bg-input/30 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40\",\n            className,\n          )}\n        />\n        {isFormControl && (\n          <VisuallyHiddenInput\n            type=\"hidden\"\n            control={formTrigger}\n            name={name}\n            value={value}\n            disabled={disabled}\n            readOnly={readOnly}\n            required={required}\n          />\n        )}\n      </PhoneInputContext.Provider>\n    </StoreContext.Provider>\n  );\n}\n\ninterface PhoneInputCountrySelectProps\n  extends\n    React.ComponentProps<typeof Popover>,\n    Pick<\n      React.ComponentProps<typeof PopoverTrigger>,\n      \"disabled\" | \"className\"\n    > {}\n\nfunction PhoneInputCountrySelect(props: PhoneInputCountrySelectProps) {\n  const {\n    disabled: disabledProp,\n    className,\n    children,\n    onOpenChange: onOpenChangeProp,\n    ...popoverProps\n  } = props;\n\n  const { countries, inputRef, disabled, showFlag } =\n    usePhoneInputContext(COUNTRY_SELECT_NAME);\n  const store = useStoreContext(COUNTRY_SELECT_NAME);\n  const country = useStore((state) => state.country);\n  const open = useStore((state) => state.open);\n  const onOpenChangeRef = useAsRef(onOpenChangeProp);\n\n  const isDisabled = disabledProp || disabled;\n\n  const countryContext = countries.find((c) => c.code === country);\n\n  const onOpenChange = React.useCallback(\n    (open: boolean) => {\n      store.setState(\"open\", open);\n      onOpenChangeRef.current?.(open);\n    },\n    [store, onOpenChangeRef],\n  );\n\n  return (\n    <Popover open={open} onOpenChange={onOpenChange} {...popoverProps}>\n      <PopoverTrigger\n        data-slot=\"phone-input-country-select\"\n        disabled={isDisabled}\n        className={cn(\n          \"flex h-full shrink-0 items-center gap-2 rounded-l-md border-r border-input bg-transparent px-3 text-sm transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:z-10 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40\",\n          className,\n        )}\n      >\n        {!countryContext ? (\n          <div className=\"h-4 w-6 rounded bg-muted/50\" />\n        ) : (\n          showFlag &&\n          countryContext.flag && (\n            <div className=\"w-6 text-lg leading-none\">\n              {countryContext.flag}\n            </div>\n          )\n        )}\n        <ChevronDown className=\"size-4 opacity-50\" />\n      </PopoverTrigger>\n      <PopoverContent className=\"w-[300px] p-0\" align=\"start\">\n        <Command>\n          <CommandInput placeholder=\"Search country...\" />\n          <CommandList>\n            <CommandEmpty>No country found.</CommandEmpty>\n            <CommandGroup>\n              {countries.map((c) => (\n                <CommandItem\n                  key={c.code}\n                  value={`${c.name} ${c.dialCode} ${c.code}`}\n                  onSelect={() => {\n                    store.setState(\"country\", c.code);\n                    store.setState(\"open\", false);\n                    requestAnimationFrame(() => {\n                      inputRef.current?.focus();\n                    });\n                  }}\n                >\n                  {showFlag && c.flag && (\n                    <span className=\"text-lg\">{c.flag}</span>\n                  )}\n                  <span className=\"flex-1\">{c.name}</span>\n                  <span className=\"text-muted-foreground\">{c.dialCode}</span>\n                  <Check\n                    className={cn(\n                      \"size-4\",\n                      country === c.code ? \"opacity-100\" : \"opacity-0\",\n                    )}\n                  />\n                </CommandItem>\n              ))}\n            </CommandGroup>\n          </CommandList>\n        </Command>\n      </PopoverContent>\n    </Popover>\n  );\n}\n\nfunction PhoneInputField(props: React.ComponentProps<\"input\">) {\n  const {\n    onChange: onChangeProp,\n    className,\n    disabled: disabledProp,\n    readOnly: readOnlyProp,\n    required: requiredProp,\n    ref,\n    ...inputProps\n  } = props;\n\n  const {\n    inputRef,\n    disabled,\n    invalid,\n    readOnly,\n    required,\n    placeholder,\n    countries,\n  } = usePhoneInputContext(FIELD_NAME);\n  const store = useStoreContext(FIELD_NAME);\n  const value = useStore((state) => state.value);\n\n  const composedRef = useComposedRefs(ref, inputRef);\n\n  const onChangeRef = useAsRef(onChangeProp);\n\n  const isDisabled = disabledProp || disabled;\n  const isReadOnly = readOnlyProp || readOnly;\n  const isRequired = requiredProp || required;\n\n  const onChange = React.useCallback(\n    (event: React.ChangeEvent<HTMLInputElement>) => {\n      if (isDisabled || isReadOnly) return;\n\n      onChangeRef.current?.(event);\n      if (event.defaultPrevented) return;\n\n      const inputValue = event.target.value;\n\n      const startsWithPlus = inputValue.startsWith(\"+\");\n      const digits = inputValue.replace(/\\D/g, \"\");\n      const newValue = digits ? `+${digits}` : startsWithPlus ? \"+\" : \"\";\n      store.setState(\"startsWithPlus\", startsWithPlus);\n      store.setState(\"value\", newValue);\n    },\n    [store, onChangeRef, isDisabled, isReadOnly],\n  );\n\n  const displayValue = React.useMemo(() => {\n    return formatPhoneNumber(value, countries);\n  }, [value, countries]);\n\n  return (\n    <Input\n      type=\"tel\"\n      inputMode=\"tel\"\n      aria-required={isRequired}\n      aria-invalid={invalid}\n      data-slot=\"phone-input-field\"\n      disabled={isDisabled}\n      readOnly={isReadOnly}\n      required={isRequired}\n      {...inputProps}\n      ref={composedRef}\n      className={cn(\n        \"h-full flex-1 rounded-l-none rounded-r-md border-0 bg-transparent shadow-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:bg-transparent aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 dark:bg-transparent dark:disabled:bg-transparent dark:aria-invalid:ring-destructive/40\",\n        className,\n      )}\n      placeholder={placeholder}\n      value={displayValue}\n      onChange={onChange}\n    />\n  );\n}\n\nexport {\n  PhoneInput,\n  PhoneInputCountrySelect,\n  PhoneInputField,\n  type PhoneInputProps,\n  useStore as usePhoneInput,\n};\n",
      "target": ""
    },
    {
      "path": "components/visually-hidden-input.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\ntype InputValue = string[] | string;\n\ninterface VisuallyHiddenInputProps<T = InputValue> extends Omit<\n  React.InputHTMLAttributes<HTMLInputElement>,\n  \"value\" | \"checked\" | \"onReset\"\n> {\n  value?: T;\n  checked?: boolean;\n  control: HTMLElement | null;\n  bubbles?: boolean;\n}\n\nfunction VisuallyHiddenInput<T = InputValue>(\n  props: VisuallyHiddenInputProps<T>,\n) {\n  const {\n    control,\n    value,\n    checked,\n    bubbles = true,\n    type = \"hidden\",\n    style,\n    ...inputProps\n  } = props;\n\n  const isCheckInput = React.useMemo(\n    () => type === \"checkbox\" || type === \"radio\" || type === \"switch\",\n    [type],\n  );\n  const inputRef = React.useRef<HTMLInputElement>(null);\n\n  const prevValueRef = React.useRef<{\n    value: T | boolean | undefined;\n    previous: T | boolean | undefined;\n  }>({\n    value: isCheckInput ? checked : value,\n    previous: isCheckInput ? checked : value,\n  });\n\n  const prevValue = React.useMemo(() => {\n    const currentValue = isCheckInput ? checked : value;\n    if (prevValueRef.current.value !== currentValue) {\n      prevValueRef.current.previous = prevValueRef.current.value;\n      prevValueRef.current.value = currentValue;\n    }\n    return prevValueRef.current.previous;\n  }, [isCheckInput, value, checked]);\n\n  const [controlSize, setControlSize] = React.useState<{\n    width?: number;\n    height?: number;\n  }>({});\n\n  React.useLayoutEffect(() => {\n    if (!control) {\n      setControlSize({});\n      return;\n    }\n\n    setControlSize({\n      width: control.offsetWidth,\n      height: control.offsetHeight,\n    });\n\n    if (typeof window === \"undefined\") return;\n\n    const resizeObserver = new ResizeObserver((entries) => {\n      if (!Array.isArray(entries) || !entries.length) return;\n\n      const entry = entries[0];\n      if (!entry) return;\n\n      let width: number;\n      let height: number;\n\n      if (\"borderBoxSize\" in entry) {\n        const borderSizeEntry = entry.borderBoxSize;\n        const borderSize = Array.isArray(borderSizeEntry)\n          ? borderSizeEntry[0]\n          : borderSizeEntry;\n        width = borderSize.inlineSize;\n        height = borderSize.blockSize;\n      } else {\n        width = control.offsetWidth;\n        height = control.offsetHeight;\n      }\n\n      setControlSize({ width, height });\n    });\n\n    resizeObserver.observe(control, { box: \"border-box\" });\n    return () => {\n      resizeObserver.disconnect();\n    };\n  }, [control]);\n\n  React.useEffect(() => {\n    const input = inputRef.current;\n    if (!input) return;\n\n    const inputProto = window.HTMLInputElement.prototype;\n    const propertyKey = isCheckInput ? \"checked\" : \"value\";\n    const eventType = isCheckInput ? \"click\" : \"input\";\n    const currentValue = isCheckInput ? checked : value;\n\n    const serializedCurrentValue = isCheckInput\n      ? checked\n      : typeof value === \"object\" && value !== null\n        ? JSON.stringify(value)\n        : value;\n\n    const descriptor = Object.getOwnPropertyDescriptor(inputProto, propertyKey);\n\n    const setter = descriptor?.set;\n\n    if (prevValue !== currentValue && setter) {\n      const event = new Event(eventType, { bubbles });\n      setter.call(input, serializedCurrentValue);\n      input.dispatchEvent(event);\n    }\n  }, [prevValue, value, checked, bubbles, isCheckInput]);\n\n  const composedStyle = React.useMemo<React.CSSProperties>(() => {\n    return {\n      ...style,\n      ...(controlSize.width !== undefined && controlSize.height !== undefined\n        ? controlSize\n        : {}),\n      border: 0,\n      clip: \"rect(0 0 0 0)\",\n      clipPath: \"inset(50%)\",\n      height: \"1px\",\n      margin: \"-1px\",\n      overflow: \"hidden\",\n      padding: 0,\n      position: \"absolute\",\n      whiteSpace: \"nowrap\",\n      width: \"1px\",\n    };\n  }, [style, controlSize]);\n\n  return (\n    <input\n      type={type}\n      {...inputProps}\n      ref={inputRef}\n      aria-hidden={isCheckInput}\n      tabIndex={-1}\n      defaultChecked={isCheckInput ? checked : undefined}\n      style={composedStyle}\n    />\n  );\n}\n\nexport { VisuallyHiddenInput };\n",
      "target": ""
    },
    {
      "path": "lib/compose-refs.ts",
      "type": "registry:lib",
      "content": "/**\n * @see https://github.com/radix-ui/primitives/blob/main/packages/react/compose-refs/src/compose-refs.tsx\n */\n\nimport * as React from \"react\";\n\ntype PossibleRef<T> = React.Ref<T> | undefined;\n\n/**\n * Set a given ref to a given value\n * This utility takes care of different types of refs: callback refs and RefObject(s)\n */\nfunction setRef<T>(ref: PossibleRef<T>, value: T) {\n  if (typeof ref === \"function\") {\n    return ref(value);\n  }\n\n  if (ref !== null && ref !== undefined) {\n    ref.current = value;\n  }\n}\n\n/**\n * A utility to compose multiple refs together\n * Accepts callback refs and RefObject(s)\n */\nfunction composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n  return (node) => {\n    let hasCleanup = false;\n    const cleanups = refs.map((ref) => {\n      const cleanup = setRef(ref, node);\n      if (!hasCleanup && typeof cleanup === \"function\") {\n        hasCleanup = true;\n      }\n      return cleanup;\n    });\n\n    // React <19 will log an error to the console if a callback ref returns a\n    // value. We don't use ref cleanups internally so this will only happen if a\n    // user's ref callback returns a value, which we only expect if they are\n    // using the cleanup functionality added in React 19.\n    if (hasCleanup) {\n      return () => {\n        for (let i = 0; i < cleanups.length; i++) {\n          const cleanup = cleanups[i];\n          if (typeof cleanup === \"function\") {\n            cleanup();\n          } else {\n            setRef(refs[i], null);\n          }\n        }\n      };\n    }\n  };\n}\n\n/**\n * A custom hook that composes multiple refs\n * Accepts callback refs and RefObject(s)\n */\nfunction useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n  // oxlint-disable-next-line react/exhaustive-deps -- we want to memoize by all values\n  return React.useCallback(composeRefs(...refs), refs);\n}\n\nexport { composeRefs, useComposedRefs };\n",
      "target": ""
    }
  ]
}