{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"motion-hover","type":"registry:lib","title":"Motion hover","description":"The hover highlight motion menus, selects, tabs and segmented controls share: one pill, gliding without a blink.","dependencies":["motion"],"registryDependencies":["https://dashral.com/r/motion-tokens.json"],"files":[{"path":"registry/dashral/lib/motion-hover.ts","type":"registry:file","target":"components/dashral/lib/motion-hover.ts","content":"\"use client\"\n\n// The hover highlight motion menus, selects, tabs and segmented controls share: one pill, gliding without a blink.\n// It glides from item to item without going out between them. Its opacity is kept here, outside the pill, so moving to\n// the next item (a new pill there, by layoutId) doesn't start it from nothing; and letting go of an item waits a\n// moment, so passing from one to the next (or a menu moving focus to itself in between) never blinks it out.\n\nimport { useCallback, useEffect, useRef, useState } from \"react\"\nimport { animate, useMotionValue } from \"motion/react\"\n\nimport { duration } from \"./motion-tokens\"\n\nexport function useHoverPill<K>(reduced: boolean) {\n  const [hot, setHot] = useState<K | null>(null)\n  const opacity = useMotionValue(0)\n  const timer = useRef<ReturnType<typeof setTimeout>>(undefined)\n  useEffect(() => () => clearTimeout(timer.current), [])\n\n  /** The pointer or the keys are on this item: the pill goes there (gliding, if it was showing). */\n  const enter = useCallback(\n    (key: K) => {\n      clearTimeout(timer.current)\n      setHot(key)\n      animate(opacity, 1, { duration: reduced ? 0 : duration.fast })\n    },\n    [opacity, reduced]\n  )\n\n  /** Off an item: unless another takes the pill straight away, it fades where it is, then goes. */\n  const leave = useCallback(() => {\n    clearTimeout(timer.current)\n    timer.current = setTimeout(() => {\n      animate(opacity, 0, { duration: reduced ? 0 : duration.fast, onComplete: () => setHot(null) })\n    }, 60)\n  }, [opacity, reduced])\n\n  /** At once, as when the list closes. */\n  const clear = useCallback(() => {\n    clearTimeout(timer.current)\n    opacity.jump(0)\n    setHot(null)\n  }, [opacity])\n\n  return { hot, enter, leave, clear, opacity }\n}\n"}]}