{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "phone-input",
  "title": "Phone Input",
  "description": "International phone input primitive.",
  "dependencies": [
    "react-international-phone"
  ],
  "registryDependencies": [
    "@circle-ui/utils"
  ],
  "files": [
    {
      "path": "registry/berlin/circle-ui/form.tsx",
      "content": "// Generated from packages/ui/src/components/form.tsx\n\"use client\";\n\nimport * as React from \"react\";\nimport { PhoneInput as BasePhoneInput } from \"react-international-phone\";\nimport \"react-international-phone/style.css\";\n\nimport { cn } from \"@/registry/berlin/lib/utils\";\n\ntype FieldElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n\nexport interface FormRenderProps {\n  values: Record<string, string>;\n  errors: Record<string, string>;\n  handleChange: (event: React.ChangeEvent<FieldElement>) => void;\n  handleBlur: (event: React.FocusEvent<FieldElement>) => void;\n  validateValues: () => Record<string, string>;\n  submitForm: () => boolean;\n}\n\nexport interface FormProps {\n  children: (formProps: FormRenderProps) => React.ReactNode;\n  onSubmit?: (data: Record<string, string>) => void;\n  initialValues?: Record<string, string>;\n  validate?: (values: Record<string, string>) => Record<string, string>;\n  className?: string;\n}\n\ntype BaseFieldProps<T> = {\n  className?: string;\n  disabled?: boolean;\n  error?: string;\n  label?: string;\n  name: string;\n  onBlur?: React.FocusEventHandler<T>;\n  onChange?: React.ChangeEventHandler<T>;\n  placeholder?: string;\n  value?: string;\n  defaultValue?: string;\n};\n\nexport interface TextBoxProps\n  extends\n    Omit<\n      React.InputHTMLAttributes<HTMLInputElement>,\n      | \"className\"\n      | \"defaultValue\"\n      | \"disabled\"\n      | \"name\"\n      | \"onBlur\"\n      | \"onChange\"\n      | \"placeholder\"\n      | \"size\"\n      | \"value\"\n    >,\n    BaseFieldProps<HTMLInputElement> {}\nexport interface TextAreaProps\n  extends\n    Omit<\n      React.TextareaHTMLAttributes<HTMLTextAreaElement>,\n      | \"className\"\n      | \"defaultValue\"\n      | \"disabled\"\n      | \"name\"\n      | \"onBlur\"\n      | \"onChange\"\n      | \"placeholder\"\n      | \"value\"\n    >,\n    BaseFieldProps<HTMLTextAreaElement> {\n  resize?: \"none\" | \"both\" | \"horizontal\" | \"vertical\";\n}\nexport interface DropdownOption {\n  id: string;\n  title: string;\n}\nexport interface DropdownProps\n  extends\n    Omit<\n      React.SelectHTMLAttributes<HTMLSelectElement>,\n      | \"className\"\n      | \"defaultValue\"\n      | \"disabled\"\n      | \"name\"\n      | \"onBlur\"\n      | \"onChange\"\n      | \"placeholder\"\n      | \"value\"\n    >,\n    BaseFieldProps<HTMLSelectElement> {\n  options?: DropdownOption[] | Record<string, string>;\n}\n\nexport interface PhoneInputProps\n  extends Omit<\n    React.ComponentProps<typeof BasePhoneInput>,\n    \"inputClassName\" | \"inputStyle\" | \"onChange\" | \"value\"\n  > {\n  label?: string;\n  subtitle?: string;\n  error?: string;\n  name?: string;\n  value?: string;\n  onChange?: (value: string) => void;\n}\n\nexport interface SelectWithInputProps\n  extends Pick<\n    DropdownProps,\n    | \"className\"\n    | \"disabled\"\n    | \"error\"\n    | \"label\"\n    | \"name\"\n    | \"placeholder\"\n    | \"required\"\n  > {\n  options?: DropdownOption[] | Record<string, string>;\n  value?: string;\n  onChange?: (value: string) => void;\n  otherOptionValue?: string;\n}\n\nfunction FieldShell({\n  className,\n  disabled,\n  error,\n  floating,\n  focused,\n  label,\n  multiline,\n  labelTargetId,\n  children,\n}: React.PropsWithChildren<{\n  className?: string;\n  disabled?: boolean;\n  error?: string;\n  floating: boolean;\n  focused: boolean;\n  label?: string;\n  labelTargetId: string;\n  multiline?: boolean;\n}>) {\n  return (\n    <div\n      className={cn(\"relative flex w-full flex-col justify-center\", className)}\n    >\n      <div\n        className={cn(\n          \"relative rounded-[var(--radius-field)]\",\n          \"[&_textarea]:align-top [&_textarea]:resize-vertical\",\n          \"[&_.circle-field]:w-full [&_.circle-field]:rounded-[var(--radius-field)] [&_.circle-field]:border [&_.circle-field]:border-field-border [&_.circle-field]:bg-field-background [&_.circle-field]:text-[16px] [&_.circle-field]:leading-[24px] [&_.circle-field]:tracking-[-0.16px] [&_.circle-field]:text-field-foreground [&_.circle-field]:outline-none\",\n          \"[&_.circle-field]:transition-[background-color,border-color,color] [&_.circle-field]:duration-200 [&_.circle-field]:ease-[cubic-bezier(0.4,0,0.2,1)]\",\n          \"[&_.circle-field:focus]:border-2 [&_.circle-field:focus]:border-field-focus [&_.circle-field:focus]:shadow-none\",\n          \"[&_.circle-field:disabled]:border-field-border [&_.circle-field:disabled]:bg-field-disabled [&_.circle-field:disabled]:text-muted-foreground\",\n          \"[&_.circle-field[data-invalid=true]]:border-field-error [&_.circle-field[data-invalid=true]]:bg-field-background\",\n          \"[&_.circle-field[data-invalid=true]:focus]:border [&_.circle-field[data-invalid=true]:focus]:border-field-error [&_.circle-field[data-invalid=true]:focus]:shadow-none\",\n        )}\n      >\n        {children}\n        {label ? (\n          <label\n            className={cn(\n              \"pointer-events-none absolute m-0 cursor-text bg-transparent p-0 text-field-label transition-all duration-200 ease-[cubic-bezier(0.4,0,0.2,1)]\",\n              multiline\n                ? \"top-[42px] -translate-y-1/2\"\n                : \"top-1/2 -translate-y-1/2\",\n              multiline\n                ? \"left-4 text-[16px] leading-[24px] tracking-[-0.16px]\"\n                : \"left-4 text-[16px] leading-[24px] tracking-[-0.16px]\",\n              floating &&\n                (multiline\n                  ? \"top-[12px] translate-y-0 text-[11px] leading-[24px] tracking-[0]\"\n                  : cn(\n                      \"translate-y-0 text-[11px] leading-[24px] tracking-[0]\",\n                      focused ? \"left-4 top-[10px]\" : \"left-[15px] top-[9px]\",\n                    )),\n              disabled && \"text-field-label\",\n            )}\n            htmlFor={labelTargetId}\n          >\n            {label}\n          </label>\n        ) : null}\n      </div>\n      {error ? (\n        <p className=\"mt-1 text-[12px] font-medium leading-[16px] tracking-[-0.12px] text-destructive\">\n          {error}\n        </p>\n      ) : null}\n    </div>\n  );\n}\n\nexport function TextBox({\n  name,\n  label,\n  error,\n  className,\n  value,\n  defaultValue,\n  placeholder,\n  onFocus,\n  onBlur,\n  onChange,\n  disabled,\n  ...props\n}: TextBoxProps) {\n  const [focused, setFocused] = React.useState(false);\n  const [currentValue, setCurrentValue] = React.useState(\n    (value ?? defaultValue ?? \"\") as string,\n  );\n  const floating = focused || Boolean(value ?? currentValue);\n\n  return (\n    <FieldShell\n      className={className}\n      disabled={disabled}\n      error={error}\n      floating={floating}\n      focused={focused}\n      label={label}\n      labelTargetId={name}\n    >\n      <input\n        id={name}\n        name={name}\n        value={value}\n        defaultValue={defaultValue}\n        placeholder={floating ? placeholder : \"\"}\n        disabled={disabled}\n        data-invalid={error ? \"true\" : \"false\"}\n        className={cn(\n          \"circle-field\",\n          floating\n            ? \"h-[68px] px-[16px] pb-[8px] pt-[24px]\"\n            : \"h-[68px] p-[16px]\",\n          \"placeholder:text-field-foreground disabled:placeholder:text-field-placeholder\",\n        )}\n        onFocus={(event) => {\n          setFocused(true);\n          onFocus?.(event);\n        }}\n        onBlur={(event) => {\n          setFocused(false);\n          setCurrentValue(event.target.value);\n          onBlur?.(event);\n        }}\n        onChange={(event) => {\n          setCurrentValue(event.target.value);\n          onChange?.(event);\n        }}\n        {...props}\n      />\n    </FieldShell>\n  );\n}\n\nexport function TextArea({\n  name,\n  label,\n  error,\n  className,\n  value,\n  defaultValue,\n  placeholder,\n  onFocus,\n  onBlur,\n  onChange,\n  resize = \"vertical\",\n  rows = 4,\n  disabled,\n  ...props\n}: TextAreaProps) {\n  const [focused, setFocused] = React.useState(false);\n  const [currentValue, setCurrentValue] = React.useState(\n    (value ?? defaultValue ?? \"\") as string,\n  );\n  const floating = focused || Boolean(value ?? currentValue);\n\n  return (\n    <FieldShell\n      className={className}\n      disabled={disabled}\n      error={error}\n      floating={floating}\n      focused={focused}\n      label={label}\n      labelTargetId={name}\n      multiline\n    >\n      <textarea\n        id={name}\n        name={name}\n        value={value}\n        defaultValue={defaultValue}\n        rows={rows}\n        placeholder={floating ? placeholder : \"\"}\n        disabled={disabled}\n        style={{ resize }}\n        data-invalid={error ? \"true\" : \"false\"}\n        className=\"circle-field min-h-[120px] px-[16px] pb-[8px] pt-[28px] placeholder:text-field-foreground disabled:placeholder:text-field-placeholder\"\n        onFocus={(event) => {\n          setFocused(true);\n          onFocus?.(event);\n        }}\n        onBlur={(event) => {\n          setFocused(false);\n          setCurrentValue(event.target.value);\n          onBlur?.(event);\n        }}\n        onChange={(event) => {\n          setCurrentValue(event.target.value);\n          onChange?.(event);\n        }}\n        {...props}\n      />\n    </FieldShell>\n  );\n}\n\nfunction ChevronIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      className={className}\n      width=\"16\"\n      height=\"16\"\n      viewBox=\"0 0 16 16\"\n      fill=\"none\"\n      aria-hidden=\"true\"\n    >\n      <path\n        d=\"m3 6 5 5 5-5\"\n        stroke=\"currentColor\"\n        strokeWidth=\"1.5\"\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n      />\n    </svg>\n  );\n}\n\nexport function Dropdown({\n  name,\n  label,\n  error,\n  className,\n  options,\n  placeholder = \"Select an option\",\n  value,\n  defaultValue,\n  onFocus,\n  onBlur,\n  onChange,\n  disabled,\n  ...props\n}: DropdownProps) {\n  const [focused, setFocused] = React.useState(false);\n  const [currentValue, setCurrentValue] = React.useState(\n    (value ?? defaultValue ?? \"\") as string,\n  );\n  const normalizedOptions = React.useMemo(() => {\n    if (!options) return [];\n    return Array.isArray(options)\n      ? options\n      : Object.entries(options).map(([id, title]) => ({ id, title }));\n  }, [options]);\n  const hasValue = Boolean(value ?? currentValue);\n\n  return (\n    <FieldShell\n      className={className}\n      disabled={disabled}\n      error={error}\n      floating={true}\n      focused={focused}\n      label={label}\n      labelTargetId={name}\n    >\n      <div className=\"relative\">\n        <select\n          id={name}\n          name={name}\n          value={value}\n          defaultValue={defaultValue}\n          disabled={disabled}\n          data-invalid={error ? \"true\" : \"false\"}\n          className={cn(\n            \"circle-field h-[68px] appearance-none pb-[8px] pl-[16px] pr-[40px] pt-[24px]\",\n            hasValue ? \"text-field-foreground\" : \"text-field-placeholder\",\n          )}\n          onFocus={(event) => {\n            setFocused(true);\n            onFocus?.(event);\n          }}\n          onBlur={(event) => {\n            setFocused(false);\n            setCurrentValue(event.target.value);\n            onBlur?.(event);\n          }}\n          onChange={(event) => {\n            setCurrentValue(event.target.value);\n            onChange?.(event);\n          }}\n          {...props}\n        >\n          <option value=\"\" disabled>\n            {placeholder}\n          </option>\n          {normalizedOptions.map((option) => (\n            <option key={option.id} value={option.id}>\n              {option.title}\n            </option>\n          ))}\n        </select>\n        <ChevronIcon className=\"pointer-events-none absolute right-[15px] top-1/2 h-4 w-4 -translate-y-1/2 text-field-chevron\" />\n      </div>\n    </FieldShell>\n  );\n}\n\nexport function PhoneInput({\n  className,\n  defaultCountry = \"de\",\n  disabled,\n  error,\n  label,\n  name = \"phone\",\n  onChange,\n  placeholder,\n  subtitle,\n  value,\n  ...props\n}: PhoneInputProps) {\n  return (\n    <div className={cn(\"flex w-full flex-col gap-2\", className)}>\n      {label ? (\n        <div className=\"space-y-0.5\">\n          <label\n            className=\"block text-sm font-medium leading-6 text-foreground\"\n            htmlFor={name}\n          >\n            {label}\n          </label>\n          {subtitle ? (\n            <p className=\"text-sm leading-5 text-muted-foreground\">{subtitle}</p>\n          ) : null}\n        </div>\n      ) : null}\n      <BasePhoneInput\n        countrySelectorStyleProps={{ style: { height: \"68px\" } }}\n        defaultCountry={defaultCountry}\n        disableDialCodeAndPrefix\n        disabled={disabled}\n        inputClassName={cn(error ? \"circle-phone-error\" : undefined)}\n        inputStyle={{\n          background: \"var(--field-background)\",\n          border: `1px solid ${error ? \"var(--destructive)\" : \"var(--field-border)\"}`,\n          borderRadius: \"var(--radius-field)\",\n          color: \"var(--field-foreground)\",\n          fontSize: \"16px\",\n          lineHeight: \"24px\",\n          minHeight: \"68px\",\n          padding: \"20px 16px\",\n          width: \"100%\",\n        }}\n        name={name}\n        onChange={(nextValue: string) => onChange?.(nextValue)}\n        placeholder={placeholder}\n        showDisabledDialCodeAndPrefix\n        value={value}\n        {...props}\n      />\n      {error ? (\n        <p className=\"text-[12px] font-medium leading-4 text-destructive\">\n          {error}\n        </p>\n      ) : null}\n    </div>\n  );\n}\n\nexport function SelectWithInput({\n  label,\n  onChange,\n  options,\n  otherOptionValue = \"other\",\n  placeholder,\n  value,\n  ...props\n}: SelectWithInputProps) {\n  const [showInput, setShowInput] = React.useState(false);\n\n  React.useEffect(() => {\n    if (!value) {\n      setShowInput(false);\n      return;\n    }\n\n    if (Array.isArray(options)) {\n      setShowInput(!options.some((option) => option.id === value));\n      return;\n    }\n\n    setShowInput(!(value in (options ?? {})));\n  }, [options, value]);\n\n  if (showInput) {\n    return (\n      <TextBox\n        className={props.className}\n        disabled={props.disabled}\n        error={props.error}\n        label={label}\n        name={props.name}\n        onChange={(event) => onChange?.(event.target.value)}\n        placeholder={placeholder}\n        required={props.required}\n        value={value}\n      />\n    );\n  }\n\n  return (\n    <Dropdown\n      className={props.className}\n      disabled={props.disabled}\n      error={props.error}\n      label={label}\n      name={props.name}\n      onChange={(event) => {\n        const nextValue = event.target.value;\n        if (nextValue === otherOptionValue) {\n          onChange?.(\"\");\n          setShowInput(true);\n          return;\n        }\n        onChange?.(nextValue);\n      }}\n      options={options}\n      placeholder={placeholder}\n      required={props.required}\n      value={value}\n    />\n  );\n}\n\nexport function Form({\n  children,\n  onSubmit = () => undefined,\n  initialValues = {},\n  validate = () => ({}),\n  className,\n}: FormProps) {\n  const [values, setValues] =\n    React.useState<Record<string, string>>(initialValues);\n  const [errors, setErrors] = React.useState<Record<string, string>>({});\n\n  const handleChange = React.useCallback(\n    (event: React.ChangeEvent<FieldElement>) => {\n      const { name, value } = event.target;\n      setValues((current) => ({ ...current, [name]: value }));\n    },\n    [],\n  );\n\n  const handleBlur = React.useCallback(\n    (_event: React.FocusEvent<FieldElement>) => undefined,\n    [],\n  );\n\n  const validateValues = React.useCallback(() => {\n    const nextErrors = validate(values);\n    setErrors(nextErrors);\n    return nextErrors;\n  }, [validate, values]);\n\n  const submitForm = React.useCallback(() => {\n    const nextErrors = validateValues();\n    const isValid = Object.keys(nextErrors).length === 0;\n    if (isValid) onSubmit(values);\n    return isValid;\n  }, [onSubmit, validateValues, values]);\n\n  return (\n    <form\n      className={className}\n      onSubmit={(event) => {\n        event.preventDefault();\n        submitForm();\n      }}\n    >\n      {children({\n        values,\n        errors,\n        handleChange,\n        handleBlur,\n        validateValues,\n        submitForm,\n      })}\n    </form>\n  );\n}\n",
      "type": "registry:ui",
      "target": "src/components/circle/ui/form.tsx"
    }
  ],
  "type": "registry:ui"
}