{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "button",
  "type": "registry:component",
  "title": "Button",
  "description": "Something to press, in the weight its action deserves, with a spinner for while it works.",
  "dependencies": [
    "@base-ui/react",
    "tailwind-variants"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/panelui/button.tsx",
      "type": "registry:component",
      "target": "@components/panelui/button.tsx",
      "content": "/**\n * Button — something to press, in the weight its action deserves.\n *\n * ```tsx\n * <Button>Save</Button>\n * <Button variant=\"outline\" size=\"sm\">Cancel</Button>\n * <Button variant=\"ghost\" size=\"icon\" aria-label=\"Close\"><XIcon /></Button>\n * <Button loading>Saving</Button>\n * ```\n *\n * ## Weight\n *\n * `default` is the one action a view is for, and there should be one of it.\n * `secondary` and `outline` are the actions beside it, `ghost` the ones that\n * should not draw the eye until they are looked for, and `destructive` the\n * one that cannot be taken back.\n *\n * ## Loading\n *\n * `loading` sets a spinner over the label and stops presses. The label stays\n * in place, hidden, so the width does not change: a button that resizes as it\n * is pressed moves whatever sits beside it.\n *\n * ## A link that looks like a button\n *\n * An action that goes to another page is a link, whatever it looks like, so\n * that it opens in a new tab and reads as a link to a screen reader. Pass the\n * link as `render` and the button's look and behaviour are put on it:\n * `<Button render={<a href=\"/\" />}>Home</Button>`. The button is the headless\n * button primitive underneath, so keyboard handling, `disabled` and focus work\n * the same whatever it is drawn as.\n */\nimport { Button as BaseButton } from '@base-ui/react/button';\nimport { isValidElement, type ComponentProps, type ReactElement } from 'react';\nimport { tv, type VariantProps } from 'tailwind-variants';\n\nexport const buttonVariants = tv({\n  base: [\n    'relative inline-flex shrink-0 cursor-pointer select-none items-center justify-center gap-2 whitespace-nowrap font-medium outline-none',\n    'transition-[background-color,color,border-color,box-shadow,scale] duration-150',\n    'focus-visible:ring-[3px] focus-visible:ring-ring/40',\n    'active:not-disabled:scale-[0.97] motion-reduce:active:scale-100',\n    'disabled:cursor-default disabled:opacity-50 aria-busy:cursor-progress',\n    '[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4',\n  ],\n  variants: {\n    variant: {\n      default:\n        'bg-primary text-primary-foreground shadow-xs inset-shadow-[0_1px_0_rgb(255_255_255/0.12)] hover:not-disabled:bg-primary/90',\n      secondary: 'bg-secondary text-secondary-foreground hover:not-disabled:bg-secondary/80',\n      outline:\n        'border bg-background text-foreground shadow-xs hover:not-disabled:bg-accent hover:not-disabled:text-accent-foreground dark:bg-input/30',\n      ghost: 'text-foreground hover:not-disabled:bg-accent hover:not-disabled:text-accent-foreground',\n      destructive:\n        'bg-destructive text-white shadow-xs hover:not-disabled:bg-destructive/90 focus-visible:ring-destructive/40',\n      link: 'text-foreground underline-offset-4 hover:underline',\n    },\n    size: {\n      sm: 'h-8 rounded-md px-3 text-[0.8125rem]',\n      md: 'h-9 rounded-lg px-4 text-sm',\n      lg: 'h-11 rounded-xl px-5 text-[0.9375rem]',\n      'icon-sm': 'size-8 rounded-md',\n      icon: 'size-9 rounded-lg',\n      'icon-lg': 'size-11 rounded-xl [&_svg:not([class*=size-])]:size-5',\n    },\n    /** `pill` rounds the ends fully, whatever the size. */\n    shape: {\n      default: '',\n      pill: 'rounded-full',\n    },\n  },\n  defaultVariants: { variant: 'default', size: 'md', shape: 'default' },\n});\n\nexport type ButtonVariant = NonNullable<VariantProps<typeof buttonVariants>['variant']>;\nexport type ButtonSize = NonNullable<VariantProps<typeof buttonVariants>['size']>;\n\nexport interface ButtonProps extends ComponentProps<'button'> {\n  /** How much the button asks to be pressed. Default `default`. */\n  variant?: ButtonVariant;\n  /** Its height and padding. The `icon` sizes are square, for a button that holds only an icon. Default `md`. */\n  size?: ButtonSize;\n  /** `pill` rounds the ends fully. Default `default`. */\n  shape?: 'default' | 'pill';\n  /** Shows a spinner over the label, sets `aria-busy` and stops presses, keeping the width. */\n  loading?: boolean;\n  /**\n   * An element to draw the button as, such as `<a href=\"/\" />` or a router's\n   * link, or a function of the props and state that returns one. It takes the\n   * button's classes, children, ref and event handlers. A link stays a link: it\n   * is not given `type`, and Space does not press it.\n   */\n  render?: BaseButton.Props['render'];\n  /**\n   * Whether `render` draws a native `<button>`. Set automatically for an\n   * element; pass it when `render` is a function that returns something else.\n   */\n  nativeButton?: boolean;\n}\n\nfunction Spinner() {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      data-slot=\"button-spinner\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"2.5\"\n      strokeLinecap=\"round\"\n      className=\"absolute inset-0 m-auto animate-spin motion-reduce:animate-none\"\n    >\n      <path d=\"M21 12a9 9 0 1 1-6.22-8.56\" />\n    </svg>\n  );\n}\n\n/** A button. Every attribute of `<button>` passes through. */\nfunction ButtonRoot({\n  variant,\n  size,\n  shape,\n  loading = false,\n  disabled,\n  type = 'button',\n  className,\n  children,\n  render,\n  nativeButton,\n  ...props\n}: ButtonProps) {\n  // An element to render tells whether it is a native button; a function cannot, so it says.\n  const native = nativeButton ?? (render === undefined || (isValidElement(render) && render.type === 'button'));\n  return (\n    <BaseButton\n      type={native ? type : undefined}\n      data-slot=\"button\"\n      data-variant={variant ?? 'default'}\n      aria-busy={loading || undefined}\n      disabled={disabled || loading}\n      focusableWhenDisabled={loading}\n      nativeButton={native}\n      render={render as ReactElement | undefined}\n      className={buttonVariants({ variant, size, shape, className })}\n      {...(props as Omit<BaseButton.Props, 'className'>)}\n    >\n      {loading ? <Spinner /> : null}\n      {/* `contents`, so the label still lays out in the button's own row. */}\n      <span className={loading ? 'invisible contents' : 'contents'}>{children}</span>\n    </BaseButton>\n  );\n}\nButtonRoot.displayName = 'Button';\n\nexport const Button = ButtonRoot;\n"
    }
  ]
}
