{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "primitives-texts-splitting",
  "title": "Splitting Text",
  "description": "A splitting text animation.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "@unlumen-ui/hooks-use-is-in-view"
  ],
  "files": [
    {
      "path": "registry/primitives/texts/splitting/index.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport {\n  motion,\n  type Variants,\n  type TargetAndTransition,\n  type HTMLMotionProps,\n  type Transition,\n} from \"motion/react\";\n\nimport {\n  useIsInView,\n  type UseIsInViewOptions,\n} from \"@/hooks/use-is-in-view\";\n\ntype DefaultSplittingTextProps = Omit<\n  HTMLMotionProps<\"div\">,\n  \"children\" | \"initial\" | \"animate\" | \"transition\"\n> & {\n  initial?: TargetAndTransition;\n  animate?: TargetAndTransition;\n  transition?: Transition;\n  stagger?: number;\n  delay?: number;\n  disableAnimation?: boolean;\n} & UseIsInViewOptions;\n\ntype CharsOrWordsSplittingTextProps = DefaultSplittingTextProps & {\n  type?: \"chars\" | \"words\";\n  text: string;\n};\n\ntype LinesSplittingTextProps = DefaultSplittingTextProps & {\n  type?: \"lines\";\n  text: string[];\n};\n\ntype SplittingTextProps =\n  | CharsOrWordsSplittingTextProps\n  | LinesSplittingTextProps;\n\nconst SplittingText: React.FC<SplittingTextProps> = ({\n  ref,\n  text,\n  type = \"chars\",\n  initial = { x: 150, opacity: 0 },\n  animate = { x: 0, opacity: 1 },\n  transition = { duration: 0.7, ease: \"easeOut\" },\n  stagger,\n  delay = 0,\n  inView = false,\n  inViewMargin = \"0px\",\n  inViewOnce = true,\n  disableAnimation = false,\n  ...props\n}) => {\n  const containerVariants: Variants = {\n    hidden: {},\n    visible: {\n      transition: {\n        delayChildren: delay / 1000,\n        staggerChildren:\n          stagger ?? (type === \"chars\" ? 0.05 : type === \"words\" ? 0.2 : 0.3),\n      },\n    },\n  };\n\n  const itemVariants: Variants = {\n    hidden: disableAnimation ? animate : initial,\n    visible: {\n      ...animate,\n      transition: disableAnimation ? { duration: 0 } : transition,\n    },\n  };\n\n  const { ref: localRef, isInView } = useIsInView(\n    ref as React.Ref<HTMLElement>,\n    {\n      inView,\n      inViewOnce,\n      inViewMargin,\n    },\n  );\n\n  if (Array.isArray(text)) {\n    return (\n      <motion.span\n        ref={localRef}\n        initial=\"hidden\"\n        animate={isInView ? \"visible\" : \"hidden\"}\n        variants={containerVariants}\n        {...props}\n      >\n        {text.map((line, i) => (\n          <React.Fragment key={`line-${i}`}>\n            <motion.span\n              variants={itemVariants}\n              style={{ display: \"inline-block\" }}\n            >\n              {line}\n            </motion.span>\n            {i < text.length - 1 ? <br /> : null}\n          </React.Fragment>\n        ))}\n      </motion.span>\n    );\n  }\n\n  if (type === \"words\") {\n    const tokens = (text as string).match(/\\S+\\s*/g) || [];\n    return (\n      <motion.span\n        ref={localRef}\n        initial=\"hidden\"\n        animate={isInView ? \"visible\" : \"hidden\"}\n        variants={containerVariants}\n        {...props}\n      >\n        {tokens.map((token, i) => (\n          <React.Fragment key={i}>\n            <motion.span\n              variants={itemVariants}\n              style={{ display: \"inline-block\", whiteSpace: \"normal\" }}\n            >\n              {token.trim()}\n            </motion.span>\n            {/\\s$/.test(token) ? \" \" : null}\n          </React.Fragment>\n        ))}\n      </motion.span>\n    );\n  }\n\n  const tokens = (text as string).split(/(\\s+)/);\n  const perChar = stagger ?? 0.05;\n  const baseDelaySec = (delay ?? 0) / 1000;\n\n  let globalIndex = 0;\n\n  return (\n    <motion.span\n      ref={localRef}\n      initial=\"hidden\"\n      animate={isInView ? \"visible\" : \"hidden\"}\n      variants={{\n        hidden: {},\n        visible: { transition: {} },\n      }}\n      {...props}\n    >\n      {tokens.map((tok, wi) => {\n        if (/^\\s+$/.test(tok)) {\n          return <span key={`space-${wi}`}>{tok}</span>;\n        }\n        const chars = Array.from(tok);\n        const wordDelay = baseDelaySec + perChar * globalIndex;\n        globalIndex += chars.length;\n\n        return (\n          <motion.span\n            key={`word-${wi}`}\n            style={{ display: \"inline-block\", whiteSpace: \"nowrap\" }}\n            variants={{}}\n            transition={{ delayChildren: wordDelay, staggerChildren: perChar }}\n            initial=\"hidden\"\n            animate={isInView ? \"visible\" : \"hidden\"}\n          >\n            {chars.map((ch, ci) => (\n              <motion.span\n                key={`ch-${wi}-${ci}`}\n                variants={itemVariants}\n                style={{ display: \"inline-block\", whiteSpace: \"pre\" }}\n              >\n                {ch}\n              </motion.span>\n            ))}\n          </motion.span>\n        );\n      })}\n    </motion.span>\n  );\n};\n\nexport { SplittingText, type SplittingTextProps };\n",
      "type": "registry:ui",
      "target": "components/unlumen-ui/primitives/texts/splitting.tsx"
    }
  ],
  "type": "registry:ui"
}