{
    "name": "draw-line-text",
    "title": "Draw Line Text",
    "description": "A smooth text effect that draws characters as strokes and fills them in—perfect for headers and highlights.",
    "type": "registry:ui",
    "dependencies": ["gsap", "@gsap/react"],
    "files": [
        {
            "path": "components/gsap/draw-line-text.tsx",
            "type": "registry:ui",
            "target": "~/components/gsap/draw-line-text.tsx",
            "content": "\"use client\";\n\nimport { ComponentProps, useRef, useState } from \"react\";\n\nimport { useGSAP } from \"@gsap/react\";\nimport gsap from \"gsap\";\n\ntype DrawTextProps = {\n    afterFill?: boolean;\n    color?: string;\n    fontSize?: number;\n    letterSpacing?: number;\n    oneByOne?: boolean;\n    strokeWidth?: number;\n    text: string;\n    wordSpacing?: number;\n} & ComponentProps<\"svg\">;\n\nexport const DrawLineText = ({\n    text,\n    oneByOne = true,\n    afterFill = true,\n    color = \"black\",\n    fontSize = 40,\n    wordSpacing = 10,\n    strokeWidth = 1,\n    letterSpacing = 0,\n    ...props\n}: DrawTextProps) => {\n    const wrapperRef = useRef<SVGSVGElement | null>(null);\n\n    const [textDimension, setTextDimension] = useState<{ height: number; width: number }>({ height: 0, width: 0 });\n\n    useGSAP(\n        () => {\n            const wrapperChildren = wrapperRef.current?.children;\n            if (!wrapperChildren) return;\n            const children = Array.from(wrapperChildren) as SVGTextElement[];\n            let totalWidth = 0;\n            let maxHeight = 0;\n            children.forEach((el, index) => {\n                el.setAttribute(\"x\", totalWidth + \"px\");\n                const elementWidth = el.getBoundingClientRect().width;\n                const elementHeight = el.getBoundingClientRect().height;\n                if (elementHeight > maxHeight) {\n                    maxHeight = elementHeight;\n                }\n                totalWidth +=\n                    +(elementWidth == 0 ? wordSpacing : elementWidth) +\n                    (children.length - 1 != index ? letterSpacing : 0);\n                const length = el.getComputedTextLength() * 8;\n                el.style.strokeDasharray = length + \"px\";\n                el.style.strokeDashoffset = length + \"px\";\n            });\n            setTextDimension({ width: totalWidth, height: maxHeight });\n\n            const textChildren = children.filter((el) => el.getBoundingClientRect().width != 0);\n\n            const tl = gsap.timeline();\n            tl.to(textChildren, {\n                strokeDashoffset: 0,\n                duration: 2.5,\n                ease: \"linear\",\n                stagger: oneByOne ? 0.8 : 0,\n            });\n            if (afterFill) {\n                tl.to(textChildren, {\n                    fillOpacity: 1,\n                    duration: 0.6,\n                    ease: \"power4.in\",\n                    stagger: {\n                        amount: 0.2,\n                        from: \"center\",\n                    },\n                });\n            }\n        },\n        { scope: wrapperRef, dependencies: [text] },\n    );\n\n    return (\n        <svg\n            {...props}\n            ref={wrapperRef}\n            style={{\n                userSelect: \"none\",\n                width: textDimension.width + \"px\",\n                height: textDimension.height * 1.03 + \"px\",\n            }}>\n            {text.split(\"\").map((char, i) => (\n                <text\n                    key={i}\n                    style={{\n                        stroke: color,\n                        fill: color,\n                        fillOpacity: 0,\n                        fontSize: fontSize,\n                        strokeWidth: `${strokeWidth}px`,\n                    }}\n                    y={fontSize}>\n                    {char}\n                </text>\n            ))}\n        </svg>\n    );\n};\n"
        }
    ]
}
