{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "stagger-text",
  "type": "registry:ui",
  "title": "StaggerText",
  "description": "A text component with staggered animation.",
  "files": [
    {
      "path": "registry/sonaui/stagger-text/stagger-text.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { motion, useReducedMotion, type Variants } from \"motion/react\";\nimport { type ReactNode, useState } from \"react\";\n\nimport { cn } from \"@/lib/sona-utils\";\n\ntype StaggerTextEleType = \"span\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\";\n\ntype StaggerTextProps<T extends StaggerTextEleType> =\n  React.ComponentPropsWithoutRef<T> & {\n    /** The text content to be displayed with staggered animation. */\n    text: string;\n    /** Additional CSS classes for the container. */\n    className?: string;\n    /**\n     * The HTML tag to be used for the text container.\n     * @default \"h3\"\n     */\n    as?: T;\n  };\n\nexport default function StaggerText({\n  text = \"text\",\n  className,\n  as = \"h3\",\n  ...props\n}: StaggerTextProps<StaggerTextEleType>) {\n  const Tag = as; // Explicitly type as a React component\n  const [activeIndex, setActiveIndex] = useState(0);\n  const [isActive, setIsActive] = useState(false);\n  const shouldReduceMotion = useReducedMotion();\n\n  if (shouldReduceMotion) {\n    return (\n      <Tag className={cn(\"tracking-wide\", className)} {...props}>\n        {text}\n      </Tag>\n    );\n  }\n\n  const characters = [...new Intl.Segmenter().segment(text)].map(\n    (segment) => segment.segment,\n  );\n\n  return (\n    <Tag\n      className={cn(\"overflow-clip tracking-wide select-text\", className)}\n      aria-label={text}\n      onCopy={(e) => {\n        e.preventDefault();\n        e.clipboardData.setData(\"text/plain\", text);\n      }}\n      {...props}\n    >\n      {characters.map((char, i) => {\n        const delay = Math.abs(activeIndex - i);\n        return (\n          <StaggerTextItem\n            char={char}\n            // biome-ignore lint/suspicious/noArrayIndexKey: chars repeat; position is the identity\n            key={i}\n            onMouseEnter={() => {\n              setActiveIndex(i);\n              setIsActive(true);\n            }}\n            onMouseLeave={() => {\n              setActiveIndex(-1);\n              setIsActive(false);\n            }}\n            delay={delay}\n            isHovered={isActive}\n          />\n        );\n      })}\n    </Tag>\n  );\n}\n\ninterface StaggerTextItemProps\n  extends React.ComponentPropsWithoutRef<typeof motion.span> {\n  char: string;\n  delay?: number;\n  isHovered?: boolean;\n}\n\nconst StaggerTextItem = ({\n  char,\n  delay,\n  isHovered,\n  ...props\n}: StaggerTextItemProps) => {\n  return (\n    <motion.span\n      {...props}\n      className=\"inline-flex relative flex-col\"\n      role=\"presentation\"\n    >\n      <StaggerItemSegment\n        variants={variants}\n        custom={delay}\n        initial=\"initial\"\n        animate={isHovered ? \"animate\" : \"exit\"}\n        aria-hidden=\"true\"\n      >\n        {char}\n      </StaggerItemSegment>\n      <StaggerItemSegment\n        isCopy\n        variants={variants}\n        custom={delay}\n        initial=\"initial\"\n        animate={isHovered ? \"animate\" : \"exit\"}\n        aria-hidden=\"true\"\n      >\n        {char}\n      </StaggerItemSegment>\n    </motion.span>\n  );\n};\n\ninterface StaggerItemSegmentProps\n  extends React.ComponentPropsWithoutRef<typeof motion.span> {\n  children: ReactNode;\n  isCopy?: boolean;\n}\n\nconst StaggerItemSegment = ({\n  children,\n  isCopy,\n  ...props\n}: StaggerItemSegmentProps) => {\n  const content = children === \" \" ? \"\\u00A0\" : children;\n\n  if (isCopy) {\n    return (\n      <motion.span\n        className=\"absolute left-0 top-[0] h-fit w-full select-text\"\n        style={{ translate: \"0 100%\" }}\n        {...props}\n      >\n        {content}\n      </motion.span>\n    );\n  }\n\n  return (\n    <motion.span className=\"h-fit select-none\" aria-hidden=\"true\" {...props}>\n      {content}\n    </motion.span>\n  );\n};\n\nconst variants: Variants = {\n  initial: { y: 0 },\n  animate: (i: number) => ({\n    y: \"-100%\",\n    transition: {\n      delay: i * 0.04,\n      duration: 0.4,\n      ease: \"easeInOut\",\n      type: \"tween\" as const,\n    },\n  }),\n  exit: (i: number) => ({\n    y: 0,\n    transition: { delay: i * 0.02, duration: 0.3 },\n  }),\n};\n",
      "target": "components/ui/stagger-text/stagger-text.tsx"
    }
  ],
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "@sona-ui/sona-utils"
  ]
}