{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "true-focus", "type": "registry:ui", "title": "True Focus", "description": "A focus effect that blurs surrounding text and highlights the active word, with manual or auto modes.", "dependencies": ["framer-motion"], "devDependencies": [], "registryDependencies": [], "files": [ { "path": "components/ui/true-focus.tsx", "type": "registry:ui", "content": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport React, { useEffect, useRef, useState } from \"react\";\n\ninterface TrueFocusProps {\n sentence?: string;\n manualMode?: boolean;\n blurAmount?: number;\n borderColor?: string;\n glowColor?: string;\n animationDuration?: number;\n pauseBetweenAnimations?: number;\n}\n\nexport const TrueFocus = ({\n sentence = \"True Focus\",\n manualMode = false,\n blurAmount = 5,\n borderColor = \"green\",\n glowColor = \"rgba(0, 255, 0, 0.6)\",\n animationDuration = 0.5,\n pauseBetweenAnimations = 1,\n}: TrueFocusProps) => {\n const words = sentence.split(\" \");\n const [currentIndex, setCurrentIndex] = useState(0);\n const containerRef = useRef(null);\n const wordRefs = useRef<(HTMLSpanElement | null)[]>([]);\n const [focusRect, setFocusRect] = useState({ x: 0, y: 0, width: 0, height: 0 });\n\n useEffect(() => {\n if (!manualMode) {\n const interval = setInterval(() => {\n setCurrentIndex((prev) => (prev + 1) % words.length);\n }, (animationDuration + pauseBetweenAnimations) * 1000);\n\n return () => clearInterval(interval);\n }\n }, [manualMode, animationDuration, pauseBetweenAnimations, words.length]);\n\n useEffect(() => {\n if (currentIndex === null || currentIndex === -1) return;\n if (!wordRefs.current[currentIndex] || !containerRef.current) return;\n\n const parentRect = containerRef.current.getBoundingClientRect();\n const activeRect = wordRefs.current[currentIndex]!.getBoundingClientRect();\n\n setFocusRect({\n x: activeRect.left - parentRect.left,\n y: activeRect.top - parentRect.top,\n width: activeRect.width,\n height: activeRect.height,\n });\n }, [currentIndex, words.length]);\n\n const handleMouseEnter = (index: number) => {\n if (manualMode) {\n setCurrentIndex(index);\n }\n };\n\n const handleMouseLeave = () => {\n if (manualMode) {\n setCurrentIndex(-1);\n }\n };\n\n return (\n \n {words.map((word, index) => {\n const isActive = index === currentIndex;\n return (\n {\n wordRefs.current[index] = el;\n }}\n className=\"relative text-[3rem] font-black cursor-pointer\"\n style={{\n filter: manualMode\n ? isActive\n ? \"blur(0px)\"\n : `blur(${blurAmount}px)`\n : isActive\n ? \"blur(0px)\"\n : `blur(${blurAmount}px)`,\n transition: `filter ${animationDuration}s ease`,\n }}\n onMouseEnter={() => handleMouseEnter(index)}\n onMouseLeave={handleMouseLeave}\n >\n {word}\n \n );\n })}\n\n = 0 ? 1 : 0,\n }}\n transition={{\n duration: animationDuration,\n }}\n style={{\n \"--border-color\": borderColor,\n \"--glow-color\": glowColor,\n } as React.CSSProperties as any}\n >\n \n \n \n \n \n \n );\n};\n" } ], "tailwind": {}, "cssVars": {}, "meta": {} }