{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "inline-confirm",
  "title": "Inline Confirm",
  "description": "Feedback next to the control that was used, not in the corner: Copied, Saved.",
  "registryDependencies": [
    "https://sproutboat.com/ikada/r/theme.json",
    "https://sproutboat.com/ikada/r/cn.json",
    "https://sproutboat.com/ikada/r/icons.json"
  ],
  "files": [
    {
      "path": "registry/ikada/ui/inline-confirm.tsx",
      "content": "\"use client\";\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { cn } from \"@/components/ikada/lib/cn\";\nimport { CheckIcon } from \"@/components/ikada/lib/icons\";\n\n/** Confirmation state with a timer: `confirm()` turns `active` on, and it turns itself off after `duration` ms. */\nexport function useInlineConfirm(duration = 1600) {\n  const [active, setActive] = useState(false);\n  const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n  useEffect(() => () => clearTimeout(timer.current), []);\n  const confirm = useCallback(() => {\n    setActive(true);\n    clearTimeout(timer.current);\n    timer.current = setTimeout(() => setActive(false), duration);\n  }, [duration]);\n  return { active, confirm };\n}\n\nexport interface InlineConfirmProps extends Omit<ComponentProps<\"span\">, \"children\"> {\n  /** The word shown and announced: \"Copied\", \"Saved\". */\n  message: string;\n  /** Screen-reader text when it differs from the visible word, e.g. \"Copied to clipboard\". */\n  announce?: string;\n  /** The control. It receives `confirm`; call it when the action succeeded. */\n  children: (api: { confirm: () => void; active: boolean }) => ReactNode;\n  /** `bubble` floats above the control and never moves the layout; `inline` sits beside it in a reserved slot. */\n  appearance?: \"bubble\" | \"inline\";\n  /** Milliseconds the confirmation stays. Default 1600. */\n  duration?: number;\n}\n\n/**\n * Feedback next to the control that was used, not in the corner: Copied, Saved. It is a small primitive,\n * not a toast, so nothing is added to the notification history. A polite live region announces the word.\n */\nexport function InlineConfirm({\n  message,\n  announce,\n  children,\n  appearance = \"bubble\",\n  duration,\n  className,\n  ...props\n}: InlineConfirmProps) {\n  const { active, confirm } = useInlineConfirm(duration);\n  const mark = (\n    <>\n      <CheckIcon className=\"size-3.5 origin-bottom text-brand-fg group-data-open/confirm:animate-[sb-sprout_480ms_var(--ease-out)_both]\" />\n      {message}\n    </>\n  );\n  return (\n    <span\n      data-slot=\"inline-confirm\"\n      className={cn(\"relative inline-flex items-center\", appearance === \"inline\" && \"gap-2\", className)}\n      {...props}\n    >\n      {children({ confirm, active })}\n      {appearance === \"bubble\" ? (\n        <span\n          aria-hidden=\"true\"\n          data-open={active ? \"\" : undefined}\n          className={cn(\n            \"pointer-events-none absolute bottom-full start-1/2 z-(--sb-z-popover) mb-1.5 inline-flex -translate-x-1/2 items-center rtl:translate-x-1/2 gap-1 whitespace-nowrap rounded-lg bg-raised px-2 py-1 text-sb-xs font-medium text-fg shadow-float\",\n            \"group/confirm origin-bottom [transition:opacity_var(--sb-duration-popup-out)_var(--ease-out),translate_var(--sb-duration-popup-out)_var(--ease-out),scale_var(--sb-duration-popup-out)_var(--ease-out)]\",\n            \"translate-y-[calc(4px*var(--sb-move))] scale-(--sb-scale-popup) opacity-0\",\n            \"data-open:translate-y-0 data-open:scale-100 data-open:opacity-100\",\n            \"data-open:[transition:opacity_var(--sb-duration-popup-in)_var(--ease-out),translate_var(--sb-duration-spring-snappy)_var(--sb-spring-snappy),scale_var(--sb-duration-spring-snappy)_var(--sb-spring-snappy)]\",\n          )}\n        >\n          {mark}\n        </span>\n      ) : (\n        <span\n          aria-hidden=\"true\"\n          data-open={active ? \"\" : undefined}\n          className={cn(\n            \"group/confirm inline-flex min-w-16 items-center gap-1 text-sb-sm font-medium text-brand-fg\",\n            \"-translate-x-[calc(4px*var(--sb-move))] opacity-0 transition-[opacity,translate] duration-(--sb-duration-popup-in) ease-out\",\n            \"data-open:translate-x-0 data-open:opacity-100\",\n          )}\n        >\n          {mark}\n        </span>\n      )}\n      <span role=\"status\" className=\"sr-only\">\n        {active ? (announce ?? message) : \"\"}\n      </span>\n    </span>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@components/ikada/inline-confirm.tsx"
    }
  ],
  "categories": [
    "feedback"
  ],
  "type": "registry:ui"
}