{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "send-button",
  "title": "Send button",
  "description": "A send button whose paper plane bobs, turns and flies forward on hover as the label slides out behind it, with a pending state that holds the plane still.",
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/items/send-button/send-button.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion, useReducedMotionConfig, type Variants } from \"motion/react\";\nimport type { LucideIcon } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface SendButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n  /** The resting label. Defaults to \"Send\". */\n  label?: React.ReactNode;\n  /** Optional leading icon, shown before the plane. */\n  icon?: LucideIcon;\n  /** Swaps the label for `pendingLabel`, holds the plane still and blocks input. */\n  pending?: boolean;\n  /** Label shown while `pending`. Defaults to \"Sending…\". */\n  pendingLabel?: React.ReactNode;\n  /**\n   * Surface. `ink` is translucent charcoal glass; `lead` is the structural\n   * accent, for placements sitting on light glass where ink reads as a hole.\n   */\n  tone?: \"ink\" | \"lead\";\n  /** Corners. `pill` is fully rounded; `lg` squares off to the theme radius. */\n  radius?: \"pill\" | \"lg\";\n}\n\nconst FLY = { duration: 0.3, ease: \"easeOut\" } as const;\n\n/** The plane's wrapper bobs while hovered; it settles when the pointer leaves. */\nconst bob: Variants = {\n  rest: { y: 0, transition: { duration: 0.2, ease: \"easeOut\" } },\n  hover: {\n    y: [\"0.1em\", \"-0.1em\"],\n    transition: {\n      duration: 0.6,\n      ease: \"easeInOut\",\n      repeat: Infinity,\n      repeatType: \"reverse\",\n    },\n  },\n};\n\n/** The plane itself turns to face its heading and drifts into the label's place. */\nconst flight: Variants = {\n  rest: { x: 0, rotate: 0, scale: 1, transition: FLY },\n  hover: { x: \"1.2em\", rotate: 45, scale: 1.1, transition: FLY },\n};\n\n/** The label slides out behind the button's clipped right edge. */\nconst slide: Variants = {\n  rest: { x: 0, transition: FLY },\n  hover: { x: \"5em\", transition: FLY },\n};\n\n/**\n * The send action: a button whose paper plane flies forward as the label\n * slides out behind it. Spend it on the one real \"this leaves the building\"\n * moment on a screen; the effect only means something if it is rare.\n *\n * It has a pending state because sending is a round trip that can fail: while\n * `pending`, the label reads `pendingLabel`, the plane holds still and the\n * button refuses presses.\n */\nexport function SendButton({\n  label = \"Send\",\n  icon: Icon,\n  pending = false,\n  pendingLabel = \"Sending…\",\n  tone = \"ink\",\n  radius = \"pill\",\n  className,\n  type = \"button\",\n  disabled,\n  onMouseEnter,\n  onMouseLeave,\n  ...props\n}: SendButtonProps) {\n  const reduceMotion = useReducedMotionConfig();\n  const [hovered, setHovered] = React.useState(false);\n\n  // Mid-send the plane holds still under the pending label, and reduced motion\n  // keeps it still always.\n  const state = hovered && !pending && !disabled && !reduceMotion ? \"hover\" : \"rest\";\n\n  return (\n    <button\n      type={type}\n      disabled={disabled || pending}\n      aria-busy={pending || undefined}\n      data-pending={pending ? \"\" : undefined}\n      onMouseEnter={(event) => {\n        setHovered(true);\n        onMouseEnter?.(event);\n      }}\n      onMouseLeave={(event) => {\n        setHovered(false);\n        onMouseLeave?.(event);\n      }}\n      className={cn(\n        \"relative inline-flex h-10 cursor-pointer touch-manipulation items-center justify-center gap-2 overflow-hidden px-5 font-body text-sm font-semibold tracking-[-0.02em] text-on-solid shadow-[inset_0_1px_0_color-mix(in_oklab,var(--on-solid)_30%,transparent)] ring-1 backdrop-blur-md transition-colors duration-200 select-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:outline-none active:scale-[0.98] disabled:pointer-events-none disabled:opacity-50\",\n        tone === \"lead\"\n          ? \"bg-lead ring-on-solid/20 hover:bg-lead-mid\"\n          : \"bg-ink/85 ring-on-solid/25 hover:bg-ink\",\n        radius === \"lg\" ? \"rounded-lg\" : \"rounded-full\",\n        className,\n      )}\n      {...props}\n    >\n      {Icon && <Icon aria-hidden=\"true\" className=\"size-4 shrink-0\" />}\n      <motion.span\n        aria-hidden=\"true\"\n        className=\"flex items-center\"\n        variants={bob}\n        initial={false}\n        animate={state}\n      >\n        <motion.svg\n          aria-hidden=\"true\"\n          viewBox=\"0 0 24 24\"\n          width={20}\n          height={20}\n          className=\"origin-center\"\n          variants={flight}\n          initial={false}\n          animate={state}\n        >\n          <path\n            fill=\"currentColor\"\n            d=\"M1.946 9.315c-.522-.174-.527-.455.01-.634l19.087-6.362c.529-.176.832.12.684.638l-5.454 19.086c-.15.529-.455.547-.679.045L12 14l6-8-8 6-8.054-2.685z\"\n          />\n        </motion.svg>\n      </motion.span>\n      <motion.span\n        className={cn(\"block\", Icon && \"ml-2\")}\n        variants={slide}\n        initial={false}\n        animate={state}\n      >\n        {pending ? pendingLabel : label}\n      </motion.span>\n    </button>\n  );\n}\n\nexport default SendButton;\n",
      "type": "registry:ui"
    }
  ],
  "meta": {
    "kind": "primitive",
    "whenToUse": "Use for the one action on a screen that sends something out of the app: an email, a message, an invitation. Do not use it for ordinary saves or submits; the flourish only means something if it is rare.",
    "origin": {
      "project": "180Connect",
      "path": "src/components/ui/send-button.tsx"
    },
    "credits": [
      {
        "name": "adamgiebl, \"Send\" paper plane button on Uiverse.io (hover choreography: fly-1 bob, 45° turn, label slide)",
        "url": "https://uiverse.io/adamgiebl",
        "license": "MIT"
      }
    ]
  },
  "type": "registry:ui"
}
