{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "return-focus",
  "title": "Return Focus",
  "description": "Helper used by the Get IT Done components.",
  "dependencies": [],
  "registryDependencies": [
    "https://zero-design.itdone.de/r/theme.json"
  ],
  "files": [
    {
      "path": "registry/lib/return-focus.tsx",
      "content": "'use client';\nimport * as React from 'react';\n\n// Focus return fallback for Radix dialog content. Radix returns focus to the Trigger only. With no Trigger (controlled `open`, a plain Button that sets state) focus would land on the body.\n// The opener is remembered when the content mounts and focused again on close, unless a Trigger already took focus or the consumer prevented the default in `onCloseAutoFocus`.\n\ntype RememberOpener = (element: HTMLElement | null) => void;\n\n// Rendered as a child of the dialog content, so its first render runs before Radix moves focus into the dialog.\nexport const OpenerCapture = ({ onCapture }: { onCapture: RememberOpener }) => {\n  const [opener] = React.useState<HTMLElement | null>(() => {\n    if (typeof document === 'undefined') return null;\n    const active = document.activeElement;\n    return active instanceof HTMLElement && active !== document.body ? active : null;\n  });\n  React.useEffect(() => {\n    onCapture(opener);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n  return null;\n};\n\n// An opener that was re-rendered with a new key while the overlay was open is a new element: the remembered one is detached. The new element is found again by id, else by tag, accessible name and text.\nexport const findReplacement = (old: HTMLElement | null): HTMLElement | null => {\n  if (!old) return null;\n  if (old.isConnected) return old;\n  if (old.id) {\n    const byId = document.getElementById(old.id);\n    if (byId) return byId;\n  }\n  const label = old.getAttribute('aria-label');\n  const text = old.textContent?.trim() ?? '';\n  if (!label && !text) return null;\n  const candidates = Array.from(document.querySelectorAll<HTMLElement>(old.tagName.toLowerCase()));\n  return candidates.find((candidate) => candidate.getAttribute('aria-label') === label && (candidate.textContent?.trim() ?? '') === text) ?? null;\n};\n\nexport const useReturnFocusFallback = (consumerHandler?: (event: Event) => void) => {\n  const opener = React.useRef<HTMLElement | null>(null);\n  const remember = React.useCallback<RememberOpener>((element) => {\n    opener.current = element;\n  }, []);\n  const onCloseAutoFocus = React.useCallback(\n    (event: Event) => {\n      consumerHandler?.(event);\n      if (event.defaultPrevented) return;\n      const target = findReplacement(opener.current);\n      // Radix runs its own trigger focus right after this handler, so the fallback waits for it.\n      queueMicrotask(() => {\n        const active = document.activeElement;\n        const lost = !active || active === document.body || !active.isConnected;\n        if (lost && target) target.focus();\n      });\n    },\n    [consumerHandler],\n  );\n  return { remember, onCloseAutoFocus };\n};\n",
      "type": "registry:lib"
    }
  ],
  "type": "registry:lib"
}