{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "confirm-button",
  "type": "registry:ui",
  "title": "ConfirmButton",
  "description": "Tap-again-to-confirm button for a destructive or disruptive action, with the armed state announced to assistive tech.",
  "categories": [
    "actions"
  ],
  "registryDependencies": [
    "https://whiskeyjack.net/r/announce.json",
    "https://whiskeyjack.net/r/button.json",
    "https://whiskeyjack.net/r/use-input-type.json"
  ],
  "files": [
    {
      "path": "components/ui/confirm-button.tsx",
      "type": "registry:ui",
      "target": "components/ui/confirm-button.tsx",
      "content": "import * as React from 'react'\nimport { Button, type ButtonProps } from '@/components/ui/button'\nimport { useInputType } from '@/hooks/use-input-type'\nimport { announce } from '@/lib/announce'\n\nexport interface ConfirmButtonProps\n  extends Omit<ButtonProps, 'variant' | 'onClick' | 'children'> {\n  /**\n   * Label shown in the default (un-armed) state. A node rather than a string,\n   * so a button that carried an icon beside its text keeps it when it gains\n   * the confirm step; the two confirm labels stay strings because they are\n   * what gets announced.\n   */\n  label: React.ReactNode\n  /**\n   * Button variant, `destructive` by default. Set it for an action that is\n   * disruptive rather than destructive -- signing out ends a session and\n   * deserves the same tap-again guard, while red would overstate it.\n   */\n  variant?: ButtonProps['variant']\n  /**\n   * Label shown after the first tap/click arms the button (touch input).\n   * Typically the translated form of \"Tap again to confirm\".\n   */\n  touchConfirmLabel: string\n  /**\n   * Label shown after the first click arms the button (mouse/keyboard input).\n   * Typically the translated form of \"Click again to confirm\".\n   */\n  mouseConfirmLabel: string\n  /** Called only when the button is tapped/clicked a second time while armed. */\n  onConfirm: () => void\n  /**\n   * Controls the armed state from outside, e.g. to reset it when a drawer\n   * closes. When provided the component is controlled; when omitted it manages\n   * its own state.\n   */\n  armed?: boolean\n  /** Called when the internal armed state changes (controlled usage). */\n  onArmedChange?: (armed: boolean) => void\n}\n\n/**\n * Destructive action button with a tap-again-to-confirm pattern.\n *\n * Tap/click once to arm it (label switches to confirm prompt). Tap/click again\n * to execute `onConfirm`. The armed state is managed internally unless you pass\n * `armed` + `onArmedChange` for controlled usage (e.g., to reset it when the\n * enclosing drawer closes).\n *\n * Input type detection via `useInputType` automatically picks between\n * `touchConfirmLabel` and `mouseConfirmLabel`.\n *\n * Built on the DS `Button`, `variant=\"destructive\"` unless `variant` says\n * otherwise.\n */\nexport const ConfirmButton = React.forwardRef<HTMLButtonElement, ConfirmButtonProps>(function ConfirmButton({\n  label,\n  touchConfirmLabel,\n  mouseConfirmLabel,\n  onConfirm,\n  variant = 'destructive',\n  armed: controlledArmed,\n  onArmedChange,\n  ...rest\n}, ref) {\n  const inputType = useInputType()\n  const [internalArmed, setInternalArmed] = React.useState(false)\n\n  const isControlled = controlledArmed !== undefined\n  const armed = isControlled ? controlledArmed : internalArmed\n\n  const setArmed = React.useCallback(\n    (next: boolean) => {\n      if (!isControlled) setInternalArmed(next)\n      onArmedChange?.(next)\n    },\n    [isControlled, onArmedChange]\n  )\n\n  const confirmLabel =\n    inputType === 'touch' ? touchConfirmLabel : mouseConfirmLabel\n\n  const handleClick = () => {\n    if (armed) {\n      onConfirm()\n      setArmed(false)\n    } else {\n      setArmed(true)\n      // Announce the confirm prompt assertively so screen-reader users know\n      // the next activation will execute the destructive action.\n      announce(confirmLabel, 'assertive')\n    }\n  }\n\n  return (\n    <Button ref={ref} variant={variant} onClick={handleClick} {...rest}>\n      {armed ? confirmLabel : label}\n    </Button>\n  )\n})\n\nConfirmButton.displayName = \"ConfirmButton\"\n"
    }
  ],
  "docs": "Use ConfirmButton for an inline destructive action where a modal would be heavy: delete a row, clear data, reset a setting. The first tap arms it and the second commits. Keep the confirm label explicit about what is about to happen. Pass controlled armed/onArmedChange when sibling actions must be mutually exclusive. It is `destructive` by default; pass `variant` for an action that is disruptive rather than destructive -- signing out earns the same guard while red would overstate it. `label` takes a node, so a button that carried an icon keeps it.",
  "meta": {
    "group": "actions",
    "related": [
      "drawer-action",
      "button"
    ],
    "exports": [
      "ConfirmButton"
    ],
    "siteSlug": "confirm-button"
  }
}
