From 8d62933c86c6799cc155f1ae0e8db71d02d84671 Mon Sep 17 00:00:00 2001 From: KavanBhavsar35 Date: Wed, 12 Nov 2025 22:36:03 +0530 Subject: [PATCH] feat(true-focus): add support for custom words array prop for fine spacing control --- public/r/TrueFocus-JS-CSS.json | 2 +- public/r/TrueFocus-JS-TW.json | 2 +- public/r/TrueFocus-TS-CSS.json | 2 +- public/r/TrueFocus-TS-TW.json | 2 +- src/content/TextAnimations/TrueFocus/TrueFocus.jsx | 3 ++- src/demo/TextAnimations/TrueFocusDemo.jsx | 7 +++++++ src/tailwind/TextAnimations/TrueFocus/TrueFocus.jsx | 3 ++- src/ts-default/TextAnimations/TrueFocus/TrueFocus.tsx | 4 +++- src/ts-tailwind/TextAnimations/TrueFocus/TrueFocus.tsx | 4 +++- 9 files changed, 21 insertions(+), 8 deletions(-) diff --git a/public/r/TrueFocus-JS-CSS.json b/public/r/TrueFocus-JS-CSS.json index 11873834..8634ec1e 100644 --- a/public/r/TrueFocus-JS-CSS.json +++ b/public/r/TrueFocus-JS-CSS.json @@ -10,7 +10,7 @@ "files": [ { "path": "public/default/src/content/TextAnimations/TrueFocus/TrueFocus.jsx", - "content": "import { useEffect, useRef, useState } from 'react';\nimport { motion } from 'motion/react';\nimport './TrueFocus.css';\n\nconst 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}) => {\n const words = sentence.split(' ');\n const [currentIndex, setCurrentIndex] = useState(0);\n const [lastActiveIndex, setLastActiveIndex] = useState(null);\n const containerRef = useRef(null);\n const wordRefs = useRef([]);\n const [focusRect, setFocusRect] = useState({ x: 0, y: 0, width: 0, height: 0 });\n\n useEffect(() => {\n if (!manualMode) {\n const interval = setInterval(\n () => {\n setCurrentIndex(prev => (prev + 1) % words.length);\n },\n (animationDuration + pauseBetweenAnimations) * 1000\n );\n\n return () => clearInterval(interval);\n }\n }, [manualMode, animationDuration, pauseBetweenAnimations, words.length]);\n\n useEffect(() => {\n if (currentIndex === null || currentIndex === -1) return;\n\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 => {\n if (manualMode) {\n setLastActiveIndex(index);\n setCurrentIndex(index);\n }\n };\n\n const handleMouseLeave = () => {\n if (manualMode) {\n setCurrentIndex(lastActiveIndex);\n }\n };\n\n return (\n
\n {words.map((word, index) => {\n const isActive = index === currentIndex;\n return (\n (wordRefs.current[index] = el)}\n className={`focus-word ${manualMode ? 'manual' : ''} ${isActive && !manualMode ? 'active' : ''}`}\n style={{\n filter: manualMode\n ? isActive\n ? `blur(0px)`\n : `blur(${blurAmount}px)`\n : isActive\n ? `blur(0px)`\n : `blur(${blurAmount}px)`,\n '--border-color': borderColor,\n '--glow-color': glowColor,\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 }}\n >\n \n \n \n \n \n
\n );\n};\n\nexport default TrueFocus;\n", + "content": "import { useEffect, useRef, useState } from 'react';\nimport { motion } from 'motion/react';\nimport './TrueFocus.css';\n\nconst TrueFocus = ({\n sentence = 'True Focus',\nseparator = ' ',\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}) => {\n const words = sentence.split(separator);\n const [currentIndex, setCurrentIndex] = useState(0);\n const [lastActiveIndex, setLastActiveIndex] = useState(null);\n const containerRef = useRef(null);\n const wordRefs = useRef([]);\n const [focusRect, setFocusRect] = useState({ x: 0, y: 0, width: 0, height: 0 });\n\n useEffect(() => {\n if (!manualMode) {\n const interval = setInterval(\n () => {\n setCurrentIndex(prev => (prev + 1) % words.length);\n },\n (animationDuration + pauseBetweenAnimations) * 1000\n );\n\n return () => clearInterval(interval);\n }\n }, [manualMode, animationDuration, pauseBetweenAnimations, words.length]);\n\n useEffect(() => {\n if (currentIndex === null || currentIndex === -1) return;\n\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 => {\n if (manualMode) {\n setLastActiveIndex(index);\n setCurrentIndex(index);\n }\n };\n\n const handleMouseLeave = () => {\n if (manualMode) {\n setCurrentIndex(lastActiveIndex);\n }\n };\n\n return (\n
\n {words.map((word, index) => {\n const isActive = index === currentIndex;\n return (\n (wordRefs.current[index] = el)}\n className={`focus-word ${manualMode ? 'manual' : ''} ${isActive && !manualMode ? 'active' : ''}`}\n style={{\n filter: manualMode\n ? isActive\n ? `blur(0px)`\n : `blur(${blurAmount}px)`\n : isActive\n ? `blur(0px)`\n : `blur(${blurAmount}px)`,\n '--border-color': borderColor,\n '--glow-color': glowColor,\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 }}\n >\n \n \n \n \n \n
\n );\n};\n\nexport default TrueFocus;\n", "type": "registry:component" }, { diff --git a/public/r/TrueFocus-JS-TW.json b/public/r/TrueFocus-JS-TW.json index 77696c63..b7b0d4a5 100644 --- a/public/r/TrueFocus-JS-TW.json +++ b/public/r/TrueFocus-JS-TW.json @@ -10,7 +10,7 @@ "files": [ { "path": "public/tailwind/src/tailwind/TextAnimations/TrueFocus/TrueFocus.jsx", - "content": "import { useEffect, useRef, useState } from 'react';\nimport { motion } from 'motion/react';\n\nconst 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}) => {\n const words = sentence.split(' ');\n const [currentIndex, setCurrentIndex] = useState(0);\n const [lastActiveIndex, setLastActiveIndex] = useState(null);\n const containerRef = useRef(null);\n const wordRefs = useRef([]);\n const [focusRect, setFocusRect] = useState({ x: 0, y: 0, width: 0, height: 0 });\n\n useEffect(() => {\n if (!manualMode) {\n const interval = setInterval(\n () => {\n setCurrentIndex(prev => (prev + 1) % words.length);\n },\n (animationDuration + pauseBetweenAnimations) * 1000\n );\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 => {\n if (manualMode) {\n setLastActiveIndex(index);\n setCurrentIndex(index);\n }\n };\n\n const handleMouseLeave = () => {\n if (manualMode) {\n setCurrentIndex(lastActiveIndex);\n }\n };\n\n return (\n
\n {words.map((word, index) => {\n const isActive = index === currentIndex;\n return (\n (wordRefs.current[index] = el)}\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 '--border-color': borderColor,\n '--glow-color': glowColor,\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 }}\n >\n \n \n \n \n \n
\n );\n};\n\nexport default TrueFocus;\n", + "content": "import { useEffect, useRef, useState } from 'react';\nimport { motion } from 'motion/react';\nimport './TrueFocus.css';\n\nconst TrueFocus = ({\n sentence = 'True Focus',\nseparator = ' ',\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}) => {\n const words = sentence.split(separator);\n const [currentIndex, setCurrentIndex] = useState(0);\n const [lastActiveIndex, setLastActiveIndex] = useState(null);\n const containerRef = useRef(null);\n const wordRefs = useRef([]);\n const [focusRect, setFocusRect] = useState({ x: 0, y: 0, width: 0, height: 0 });\n\n useEffect(() => {\n if (!manualMode) {\n const interval = setInterval(\n () => {\n setCurrentIndex(prev => (prev + 1) % words.length);\n },\n (animationDuration + pauseBetweenAnimations) * 1000\n );\n\n return () => clearInterval(interval);\n }\n }, [manualMode, animationDuration, pauseBetweenAnimations, words.length]);\n\n useEffect(() => {\n if (currentIndex === null || currentIndex === -1) return;\n\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 => {\n if (manualMode) {\n setLastActiveIndex(index);\n setCurrentIndex(index);\n }\n };\n\n const handleMouseLeave = () => {\n if (manualMode) {\n setCurrentIndex(lastActiveIndex);\n }\n };\n\n return (\n
\n {words.map((word, index) => {\n const isActive = index === currentIndex;\n return (\n (wordRefs.current[index] = el)}\n className={`focus-word ${manualMode ? 'manual' : ''} ${isActive && !manualMode ? 'active' : ''}`}\n style={{\n filter: manualMode\n ? isActive\n ? `blur(0px)`\n : `blur(${blurAmount}px)`\n : isActive\n ? `blur(0px)`\n : `blur(${blurAmount}px)`,\n '--border-color': borderColor,\n '--glow-color': glowColor,\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 }}\n >\n \n \n \n \n \n
\n );\n};\n\nexport default TrueFocus;\n", "type": "registry:component" } ] diff --git a/public/r/TrueFocus-TS-CSS.json b/public/r/TrueFocus-TS-CSS.json index 282cc1fe..480138bc 100644 --- a/public/r/TrueFocus-TS-CSS.json +++ b/public/r/TrueFocus-TS-CSS.json @@ -10,7 +10,7 @@ "files": [ { "path": "public/ts/default/src/ts-default/TextAnimations/TrueFocus/TrueFocus.tsx", - "content": "import { useEffect, useRef, useState, RefObject } from 'react';\nimport { motion } from 'motion/react';\nimport './TrueFocus.css';\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\ninterface FocusRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nconst TrueFocus: React.FC = ({\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}) => {\n const words = sentence.split(' ');\n const [currentIndex, setCurrentIndex] = useState(0);\n const [lastActiveIndex, setLastActiveIndex] = useState(null);\n const containerRef = useRef(null);\n const wordRefs: React.MutableRefObject<(HTMLSpanElement | null)[]> = useRef([]);\n const [focusRect, setFocusRect] = useState({\n x: 0,\n y: 0,\n width: 0,\n height: 0\n });\n\n useEffect(() => {\n if (!manualMode) {\n const interval = setInterval(\n () => {\n setCurrentIndex(prev => (prev + 1) % words.length);\n },\n (animationDuration + pauseBetweenAnimations) * 1000\n );\n\n return () => clearInterval(interval);\n }\n }, [manualMode, animationDuration, pauseBetweenAnimations, words.length]);\n\n useEffect(() => {\n if (currentIndex === null || currentIndex === -1) return;\n\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 setLastActiveIndex(index);\n setCurrentIndex(index);\n }\n };\n\n const handleMouseLeave = () => {\n if (manualMode) {\n setCurrentIndex(lastActiveIndex ?? 0);\n }\n };\n\n return (\n
\n {words.map((word, index) => {\n const isActive = index === currentIndex;\n return (\n {\n if (el) {\n wordRefs.current[index] = el;\n }\n }}\n className={`focus-word ${manualMode ? 'manual' : ''} ${isActive && !manualMode ? 'active' : ''}`}\n style={\n {\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 '--border-color': borderColor,\n '--glow-color': glowColor\n } as React.CSSProperties\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 {\n '--border-color': borderColor,\n '--glow-color': glowColor\n } as React.CSSProperties\n }\n >\n \n \n \n \n \n
\n );\n};\n\nexport default TrueFocus;\n", + "content": "import { useEffect, useRef, useState, RefObject } from 'react';\nimport { motion } from 'motion/react';\nimport './TrueFocus.css';\n\ninterface TrueFocusProps {\n sentence?: string;\nseparator?: string;\n manualMode?: boolean;\n blurAmount?: number;\n borderColor?: string;\n glowColor?: string;\n animationDuration?: number;\n pauseBetweenAnimations?: number;\n}\n\ninterface FocusRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nconst TrueFocus: React.FC = ({\n sentence = 'True Focus',\nseparator = ' ',\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}) => {\n const words = sentence.split(separator);\n const [currentIndex, setCurrentIndex] = useState(0);\n const [lastActiveIndex, setLastActiveIndex] = useState(null);\n const containerRef = useRef(null);\n const wordRefs: React.MutableRefObject<(HTMLSpanElement | null)[]> = useRef([]);\n const [focusRect, setFocusRect] = useState({\n x: 0,\n y: 0,\n width: 0,\n height: 0\n });\n\n useEffect(() => {\n if (!manualMode) {\n const interval = setInterval(\n () => {\n setCurrentIndex(prev => (prev + 1) % words.length);\n },\n (animationDuration + pauseBetweenAnimations) * 1000\n );\n\n return () => clearInterval(interval);\n }\n }, [manualMode, animationDuration, pauseBetweenAnimations, words.length]);\n\n useEffect(() => {\n if (currentIndex === null || currentIndex === -1) return;\n\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 setLastActiveIndex(index);\n setCurrentIndex(index);\n }\n };\n\n const handleMouseLeave = () => {\n if (manualMode) {\n setCurrentIndex(lastActiveIndex ?? 0);\n }\n };\n\n return (\n
\n {words.map((word, index) => {\n const isActive = index === currentIndex;\n return (\n {\n if (el) {\n wordRefs.current[index] = el;\n }\n }}\n className={`focus-word ${manualMode ? 'manual' : ''} ${isActive && !manualMode ? 'active' : ''}`}\n style={\n {\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 '--border-color': borderColor,\n '--glow-color': glowColor\n } as React.CSSProperties\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 {\n '--border-color': borderColor,\n '--glow-color': glowColor\n } as React.CSSProperties\n }\n >\n \n \n \n \n \n
\n );\n};\n\nexport default TrueFocus;\n", "type": "registry:component" }, { diff --git a/public/r/TrueFocus-TS-TW.json b/public/r/TrueFocus-TS-TW.json index 1367c5c5..088f20d1 100644 --- a/public/r/TrueFocus-TS-TW.json +++ b/public/r/TrueFocus-TS-TW.json @@ -10,7 +10,7 @@ "files": [ { "path": "public/ts/tailwind/src/ts-tailwind/TextAnimations/TrueFocus/TrueFocus.tsx", - "content": "import { useEffect, useRef, useState } from 'react';\nimport { motion } from 'motion/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\ninterface FocusRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nconst TrueFocus: React.FC = ({\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}) => {\n const words = sentence.split(' ');\n const [currentIndex, setCurrentIndex] = useState(0);\n const [lastActiveIndex, setLastActiveIndex] = useState(null);\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 () => {\n setCurrentIndex(prev => (prev + 1) % words.length);\n },\n (animationDuration + pauseBetweenAnimations) * 1000\n );\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 setLastActiveIndex(index);\n setCurrentIndex(index);\n }\n };\n\n const handleMouseLeave = () => {\n if (manualMode) {\n setCurrentIndex(lastActiveIndex!);\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 {\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 } as React.CSSProperties\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 {\n '--border-color': borderColor,\n '--glow-color': glowColor\n } as React.CSSProperties\n }\n >\n \n \n \n \n \n
\n );\n};\n\nexport default TrueFocus;\n", + "content": "import { useEffect, useRef, useState, RefObject } from 'react';\nimport { motion } from 'motion/react';\nimport './TrueFocus.css';\n\ninterface TrueFocusProps {\n sentence?: string;\nseparator?: string;\n manualMode?: boolean;\n blurAmount?: number;\n borderColor?: string;\n glowColor?: string;\n animationDuration?: number;\n pauseBetweenAnimations?: number;\n}\n\ninterface FocusRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nconst TrueFocus: React.FC = ({\n sentence = 'True Focus',\nseparator = ' ',\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}) => {\n const words = sentence.split(separator);\n const [currentIndex, setCurrentIndex] = useState(0);\n const [lastActiveIndex, setLastActiveIndex] = useState(null);\n const containerRef = useRef(null);\n const wordRefs: React.MutableRefObject<(HTMLSpanElement | null)[]> = useRef([]);\n const [focusRect, setFocusRect] = useState({\n x: 0,\n y: 0,\n width: 0,\n height: 0\n });\n\n useEffect(() => {\n if (!manualMode) {\n const interval = setInterval(\n () => {\n setCurrentIndex(prev => (prev + 1) % words.length);\n },\n (animationDuration + pauseBetweenAnimations) * 1000\n );\n\n return () => clearInterval(interval);\n }\n }, [manualMode, animationDuration, pauseBetweenAnimations, words.length]);\n\n useEffect(() => {\n if (currentIndex === null || currentIndex === -1) return;\n\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 setLastActiveIndex(index);\n setCurrentIndex(index);\n }\n };\n\n const handleMouseLeave = () => {\n if (manualMode) {\n setCurrentIndex(lastActiveIndex ?? 0);\n }\n };\n\n return (\n
\n {words.map((word, index) => {\n const isActive = index === currentIndex;\n return (\n {\n if (el) {\n wordRefs.current[index] = el;\n }\n }}\n className={`focus-word ${manualMode ? 'manual' : ''} ${isActive && !manualMode ? 'active' : ''}`}\n style={\n {\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 '--border-color': borderColor,\n '--glow-color': glowColor\n } as React.CSSProperties\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 {\n '--border-color': borderColor,\n '--glow-color': glowColor\n } as React.CSSProperties\n }\n >\n \n \n \n \n \n
\n );\n};\n\nexport default TrueFocus;\n", "type": "registry:component" } ] diff --git a/src/content/TextAnimations/TrueFocus/TrueFocus.jsx b/src/content/TextAnimations/TrueFocus/TrueFocus.jsx index 578f5526..3d1fa892 100644 --- a/src/content/TextAnimations/TrueFocus/TrueFocus.jsx +++ b/src/content/TextAnimations/TrueFocus/TrueFocus.jsx @@ -4,6 +4,7 @@ import './TrueFocus.css'; const TrueFocus = ({ sentence = 'True Focus', + separator = ' ', manualMode = false, blurAmount = 5, borderColor = 'green', @@ -11,7 +12,7 @@ const TrueFocus = ({ animationDuration = 0.5, pauseBetweenAnimations = 1 }) => { - const words = sentence.split(' '); + const words = sentence.split(separator); const [currentIndex, setCurrentIndex] = useState(0); const [lastActiveIndex, setLastActiveIndex] = useState(null); const containerRef = useRef(null); diff --git a/src/demo/TextAnimations/TrueFocusDemo.jsx b/src/demo/TextAnimations/TrueFocusDemo.jsx index b700bff1..ec032ddf 100644 --- a/src/demo/TextAnimations/TrueFocusDemo.jsx +++ b/src/demo/TextAnimations/TrueFocusDemo.jsx @@ -36,6 +36,13 @@ const TrueFocusDemo = () => { default: "'True Focus'", description: 'The text to display with the focus animation.' }, + { + name: 'separator', + type: 'string', + default: "' '", + description: + 'Optional string used to separate words in the sentence.' + }, { name: 'manualMode', type: 'boolean', diff --git a/src/tailwind/TextAnimations/TrueFocus/TrueFocus.jsx b/src/tailwind/TextAnimations/TrueFocus/TrueFocus.jsx index f3dc7366..badc9382 100644 --- a/src/tailwind/TextAnimations/TrueFocus/TrueFocus.jsx +++ b/src/tailwind/TextAnimations/TrueFocus/TrueFocus.jsx @@ -3,6 +3,7 @@ import { motion } from 'motion/react'; const TrueFocus = ({ sentence = 'True Focus', + separator = ' ', manualMode = false, blurAmount = 5, borderColor = 'green', @@ -10,7 +11,7 @@ const TrueFocus = ({ animationDuration = 0.5, pauseBetweenAnimations = 1 }) => { - const words = sentence.split(' '); + const words = sentence.split(separator); const [currentIndex, setCurrentIndex] = useState(0); const [lastActiveIndex, setLastActiveIndex] = useState(null); const containerRef = useRef(null); diff --git a/src/ts-default/TextAnimations/TrueFocus/TrueFocus.tsx b/src/ts-default/TextAnimations/TrueFocus/TrueFocus.tsx index a30d7e88..ab0112e3 100644 --- a/src/ts-default/TextAnimations/TrueFocus/TrueFocus.tsx +++ b/src/ts-default/TextAnimations/TrueFocus/TrueFocus.tsx @@ -4,6 +4,7 @@ import './TrueFocus.css'; interface TrueFocusProps { sentence?: string; + separator?: string; manualMode?: boolean; blurAmount?: number; borderColor?: string; @@ -21,6 +22,7 @@ interface FocusRect { const TrueFocus: React.FC = ({ sentence = 'True Focus', + separator = ' ', manualMode = false, blurAmount = 5, borderColor = 'green', @@ -28,7 +30,7 @@ const TrueFocus: React.FC = ({ animationDuration = 0.5, pauseBetweenAnimations = 1 }) => { - const words = sentence.split(' '); + const words = sentence.split(separator); const [currentIndex, setCurrentIndex] = useState(0); const [lastActiveIndex, setLastActiveIndex] = useState(null); const containerRef = useRef(null); diff --git a/src/ts-tailwind/TextAnimations/TrueFocus/TrueFocus.tsx b/src/ts-tailwind/TextAnimations/TrueFocus/TrueFocus.tsx index 719d52cd..dc73614a 100644 --- a/src/ts-tailwind/TextAnimations/TrueFocus/TrueFocus.tsx +++ b/src/ts-tailwind/TextAnimations/TrueFocus/TrueFocus.tsx @@ -3,6 +3,7 @@ import { motion } from 'motion/react'; interface TrueFocusProps { sentence?: string; + separator?: string; manualMode?: boolean; blurAmount?: number; borderColor?: string; @@ -20,6 +21,7 @@ interface FocusRect { const TrueFocus: React.FC = ({ sentence = 'True Focus', + separator = ' ', manualMode = false, blurAmount = 5, borderColor = 'green', @@ -27,7 +29,7 @@ const TrueFocus: React.FC = ({ animationDuration = 0.5, pauseBetweenAnimations = 1 }) => { - const words = sentence.split(' '); + const words = sentence.split(separator); const [currentIndex, setCurrentIndex] = useState(0); const [lastActiveIndex, setLastActiveIndex] = useState(null); const containerRef = useRef(null);