Skip to content

Commit 650353c

Browse files
authored
Merge pull request #694 from KavanBhavsar35/feat/true-focus-words-prop
feat: add separator prop to TrueFocus component for customizable word separation
2 parents 09a2255 + 5d5925e commit 650353c

File tree

9 files changed

+21
-8
lines changed

9 files changed

+21
-8
lines changed

public/r/TrueFocus-JS-CSS.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"files": [
1111
{
1212
"path": "public/default/src/content/TextAnimations/TrueFocus/TrueFocus.jsx",
13-
"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 <div className=\"focus-container\" ref={containerRef}>\n {words.map((word, index) => {\n const isActive = index === currentIndex;\n return (\n <span\n key={index}\n ref={el => (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 </span>\n );\n })}\n\n <motion.div\n className=\"focus-frame\"\n animate={{\n x: focusRect.x,\n y: focusRect.y,\n width: focusRect.width,\n height: focusRect.height,\n opacity: currentIndex >= 0 ? 1 : 0\n }}\n transition={{\n duration: animationDuration\n }}\n style={{\n '--border-color': borderColor,\n '--glow-color': glowColor\n }}\n >\n <span className=\"corner top-left\"></span>\n <span className=\"corner top-right\"></span>\n <span className=\"corner bottom-left\"></span>\n <span className=\"corner bottom-right\"></span>\n </motion.div>\n </div>\n );\n};\n\nexport default TrueFocus;\n",
13+
"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 <div className=\"focus-container\" ref={containerRef}>\n {words.map((word, index) => {\n const isActive = index === currentIndex;\n return (\n <span\n key={index}\n ref={el => (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 </span>\n );\n })}\n\n <motion.div\n className=\"focus-frame\"\n animate={{\n x: focusRect.x,\n y: focusRect.y,\n width: focusRect.width,\n height: focusRect.height,\n opacity: currentIndex >= 0 ? 1 : 0\n }}\n transition={{\n duration: animationDuration\n }}\n style={{\n '--border-color': borderColor,\n '--glow-color': glowColor\n }}\n >\n <span className=\"corner top-left\"></span>\n <span className=\"corner top-right\"></span>\n <span className=\"corner bottom-left\"></span>\n <span className=\"corner bottom-right\"></span>\n </motion.div>\n </div>\n );\n};\n\nexport default TrueFocus;\n",
1414
"type": "registry:component"
1515
},
1616
{

public/r/TrueFocus-JS-TW.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"files": [
1111
{
1212
"path": "public/tailwind/src/tailwind/TextAnimations/TrueFocus/TrueFocus.jsx",
13-
"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 <div className=\"relative flex gap-4 justify-center items-center flex-wrap\" ref={containerRef}>\n {words.map((word, index) => {\n const isActive = index === currentIndex;\n return (\n <span\n key={index}\n ref={el => (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 </span>\n );\n })}\n\n <motion.div\n className=\"absolute top-0 left-0 pointer-events-none box-border border-0\"\n animate={{\n x: focusRect.x,\n y: focusRect.y,\n width: focusRect.width,\n height: focusRect.height,\n opacity: currentIndex >= 0 ? 1 : 0\n }}\n transition={{\n duration: animationDuration\n }}\n style={{\n '--border-color': borderColor,\n '--glow-color': glowColor\n }}\n >\n <span\n className=\"absolute w-4 h-4 border-[3px] rounded-[3px] top-[-10px] left-[-10px] border-r-0 border-b-0\"\n style={{\n borderColor: 'var(--border-color)',\n filter: 'drop-shadow(0 0 4px var(--border-color))'\n }}\n ></span>\n <span\n className=\"absolute w-4 h-4 border-[3px] rounded-[3px] top-[-10px] right-[-10px] border-l-0 border-b-0\"\n style={{\n borderColor: 'var(--border-color)',\n filter: 'drop-shadow(0 0 4px var(--border-color))'\n }}\n ></span>\n <span\n className=\"absolute w-4 h-4 border-[3px] rounded-[3px] bottom-[-10px] left-[-10px] border-r-0 border-t-0\"\n style={{\n borderColor: 'var(--border-color)',\n filter: 'drop-shadow(0 0 4px var(--border-color))'\n }}\n ></span>\n <span\n className=\"absolute w-4 h-4 border-[3px] rounded-[3px] bottom-[-10px] right-[-10px] border-l-0 border-t-0\"\n style={{\n borderColor: 'var(--border-color)',\n filter: 'drop-shadow(0 0 4px var(--border-color))'\n }}\n ></span>\n </motion.div>\n </div>\n );\n};\n\nexport default TrueFocus;\n",
13+
"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 <div className=\"focus-container\" ref={containerRef}>\n {words.map((word, index) => {\n const isActive = index === currentIndex;\n return (\n <span\n key={index}\n ref={el => (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 </span>\n );\n })}\n\n <motion.div\n className=\"focus-frame\"\n animate={{\n x: focusRect.x,\n y: focusRect.y,\n width: focusRect.width,\n height: focusRect.height,\n opacity: currentIndex >= 0 ? 1 : 0\n }}\n transition={{\n duration: animationDuration\n }}\n style={{\n '--border-color': borderColor,\n '--glow-color': glowColor\n }}\n >\n <span className=\"corner top-left\"></span>\n <span className=\"corner top-right\"></span>\n <span className=\"corner bottom-left\"></span>\n <span className=\"corner bottom-right\"></span>\n </motion.div>\n </div>\n );\n};\n\nexport default TrueFocus;\n",
1414
"type": "registry:component"
1515
}
1616
]

0 commit comments

Comments
 (0)