{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "primitives-texts-rolling",
  "title": "Rolling Text",
  "description": "A rolling text animation.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "@odysseyui/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/odysseyui/primitives/texts/rolling.tsx"
    }
  ],
  "type": "registry:ui"
}