{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "primitives-texts-rolling",
  "title": "Rolling Text",
  "description": "A rolling text animation.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "@unlumen-ui/hooks-use-is-in-view"
  ],
  "files": [
    {
      "path": "registry/primitives/texts/rolling/index.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion, type Transition } from \"motion/react\";\n\nimport {\n  useIsInView,\n  type UseIsInViewOptions,\n} from \"@/hooks/use-is-in-view\";\n\nconst formatCharacter = (char: string) => (char === \" \" ? \"\\u00A0\" : char);\n\nconst CHAR_STYLE: React.CSSProperties = {\n  position: \"absolute\",\n  display: \"inline-block\",\n  backfaceVisibility: \"hidden\",\n};\n\ntype RollingTextProps = Omit<React.ComponentProps<\"span\">, \"children\"> & {\n  text: string;\n  transition?: Transition;\n  delay?: number;\n} & UseIsInViewOptions;\n\nfunction RollingText({\n  ref,\n  text,\n  inView = false,\n  inViewMargin = \"0px\",\n  inViewOnce = true,\n  transition = { duration: 0.5, delay: 0.1, ease: \"easeOut\" },\n  delay = 0,\n  ...props\n}: RollingTextProps) {\n  const { ref: localRef, isInView } = useIsInView(\n    ref as React.Ref<HTMLElement>,\n    {\n      inView,\n      inViewOnce,\n      inViewMargin,\n    },\n  );\n\n  const parts = React.useMemo(() => text.split(/(\\s+)/), [text]);\n  const stepDelay = transition?.delay ?? 0;\n\n  let charIdx = 0;\n\n  return (\n    <span ref={localRef} data-slot=\"rolling-text\" {...props}>\n      {parts.map((part, wi) => {\n        if (/^\\s+$/.test(part)) {\n          return <span key={`space-${wi}`}>{part}</span>;\n        }\n\n        const chars = Array.from(part);\n        return (\n          <span\n            key={`word-${wi}`}\n            style={{ display: \"inline-block\", whiteSpace: \"nowrap\" }}\n          >\n            {chars.map((char, ci) => {\n              const thisIdx = charIdx++;\n              const charDelay = delay / 1000 + thisIdx * stepDelay;\n              return (\n                <span\n                  key={`c-${wi}-${ci}`}\n                  style={{\n                    position: \"relative\",\n                    display: \"inline-block\",\n                    perspective: \"9999999px\",\n                    transformStyle: \"preserve-3d\",\n                    width: \"auto\",\n                  }}\n                  aria-hidden=\"true\"\n                >\n                  <motion.span\n                    style={{\n                      ...CHAR_STYLE,\n                      transformOrigin: \"50% 25%\",\n                    }}\n                    initial={{ rotateX: 0 }}\n                    animate={isInView ? { rotateX: 90 } : undefined}\n                    transition={{\n                      ...transition,\n                      delay: charDelay,\n                    }}\n                  >\n                    {formatCharacter(char)}\n                  </motion.span>\n                  <motion.span\n                    style={{\n                      ...CHAR_STYLE,\n                      transformOrigin: \"50% 100%\",\n                    }}\n                    initial={{ rotateX: 90 }}\n                    animate={isInView ? { rotateX: 0 } : undefined}\n                    transition={{\n                      ...transition,\n                      delay: charDelay + 0.3,\n                    }}\n                  >\n                    {formatCharacter(char)}\n                  </motion.span>\n                  <span style={{ visibility: \"hidden\" }}>\n                    {formatCharacter(char)}\n                  </span>\n                </span>\n              );\n            })}\n          </span>\n        );\n      })}\n\n      <span className=\"sr-only\">{text}</span>\n    </span>\n  );\n}\n\nexport { RollingText, type RollingTextProps };\n",
      "type": "registry:ui",
      "target": "components/unlumen-ui/primitives/texts/rolling.tsx"
    }
  ],
  "type": "registry:ui"
}