{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "float-label",
  "title": "Float Label",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-angular/components/float-label/float-label.component.ts",
      "content": "import {\n  Component,\n  ElementRef,\n  Injector,\n  Input,\n  afterNextRender,\n  booleanAttribute,\n  inject,\n  signal,\n  ChangeDetectionStrategy,\n} from '@angular/core'\nimport { cn } from '@/lib/utils'\nimport { uniqueId } from '@/ui/popper/popper'\n\ntype FieldEl = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement\n\n/**\n * Angular port of the React FloatLabel. `<ui-float-label>` is React's `relative flex flex-col`\n * wrapper: the floating <label> first, then the projected field. The label floats (top-0,\n * scale-75) while the field is focused or has a value; a pre-filled / bound value is\n * detected after the first render, and the label is linked to the nested\n * input / textarea / select (its id, or a generated one assigned to it).\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-float-label',\n  standalone: true,\n  host: {\n    'data-uipkge': '',\n    'data-slot': 'float-label',\n    '[attr.data-floating]': 'isFloating ? \"true\" : \"false\"',\n    '[class]': 'hostClass',\n    '(focusin)': 'handleFocusIn($event)',\n    '(focusout)': 'handleFocusOut($event)',\n    '(input)': 'checkValue($event.target)',\n    '(change)': 'checkValue($event.target)',\n  },\n  template: `<label [attr.for]=\"controlId()\" [class]=\"labelClass\">{{ label }}</label\n    ><ng-content />`,\n})\nexport class UiFloatLabelComponent {\n  private readonly host = inject(ElementRef).nativeElement as HTMLElement\n  private readonly fallbackId = uniqueId('float-label')\n\n  @Input({ required: true }) label = ''\n  @Input({ transform: booleanAttribute }) required = false\n  @Input({ transform: booleanAttribute }) disabled = false\n  @Input('class') className?: string\n\n  readonly controlId = signal(this.fallbackId)\n  readonly isFocused = signal(false)\n  readonly hasValue = signal(false)\n\n  constructor() {\n    // Prefill detection + associate the floating label with the nested control (React layout effect).\n    afterNextRender(\n      () => {\n        const input = this.host.querySelector<FieldEl>('input, textarea, select')\n        if (!input) return\n        this.hasValue.set(!!input.value)\n        if (input.id) {\n          this.controlId.set(input.id)\n        } else {\n          input.id = this.fallbackId\n          this.controlId.set(this.fallbackId)\n        }\n      },\n      { injector: inject(Injector) },\n    )\n  }\n\n  get isFloating(): boolean {\n    return this.isFocused() || this.hasValue()\n  }\n\n  get hostClass(): string {\n    return cn('relative flex flex-col', this.disabled && 'opacity-50 cursor-not-allowed', this.className)\n  }\n\n  get labelClass(): string {\n    const floating = this.isFloating\n    return cn(\n      'text-muted-foreground pointer-events-none absolute left-3 z-10 bg-transparent px-1 text-sm transition-[color,background-color,top,translate,scale] duration-200',\n      !floating && 'top-1/2 -translate-y-1/2',\n      floating && 'top-0 -translate-y-1/2 scale-75 bg-background text-foreground',\n      this.isFocused() && 'text-ring',\n      this.required && \"after:text-destructive after:ml-0.5 after:content-['*']\",\n    )\n  }\n\n  checkValue(target: EventTarget | null): void {\n    const el = target as HTMLElement | null\n    if (!el) return\n    if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {\n      this.hasValue.set(!!el.value)\n    } else {\n      const input = el.querySelector?.<FieldEl>('input, textarea, select')\n      if (input) this.hasValue.set(!!input.value)\n    }\n  }\n\n  handleFocusIn(event: FocusEvent): void {\n    this.isFocused.set(true)\n    this.checkValue(event.target)\n  }\n\n  handleFocusOut(event: FocusEvent): void {\n    this.isFocused.set(false)\n    this.checkValue(event.target)\n  }\n}\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/float-label/float-label.component.ts"
    },
    {
      "path": "packages/registry-angular/components/float-label/index.ts",
      "content": "export { UiFloatLabelComponent } from './float-label.component'\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/float-label/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/popper.json"
  ],
  "description": "Floating label wrapper for any input element. The label floats up when the input is focused or has a value. Wrap it around any input, select, or textarea. Supports required indicator and disabled state.",
  "categories": [
    "control",
    "form"
  ]
}