{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "button",
  "title": "Button",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-angular/components/button/button.component.ts",
      "content": "import { Component, ElementRef, Input, booleanAttribute, inject, ChangeDetectionStrategy } from '@angular/core'\nimport { cn } from '@/lib/utils'\nimport { buttonVariants, type ButtonVariants } from './button.variants'\n\nexport type ButtonVariant = NonNullable<ButtonVariants['variant']>\nexport type ButtonSize = NonNullable<ButtonVariants['size']>\n\n/**\n * Angular port of UIPKGE Button (React `Button`). Put it on the element you want to render\n * (`<button ui-button>`, `<a ui-button>`): the attribute form is the Radix `asChild`\n * equivalent, so an anchor keeps its own semantics and never receives `type`. On a native\n * `<button>` the default `type=\"button\"` avoids accidental form submits and `disabled` sets\n * the native attribute. The `<ui-button>` element form gets button semantics via\n * role + tabindex + Enter / Space (disabled there means aria-disabled + out of the tab order;\n * prefer `<button ui-button>` when you need a truly inert control). Class strings come from\n * shared `buttonVariants`.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-button, [ui-button]',\n  standalone: true,\n  host: {\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.data-slot]': '\"button\"',\n    '[attr.data-variant]': 'variant',\n    '[attr.data-size]': 'size',\n    '[attr.type]': 'isButton ? type : null',\n    '[attr.disabled]': 'isButton && disabled ? \"\" : null',\n    '[attr.role]': 'isCustom ? \"button\" : null',\n    '[attr.tabindex]': 'isCustom ? (disabled ? -1 : 0) : null',\n    '[attr.aria-disabled]': 'isCustom && disabled ? \"true\" : null',\n    '[attr.data-disabled]': 'isCustom && disabled ? \"\" : null',\n    '[class]': 'hostClass',\n    '(keydown)': 'onKeydown($event)',\n  },\n  template: `<ng-content />`,\n})\nexport class UiButtonComponent {\n  private readonly el = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement\n  /** Native `<button>` host: gets `type` + native `disabled`. */\n  readonly isButton = this.el.tagName === 'BUTTON'\n  /** `<ui-button>` custom element: needs role / tabindex / keyboard activation. */\n  readonly isCustom = this.el.tagName === 'UI-BUTTON'\n\n  @Input() variant: ButtonVariant = 'default'\n  @Input() size: ButtonSize = 'default'\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(buttonVariants({ variant: this.variant, size: this.size }), this.className)\n  }\n\n  onKeydown(event: KeyboardEvent): void {\n    if (!this.isCustom || this.disabled) return\n    if (event.key === 'Enter' || event.key === ' ') {\n      event.preventDefault()\n      this.el.click()\n    }\n  }\n}\n\nexport { buttonVariants, type ButtonVariants }\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/button/button.component.ts"
    },
    {
      "path": "packages/registry-angular/components/button/button-group.component.ts",
      "content": "import { Component, Input, booleanAttribute, ChangeDetectionStrategy } from '@angular/core'\nimport { cn } from '@/lib/utils'\n\nexport type ButtonGroupOrientation = 'horizontal' | 'vertical'\n\n/**\n * Angular port of UIPKGE ButtonGroup. Segmented toolbars and split buttons\n * with shared borders. Class strings identical to React ButtonGroup.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-button-group, [ui-button-group]',\n  standalone: true,\n  host: {\n    '[attr.data-slot]': '\"button-group\"',\n    '[attr.data-uipkge]': '\"\"',\n    '[attr.role]': '\"group\"',\n    '[attr.data-orientation]': 'orientation',\n    '[attr.data-attached]': 'attachedAttr',\n    '[class]': 'hostClass',\n  },\n  template: `<ng-content />`,\n})\nexport class UiButtonGroupComponent {\n  @Input() orientation: ButtonGroupOrientation = 'horizontal'\n  @Input({ transform: booleanAttribute }) attached = true\n  @Input('class') className?: string\n\n  get attachedAttr(): string | undefined {\n    return this.attached ? '' : undefined\n  }\n\n  get hostClass(): string {\n    const orientation = this.orientation\n    const attached = this.attached\n    return cn(\n      'inline-flex items-center',\n      orientation === 'vertical' ? 'flex-col items-stretch' : 'flex-row',\n      // The `.contents>button` rules reach a trigger button inside a layout-transparent wrapper\n      // root (<ui-dropdown-menu>, <ui-popover>, ...), which React's Radix roots don't render.\n      // Its data-slot is the trigger's, as in React, so only the plain `button` rules apply.\n      attached && [\n        '[&>[data-slot=button]]:relative [&>[data-slot=button]:focus-visible]:z-20 [&>[data-slot=button]:hover]:z-10',\n        '[&>button]:relative [&>button:focus-visible]:z-20 [&>button:hover]:z-10',\n        '[&>.contents>button]:relative [&>.contents>button:focus-visible]:z-20 [&>.contents>button:hover]:z-10',\n        orientation === 'horizontal' && [\n          '[&>[data-slot=button]]:rounded-none [&>button]:rounded-none',\n          '[&>.contents>button]:rounded-none',\n          '[&>[data-slot=button]:first-child]:rounded-l-md [&>button:first-child]:rounded-l-md',\n          '[&>.contents:first-child>button]:rounded-l-md',\n          '[&>[data-slot=button]:last-child]:rounded-r-md [&>button:last-child]:rounded-r-md',\n          '[&>.contents:last-child>button]:rounded-r-md',\n          '[&>[data-slot=button]:only-child]:rounded-md [&>button:only-child]:rounded-md',\n          '[&>.contents:only-child>button]:rounded-md',\n          '[&>[data-slot=button]:not(:first-child)]:-ml-px [&>button:not(:first-child)]:-ml-px',\n          '[&>.contents:not(:first-child)>button]:-ml-px',\n          '[&>[data-slot=button][data-variant=default]:not(:first-child)]:border-primary-foreground/20 [&>[data-slot=button][data-variant=default]:not(:first-child)]:border-l',\n          '[&>[data-slot=button]:not([data-variant]):not(:first-child)]:border-primary-foreground/20 [&>[data-slot=button]:not([data-variant]):not(:first-child)]:border-l',\n          '[&>[data-slot=button][data-variant=secondary]:not(:first-child)]:border-border [&>[data-slot=button][data-variant=secondary]:not(:first-child)]:border-l',\n          '[&>[data-slot=button][data-variant=destructive]:not(:first-child)]:border-l [&>[data-slot=button][data-variant=destructive]:not(:first-child)]:border-white/20',\n        ],\n        orientation === 'vertical' && [\n          '[&>[data-slot=button]]:rounded-none [&>button]:rounded-none',\n          '[&>.contents>button]:rounded-none',\n          '[&>[data-slot=button]:first-child]:rounded-t-md [&>button:first-child]:rounded-t-md',\n          '[&>.contents:first-child>button]:rounded-t-md',\n          '[&>[data-slot=button]:last-child]:rounded-b-md [&>button:last-child]:rounded-b-md',\n          '[&>.contents:last-child>button]:rounded-b-md',\n          '[&>[data-slot=button]:only-child]:rounded-md [&>button:only-child]:rounded-md',\n          '[&>.contents:only-child>button]:rounded-md',\n          '[&>[data-slot=button]:not(:first-child)]:-mt-px [&>button:not(:first-child)]:-mt-px',\n          '[&>.contents:not(:first-child)>button]:-mt-px',\n          '[&>[data-slot=button][data-variant=default]:not(:first-child)]:border-primary-foreground/20 [&>[data-slot=button][data-variant=default]:not(:first-child)]:border-t',\n          '[&>[data-slot=button]:not([data-variant]):not(:first-child)]:border-primary-foreground/20 [&>[data-slot=button]:not([data-variant]):not(:first-child)]:border-t',\n          '[&>[data-slot=button][data-variant=secondary]:not(:first-child)]:border-border [&>[data-slot=button][data-variant=secondary]:not(:first-child)]:border-t',\n          '[&>[data-slot=button][data-variant=destructive]:not(:first-child)]:border-t [&>[data-slot=button][data-variant=destructive]:not(:first-child)]:border-white/20',\n        ],\n      ],\n      !attached && (orientation === 'vertical' ? 'gap-1' : 'gap-1.5'),\n      this.className,\n    )\n  }\n}\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/button/button-group.component.ts"
    },
    {
      "path": "packages/registry-angular/components/button/button.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file (rather than the package\n * `index.ts`) so `Button.vue` can `import { buttonVariants } from\n * './button.variants'` without creating a circular dependency through the\n * index. See card.variants.ts for the same pattern + the SSR symptom that\n * motivated the split.\n */\nexport const buttonVariants = cva(\n  \"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,background-color,border-color,box-shadow,opacity,transform,scale] duration-150 active:scale-[0.97] active:duration-100 touch-manipulation disabled:cursor-not-allowed disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n  {\n    variants: {\n      variant: {\n        default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',\n        destructive:\n          'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',\n        outline:\n          'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',\n        secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',\n        ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',\n        link: 'text-primary underline-offset-4 hover:underline',\n      },\n      size: {\n        default: 'h-9 px-4 py-2 has-[>svg]:px-3',\n        sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',\n        lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',\n        xs: 'h-7 rounded-md gap-1 px-2 has-[>svg]:px-1.5 text-xs',\n        icon: 'size-9',\n        'icon-sm': 'size-8',\n        'icon-lg': 'size-10',\n        // Square counterpart of `xs` (h-7). Dense data toolbars run at 28px and\n        // had no icon-only tier to match, so icon controls sat 4px taller than\n        // the buttons beside them. Icons inside carry their own size class.\n        'icon-xs': 'size-7',\n        // Row-action tier: the hover-revealed copy control inside a two-line\n        // table cell. Anything taller than the 20px text line it sits on makes\n        // the row grow when the control appears.\n        'icon-2xs': 'size-5',\n      },\n    },\n    defaultVariants: {\n      variant: 'default',\n      size: 'default',\n    },\n  },\n)\n\nexport type ButtonVariants = VariantProps<typeof buttonVariants>\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/button/button.variants.ts"
    },
    {
      "path": "packages/registry-angular/components/button/index.ts",
      "content": "export {\n  UiButtonComponent,\n  buttonVariants,\n  type ButtonVariants,\n  type ButtonSize,\n  type ButtonVariant,\n} from './button.component'\nexport { UiButtonGroupComponent, type ButtonGroupOrientation } from './button-group.component'\n",
      "type": "registry:ui",
      "target": "~/src/app/components/ui/button/index.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Interactive button component with variants (default, destructive, outline, secondary, ghost, link), sizes, and standalone Angular API.",
  "categories": [
    "control"
  ]
}