{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "input",
  "title": "Input",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-angular/components/input/input.component.ts",
      "content": "import {\n  type AfterViewChecked,\n  ChangeDetectorRef,\n  Component,\n  ElementRef,\n  EventEmitter,\n  Input,\n  Output,\n  TemplateRef,\n  ViewChild,\n  booleanAttribute,\n  forwardRef,\n  inject,\n  signal,\n  ChangeDetectionStrategy,\n} from '@angular/core'\nimport { NG_VALUE_ACCESSOR, type ControlValueAccessor } from '@angular/forms'\nimport { cn } from '@/lib/utils'\nimport { UiRenderTemplateDirective } from '@/ui/popper/popper'\n\nexport type InputSize = 'small' | 'middle' | 'large'\nexport type InputVariant = 'outlined' | 'filled' | 'borderless'\nexport type InputStatus = 'error' | 'warning'\n/** Text, or an <ng-template> (an icon, a node), for prefix / suffix / addons. */\nexport type InputSlot = string | TemplateRef<unknown> | null | undefined\n\nconst sizeClasses: Record<InputSize, string> = {\n  small: 'h-8 text-xs',\n  middle: 'h-9 text-base md:text-sm',\n  large: 'h-11 text-base',\n}\n\nconst variantMap: Record<InputVariant, string> = {\n  outlined: 'border-input bg-transparent shadow-xs',\n  filled: 'border-transparent bg-muted/50 shadow-none',\n  borderless: 'border-transparent bg-transparent shadow-none',\n}\n\nconst statusMap: Record<InputStatus, string> = {\n  error:\n    'border-destructive focus-within:border-destructive focus-within:ring-destructive/20 dark:focus-within:ring-destructive/40',\n  warning: 'border-warning focus-within:border-warning focus-within:ring-warning/20',\n}\n\nfunction optionalBool(v: unknown): boolean | undefined {\n  return v === undefined || v === null ? undefined : booleanAttribute(v)\n}\n\n/**\n * Angular port of the React Input. The `<ui-input>` host is React's outer `flex w-full`\n * row; inside it sit the optional addons and the bordered wrapper (`data-slot=\"input\"`,\n * which receives `class`) around the native `<input>`.\n *\n * Model is React's `value` / `defaultValue` / `valueChange`: a bound `value` is controlled\n * (typing that the parent does not accept is reverted, like React), otherwise the input\n * keeps its own state. NG_VALUE_ACCESSOR is provided for formControl / ngModel.\n * `prefix` / `suffix` / `prefixIcon` / `suffixIcon` / `addonBefore` / `addonAfter` take\n * text or an <ng-template>.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-input, div[ui-input]',\n  standalone: true,\n  exportAs: 'uiInput',\n  imports: [UiRenderTemplateDirective],\n  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => UiInputComponent), multi: true }],\n  host: {\n    '[attr.class]': '\"flex w-full\"',\n    // These belong to the native <input>; keep them off the host so ids stay unique.\n    '[attr.id]': 'null',\n    '[attr.aria-invalid]': 'null',\n    '[attr.aria-label]': 'null',\n    '[attr.aria-describedby]': 'null',\n    '[attr.placeholder]': 'null',\n  },\n  template: `\n    @if (addonBefore) {\n      <div [class]=\"addonClasses('before')\">\n        @if (isTemplate(addonBefore)) {\n          <ng-container [uiRenderTemplate]=\"addonBefore\" />\n        } @else {\n          {{ addonBefore }}\n        }\n      </div>\n    }\n    <div\n      data-uipkge=\"\"\n      data-slot=\"input\"\n      [class]=\"wrapperClasses\"\n      [attr.aria-invalid]=\"resolvedAriaInvalid\"\n      (mouseenter)=\"isHovered.set(true)\"\n      (mouseleave)=\"isHovered.set(false)\"\n      (click)=\"focus()\"\n    >\n      @if (hasPrefix) {\n        <span [class]=\"prefixClass\">\n          @if (isTemplate(prefixContent)) {\n            <ng-container [uiRenderTemplate]=\"prefixContent\" />\n          } @else {\n            {{ prefixContent }}\n          }\n        </span>\n      }\n      <input\n        #inputEl\n        [attr.id]=\"id ?? null\"\n        [value]=\"currentValue\"\n        [type]=\"computedType\"\n        [disabled]=\"disabled\"\n        [readOnly]=\"readOnly\"\n        [attr.maxlength]=\"maxLength ?? null\"\n        [attr.minlength]=\"minLength ?? null\"\n        [attr.aria-invalid]=\"resolvedAriaInvalid\"\n        [attr.aria-label]=\"ariaLabel ?? null\"\n        [attr.aria-describedby]=\"ariaDescribedby ?? null\"\n        [attr.placeholder]=\"placeholder ?? null\"\n        [attr.name]=\"name ?? null\"\n        [attr.autocomplete]=\"autoComplete ?? null\"\n        [attr.inputmode]=\"inputMode ?? null\"\n        [attr.pattern]=\"pattern ?? null\"\n        [attr.min]=\"min ?? null\"\n        [attr.max]=\"max ?? null\"\n        [attr.step]=\"step ?? null\"\n        [required]=\"required\"\n        [autofocus]=\"autoFocus\"\n        [class]=\"inputClass\"\n        (input)=\"onInput($event)\"\n        (focus)=\"onFocus($event)\"\n        (blur)=\"onBlur($event)\"\n      />\n      @if (hasRight) {\n        <div [class]=\"rightRailClass\">\n          @if (showClear) {\n            <button\n              type=\"button\"\n              aria-label=\"Clear input\"\n              class=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 shrink-0 rounded p-0.5 transition-colors focus-visible:ring-1 focus-visible:outline-none\"\n              (mousedown)=\"$event.preventDefault()\"\n              (click)=\"handleClear()\"\n            >\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                width=\"24\"\n                height=\"24\"\n                viewBox=\"0 0 24 24\"\n                fill=\"none\"\n                stroke=\"currentColor\"\n                stroke-width=\"2\"\n                stroke-linecap=\"round\"\n                stroke-linejoin=\"round\"\n                class=\"lucide lucide-x size-4\"\n                aria-hidden=\"true\"\n              >\n                <path d=\"M18 6 6 18\" />\n                <path d=\"m6 6 12 12\" />\n              </svg>\n            </button>\n          }\n          @if (hasPasswordToggle && !disabled && !readOnly) {\n            <button\n              type=\"button\"\n              [attr.aria-label]=\"passwordVisible() ? 'Hide password' : 'Show password'\"\n              [attr.aria-pressed]=\"passwordVisible()\"\n              class=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 shrink-0 rounded p-0.5 transition-colors focus-visible:ring-1 focus-visible:outline-none\"\n              (mousedown)=\"$event.preventDefault()\"\n              (click)=\"togglePassword()\"\n            >\n              @if (passwordVisible()) {\n                <svg\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                  width=\"24\"\n                  height=\"24\"\n                  viewBox=\"0 0 24 24\"\n                  fill=\"none\"\n                  stroke=\"currentColor\"\n                  stroke-width=\"2\"\n                  stroke-linecap=\"round\"\n                  stroke-linejoin=\"round\"\n                  class=\"lucide lucide-eye-off size-4\"\n                  aria-hidden=\"true\"\n                >\n                  <path\n                    d=\"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\"\n                  />\n                  <path d=\"M14.084 14.158a3 3 0 0 1-4.242-4.242\" />\n                  <path\n                    d=\"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\"\n                  />\n                  <path d=\"m2 2 20 20\" />\n                </svg>\n              } @else {\n                <svg\n                  xmlns=\"http://www.w3.org/2000/svg\"\n                  width=\"24\"\n                  height=\"24\"\n                  viewBox=\"0 0 24 24\"\n                  fill=\"none\"\n                  stroke=\"currentColor\"\n                  stroke-width=\"2\"\n                  stroke-linecap=\"round\"\n                  stroke-linejoin=\"round\"\n                  class=\"lucide lucide-eye size-4\"\n                  aria-hidden=\"true\"\n                >\n                  <path\n                    d=\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\"\n                  />\n                  <circle cx=\"12\" cy=\"12\" r=\"3\" />\n                </svg>\n              }\n            </button>\n          }\n          @if (hasCount) {\n            <span class=\"text-muted-foreground pointer-events-none text-xs select-none\"\n              >{{ currentValue.length }}/{{ maxLength }}</span\n            >\n          }\n          @if (hasSuffix) {\n            <span class=\"text-muted-foreground pointer-events-none select-none\">\n              @if (isTemplate(suffixContent)) {\n                <ng-container [uiRenderTemplate]=\"suffixContent\" />\n              } @else {\n                {{ suffixContent }}\n              }\n            </span>\n          }\n        </div>\n      }\n    </div>\n    @if (addonAfter) {\n      <div [class]=\"addonClasses('after')\">\n        @if (isTemplate(addonAfter)) {\n          <ng-container [uiRenderTemplate]=\"addonAfter\" />\n        } @else {\n          {{ addonAfter }}\n        }\n      </div>\n    }\n  `,\n})\nexport class UiInputComponent implements ControlValueAccessor, AfterViewChecked {\n  // Zoneless-safe: form writes happen outside template events, so schedule a repaint.\n  private readonly cdr = inject(ChangeDetectorRef)\n  @ViewChild('inputEl', { static: true }) inputEl!: ElementRef<HTMLInputElement>\n\n  private readonly _valueProp = signal<string | undefined>(undefined)\n  private readonly _internal = signal<string | null>(null)\n\n  /** Controlled value (React `value`). Reading it returns the resolved current value. */\n  @Input()\n  set value(v: string | number | null | undefined) {\n    this._valueProp.set(v === undefined ? undefined : String(v ?? ''))\n  }\n  get value(): string {\n    return this.currentValue\n  }\n  @Input() defaultValue?: string | number\n  /** React `onChange`: emits the new text on every edit (and '' on clear). */\n  @Output() valueChange = new EventEmitter<string>()\n  /** React `onFocus` / `onBlur` (focus events do not bubble out of the host). */\n  @Output('focus') focusEvent = new EventEmitter<FocusEvent>()\n  @Output('blur') blurEvent = new EventEmitter<FocusEvent>()\n\n  @Input() size: InputSize = 'middle'\n  @Input() variant: InputVariant = 'outlined'\n  @Input() status?: InputStatus\n  @Input() prefix?: InputSlot\n  @Input() suffix?: InputSlot\n  @Input() prefixIcon?: InputSlot\n  @Input() suffixIcon?: InputSlot\n  @Input() addonBefore?: InputSlot\n  @Input() addonAfter?: InputSlot\n  @Input({ transform: booleanAttribute }) allowClear = false\n  @Input({ transform: booleanAttribute }) showCount = false\n  @Input({ transform: booleanAttribute }) showPasswordToggle = false\n  @Input() type = 'text'\n  @Input({ transform: booleanAttribute }) disabled = false\n  @Input({ transform: booleanAttribute }) readOnly = false\n  /** Lower-case HTML spelling of `readOnly`. */\n  @Input({ alias: 'readonly', transform: booleanAttribute }) set readonlyAlias(v: boolean) {\n    this.readOnly = v\n  }\n  @Input() maxLength?: number\n  /** Lower-case HTML spelling of `maxLength`. */\n  @Input('maxlength') set maxlengthAlias(v: number | string | undefined) {\n    this.maxLength = v == null || v === '' ? undefined : Number(v)\n  }\n  @Input() minLength?: number\n  @Input() id?: string\n  @Input() name?: string\n  @Input() placeholder?: string\n  @Input() autoComplete?: string\n  @Input() inputMode?: string\n  @Input() pattern?: string\n  @Input() min?: number | string\n  @Input() max?: number | string\n  @Input() step?: number | string\n  @Input({ transform: booleanAttribute }) required = false\n  @Input({ transform: booleanAttribute }) autoFocus = false\n  @Input({ alias: 'aria-invalid', transform: optionalBool }) ariaInvalid?: boolean\n  @Input('aria-label') ariaLabel?: string\n  @Input('aria-describedby') ariaDescribedby?: string\n  /** Wrapper className: lands on the bordered control, not the <input>. */\n  @Input('class') className?: string\n\n  readonly isFocused = signal(false)\n  readonly isHovered = signal(false)\n  readonly passwordVisible = signal(false)\n\n  private onChange: (v: string) => void = () => {}\n  private onTouched: () => void = () => {}\n\n  get isControlled(): boolean {\n    return this._valueProp() !== undefined\n  }\n\n  get currentValue(): string {\n    return this._valueProp() ?? this._internal() ?? (this.defaultValue != null ? String(this.defaultValue) : '')\n  }\n\n  get isPassword(): boolean {\n    return this.type === 'password'\n  }\n  get hasPrefix(): boolean {\n    return !!this.prefix || !!this.prefixIcon\n  }\n  get hasSuffix(): boolean {\n    return !!this.suffix || !!this.suffixIcon\n  }\n  /** A prefixIcon wins over the prefix text, like React's `prefixIcon ?? prefix`. */\n  get prefixContent(): InputSlot {\n    return this.prefixIcon ?? this.prefix\n  }\n  get suffixContent(): InputSlot {\n    return this.suffixIcon ?? this.suffix\n  }\n  get hasPasswordToggle(): boolean {\n    return this.showPasswordToggle && this.isPassword\n  }\n  get hasCount(): boolean {\n    return this.showCount && this.maxLength != null\n  }\n  get hasRight(): boolean {\n    return this.hasSuffix || this.allowClear || this.hasPasswordToggle || this.hasCount\n  }\n  get showClear(): boolean {\n    return (\n      this.allowClear &&\n      this.currentValue.length > 0 &&\n      (this.isFocused() || this.isHovered()) &&\n      !this.disabled &&\n      !this.readOnly\n    )\n  }\n  get computedType(): string {\n    return !this.isPassword ? this.type : this.passwordVisible() ? 'text' : 'password'\n  }\n  /** status=error implies invalid for AT; set on the wrapper and the native input. */\n  get resolvedAriaInvalid(): 'true' | 'false' | null {\n    const v = this.status === 'error' ? true : this.ariaInvalid\n    return v === undefined ? null : v ? 'true' : 'false'\n  }\n\n  private get sidePad(): { l: string; r: string } {\n    return this.size === 'small'\n      ? { l: 'pl-2', r: 'pr-2' }\n      : this.size === 'large'\n        ? { l: 'pl-3', r: 'pr-3' }\n        : { l: 'pl-2.5', r: 'pr-2.5' }\n  }\n\n  get wrapperClasses(): string {\n    const hasBefore = !!this.addonBefore\n    const hasAfter = !!this.addonAfter\n    const wrapperRounded =\n      hasBefore && hasAfter\n        ? 'rounded-none'\n        : hasBefore\n          ? 'rounded-l-none rounded-r-md'\n          : hasAfter\n            ? 'rounded-r-none rounded-l-md'\n            : 'rounded-md'\n    return cn(\n      'flex w-full items-center gap-1.5 overflow-hidden border transition-[color,box-shadow] outline-none',\n      sizeClasses[this.size],\n      variantMap[this.variant],\n      this.status ? statusMap[this.status] : '',\n      !this.status ? 'focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]' : '',\n      this.disabled ? 'pointer-events-none opacity-50 cursor-not-allowed bg-muted/30' : '',\n      'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',\n      wrapperRounded,\n      this.className,\n    )\n  }\n\n  addonClasses(position: 'before' | 'after'): string {\n    const roundedClass =\n      position === 'before' ? 'rounded-l-md rounded-r-none border-r-0' : 'rounded-r-md rounded-l-none border-l-0'\n    return cn(\n      'flex items-center bg-muted px-3 text-sm text-muted-foreground border border-input',\n      roundedClass,\n      sizeClasses[this.size],\n    )\n  }\n\n  get prefixClass(): string {\n    return cn('text-muted-foreground pointer-events-none shrink-0 select-none', this.sidePad.l)\n  }\n\n  get rightRailClass(): string {\n    return cn('flex shrink-0 items-center gap-1', this.sidePad.r)\n  }\n\n  get inputClass(): string {\n    const { l, r } = this.sidePad\n    const hasLeft = this.hasPrefix\n    const hasRight = this.hasRight\n    const inputPadding =\n      !hasLeft && !hasRight\n        ? cn(l, r)\n        : hasLeft && !hasRight\n          ? cn('pl-0', r)\n          : !hasLeft && hasRight\n            ? cn(l, 'pr-0')\n            : 'px-0'\n    return cn(\n      'w-full min-w-0 flex-1 bg-transparent outline-none',\n      'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground',\n      'file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium',\n      'disabled:cursor-not-allowed',\n      inputPadding,\n    )\n  }\n\n  isTemplate(v: InputSlot): v is TemplateRef<unknown> {\n    return v instanceof TemplateRef\n  }\n\n  /** Focus the native input (React: the forwarded ref). */\n  focus(): void {\n    this.inputEl?.nativeElement.focus()\n  }\n\n  onInput(event: Event): void {\n    const next = (event.target as HTMLInputElement).value\n    if (!this.isControlled) this._internal.set(next)\n    this.onChange(next)\n    this.valueChange.emit(next)\n  }\n\n  /** A controlled parent that did not take the edit wins: put its value back (React semantics). */\n  ngAfterViewChecked(): void {\n    const el = this.inputEl?.nativeElement\n    if (el && this.isControlled && el.value !== this.currentValue) el.value = this.currentValue\n  }\n\n  onFocus(event: FocusEvent): void {\n    this.isFocused.set(true)\n    this.focusEvent.emit(event)\n  }\n\n  onBlur(event: FocusEvent): void {\n    this.isFocused.set(false)\n    this.onTouched()\n    this.blurEvent.emit(event)\n  }\n\n  handleClear(): void {\n    const el = this.inputEl.nativeElement\n    // Go through a real input event so every listener (valueChange, forms, bubbling (input)) sees it.\n    el.value = ''\n    el.dispatchEvent(new Event('input', { bubbles: true }))\n    el.focus()\n  }\n\n  togglePassword(): void {\n    this.passwordVisible.update((v) => !v)\n    this.focus()\n  }\n\n  writeValue(v: string | number | null): void {\n    this._internal.set(v == null ? '' : String(v))\n    this.cdr.markForCheck()\n  }\n\n  registerOnChange(fn: (v: string) => void): void {\n    this.onChange = fn\n  }\n\n  registerOnTouched(fn: () => void): void {\n    this.onTouched = fn\n  }\n\n  setDisabledState(isDisabled: boolean): void {\n    this.disabled = isDisabled\n    this.cdr.markForCheck()\n  }\n}\n\nexport type InputGroupSize = 'small' | 'middle' | 'large'\n\nconst groupSizeClasses: Record<InputGroupSize, string> = {\n  small: 'h-8 text-xs',\n  middle: 'h-9 text-sm',\n  large: 'h-11 text-base',\n}\n\n/** React InputGroup: one bordered row that flattens nested Inputs, addons and buttons. */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-input-group, div[ui-input-group]',\n  standalone: true,\n  host: {\n    'data-uipkge': '',\n    'data-slot': 'input-group',\n    '[attr.data-size]': 'size',\n    '[attr.data-disabled]': 'disabled ? \"\" : null',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiInputGroupComponent {\n  @Input() size: InputGroupSize = 'middle'\n  @Input({ transform: booleanAttribute }) disabled = false\n  @Input('class') className?: string\n\n  get hostClass(): string {\n    return cn(\n      'group/input-group border-input bg-background relative flex w-full items-stretch rounded-md border shadow-xs transition-[color,box-shadow]',\n      'focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px] focus-within:outline-none',\n      '[&_[data-slot=input]]:rounded-none [&_[data-slot=input]]:border-0 [&_[data-slot=input]]:bg-transparent [&_[data-slot=input]]:shadow-none [&_[data-slot=input]]:focus-within:ring-0',\n      '[&_input]:h-full [&_input]:flex-1 [&_input]:border-0 [&_input]:bg-transparent [&_input]:px-3 [&_input]:text-sm [&_input]:outline-none [&_input]:focus-visible:ring-0',\n      this.disabled && 'bg-muted/30 pointer-events-none cursor-not-allowed opacity-50',\n      groupSizeClasses[this.size],\n      this.className,\n    )\n  }\n}\n\n/** React InputGroupAddon: static text / icon segment inside an InputGroup. */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-input-group-addon, div[ui-input-group-addon]',\n  standalone: true,\n  host: {\n    'data-uipkge': '',\n    'data-slot': 'input-group-addon',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiInputGroupAddonComponent {\n  /** Kept for React API parity (React accepts it but does not use it either). */\n  @Input() align: 'inline' | 'block' = 'inline'\n  @Input('class') className?: string\n\n  get hostClass(): string {\n    return cn(\n      'text-muted-foreground flex shrink-0 items-center justify-center px-3 text-sm select-none',\n      'border-input first:rounded-l-[calc(var(--radius)-1px)] last:rounded-r-[calc(var(--radius)-1px)]',\n      'border-r first:border-l-0 last:border-r-0',\n      this.className,\n    )\n  }\n}\n\nexport type InputGroupButtonVariant = 'default' | 'secondary' | 'ghost' | 'outline'\n\nconst groupButtonVariants: Record<InputGroupButtonVariant, string> = {\n  default: 'bg-primary text-primary-foreground hover:bg-primary/90',\n  secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',\n  ghost: 'hover:bg-accent hover:text-accent-foreground',\n  outline: 'border-l border-input hover:bg-accent hover:text-accent-foreground',\n}\n\n/** React InputGroupButton: put it on a native button inside an InputGroup. */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'button[ui-input-group-button]',\n  standalone: true,\n  host: {\n    'data-uipkge': '',\n    'data-slot': 'input-group-button',\n    '[attr.type]': 'type',\n    '[disabled]': 'disabled',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiInputGroupButtonComponent {\n  @Input() variant: InputGroupButtonVariant = 'ghost'\n  @Input() type: 'button' | 'submit' | 'reset' = 'button'\n  @Input({ transform: booleanAttribute }) disabled = false\n  @Input('class') className?: string\n\n  get hostClass(): string {\n    return cn(\n      'inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 px-3 text-sm font-medium transition-colors select-none',\n      'first:rounded-l-[calc(var(--radius)-1px)] last:rounded-r-[calc(var(--radius)-1px)]',\n      'focus-visible:ring-ring focus-visible:ring-1 focus-visible:outline-none',\n      'disabled:pointer-events-none disabled:opacity-50',\n      groupButtonVariants[this.variant],\n      this.className,\n    )\n  }\n}\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/input/input.component.ts"
    },
    {
      "path": "packages/registry-angular/components/input/index.ts",
      "content": "export {\n  UiInputComponent,\n  UiInputGroupComponent,\n  UiInputGroupAddonComponent,\n  UiInputGroupButtonComponent,\n  type InputSize,\n  type InputVariant,\n  type InputStatus,\n  type InputSlot,\n  type InputGroupSize,\n  type InputGroupButtonVariant,\n} from './input.component'\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/input/index.ts"
    }
  ],
  "dependencies": [
    "@angular/forms"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/popper.json"
  ],
  "description": "Text input — single-line. Three sizes, three variants (outlined / filled / borderless), error / warning status, prefix / suffix (text or ng-template), addonBefore / addonAfter, allow-clear, password toggle, char count, and composite InputGroup with addons and action buttons.",
  "categories": [
    "control"
  ]
}