{
  "name": "button",
  "dependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "button.tsx",
      "content": "\"use client\";\r\n\r\nimport { createVariants, css, keyframes, themeVars as theme } from \"@yugnex/core\";\r\nimport {\r\n  cloneElement,\r\n  forwardRef,\r\n  isValidElement,\r\n  type ButtonHTMLAttributes,\r\n  type ReactElement,\r\n  type Ref,\r\n} from \"react\";\r\n\r\nfunction mergeRefs<T>(...refs: Array<Ref<T> | undefined>): (node: T | null) => void {\r\n  return (node) => {\r\n    for (const ref of refs) {\r\n      if (typeof ref === \"function\") ref(node);\r\n      else if (ref) (ref as { current: T | null }).current = node;\r\n    }\r\n  };\r\n}\r\n\r\nconst spin = keyframes({\r\n  from: { transform: \"rotate(0deg)\" },\r\n  to: { transform: \"rotate(360deg)\" },\r\n});\r\n\r\nconst spinnerClass = css({\r\n  width: \"1em\",\r\n  height: \"1em\",\r\n  borderRadius: \"9999px\",\r\n  border: \"2px solid currentColor\",\r\n  borderTopColor: \"transparent\",\r\n  animation: `${spin} 0.6s linear infinite`,\r\n});\r\n\r\nconst buttonVariants = createVariants({\r\n  base: {\r\n    display: \"inline-flex\",\r\n    alignItems: \"center\",\r\n    justifyContent: \"center\",\r\n    gap: theme.space[2],\r\n    fontFamily: theme.fontFamily.sans,\r\n    fontWeight: theme.fontWeight.medium,\r\n    borderRadius: theme.radius.md,\r\n    border: \"1px solid transparent\",\r\n    cursor: \"pointer\",\r\n    transitionProperty: \"background-color, border-color, transform, opacity\",\r\n    transitionDuration: theme.duration.fast,\r\n    transitionTimingFunction: theme.easing.standard,\r\n    \"&:active\": {\r\n      transform: \"scale(0.97)\",\r\n    },\r\n    \"&:focus-visible\": {\r\n      outline: `2px solid ${theme.color.ring}`,\r\n      outlineOffset: \"2px\",\r\n    },\r\n    \"&:disabled\": {\r\n      opacity: 0.5,\r\n      cursor: \"not-allowed\",\r\n      transform: \"none\",\r\n    },\r\n  },\r\n  variants: {\r\n    variant: {\r\n      solid: {\r\n        backgroundColor: theme.color.primary,\r\n        color: theme.color.primaryForeground,\r\n        \"&:hover:not(:disabled)\": { opacity: 0.92 },\r\n      },\r\n      outline: {\r\n        backgroundColor: \"transparent\",\r\n        borderColor: theme.color.border,\r\n        color: theme.color.foreground,\r\n        \"&:hover:not(:disabled)\": { backgroundColor: theme.color.muted },\r\n      },\r\n      ghost: {\r\n        backgroundColor: \"transparent\",\r\n        color: theme.color.foreground,\r\n        \"&:hover:not(:disabled)\": { backgroundColor: theme.color.muted },\r\n      },\r\n      soft: {\r\n        backgroundColor: theme.color.accent,\r\n        color: theme.color.accentForeground,\r\n        \"&:hover:not(:disabled)\": { opacity: 0.85 },\r\n      },\r\n    },\r\n    tone: {\r\n      primary: {},\r\n      destructive: {},\r\n    },\r\n    size: {\r\n      sm: {\r\n        height: \"2rem\",\r\n        paddingLeft: theme.space[3],\r\n        paddingRight: theme.space[3],\r\n        fontSize: theme.fontSize.sm,\r\n      },\r\n      md: {\r\n        height: \"2.5rem\",\r\n        paddingLeft: theme.space[4],\r\n        paddingRight: theme.space[4],\r\n        fontSize: theme.fontSize.sm,\r\n      },\r\n      lg: {\r\n        height: \"2.75rem\",\r\n        paddingLeft: theme.space[5],\r\n        paddingRight: theme.space[5],\r\n        fontSize: theme.fontSize.base,\r\n      },\r\n    },\r\n  },\r\n  compoundVariants: [\r\n    {\r\n      variant: \"solid\",\r\n      tone: \"destructive\",\r\n      css: { backgroundColor: theme.color.destructive, color: theme.color.destructiveForeground },\r\n    },\r\n    {\r\n      variant: \"outline\",\r\n      tone: \"destructive\",\r\n      css: { borderColor: theme.color.destructive, color: theme.color.destructive },\r\n    },\r\n    {\r\n      variant: \"ghost\",\r\n      tone: \"destructive\",\r\n      css: { color: theme.color.destructive },\r\n    },\r\n    {\r\n      variant: \"soft\",\r\n      tone: \"destructive\",\r\n      css: { backgroundColor: theme.color.destructive, color: theme.color.destructiveForeground },\r\n    },\r\n  ],\r\n  defaultVariants: {\r\n    variant: \"solid\",\r\n    tone: \"primary\",\r\n    size: \"md\",\r\n  },\r\n});\r\n\r\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\r\n  variant?: \"solid\" | \"outline\" | \"ghost\" | \"soft\";\r\n  tone?: \"primary\" | \"destructive\";\r\n  size?: \"sm\" | \"md\" | \"lg\";\r\n  isLoading?: boolean;\r\n  /** Render the single child element (e.g. a Next.js <Link>) with Button's classes/ref instead of a <button>. */\r\n  asChild?: boolean;\r\n}\r\n\r\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\r\n  { variant, tone, size, isLoading, disabled, className, children, asChild, ...props },\r\n  ref,\r\n) {\r\n  const classes = buttonVariants({ variant, tone, size, className });\r\n\r\n  if (asChild && isValidElement(children)) {\r\n    const element = children as ReactElement<Record<string, unknown>>;\r\n    const childRef = (element as { ref?: Ref<HTMLElement> }).ref;\r\n    const childProps = element.props as { className?: string };\r\n    return cloneElement(element, {\r\n      ...props,\r\n      ref: mergeRefs(ref, childRef),\r\n      className: [classes, childProps.className].filter(Boolean).join(\" \"),\r\n      \"aria-busy\": isLoading || undefined,\r\n    });\r\n  }\r\n\r\n  return (\r\n    <button\r\n      ref={ref}\r\n      className={classes}\r\n      disabled={disabled || isLoading}\r\n      aria-busy={isLoading || undefined}\r\n      {...props}\r\n    >\r\n      {isLoading ? <span className={spinnerClass} aria-hidden=\"true\" /> : null}\r\n      {children}\r\n    </button>\r\n  );\r\n});\r\n",
      "type": "registry:component"
    }
  ]
}