{"version":3,"file":"TypewriterEffect.mjs","names":[],"sources":["../../../src/awesome/TypewriterEffect/TypewriterEffect.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\nimport { createElement, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useMotionComponent } from '@/MotionProvider';\n\nimport { styles } from './style';\nimport type { TypewriterEffectProps } from './type';\n\nconst TypewriterEffect = memo<TypewriterEffectProps>(\n  ({\n    sentences,\n    as: Component = 'div',\n    typingSpeed = 100,\n    initialDelay = 0,\n    pauseDuration = 2000,\n    deletingSpeed = 50,\n    deletePauseDuration = 0,\n    loop = true,\n    className = '',\n    color,\n    showCursor = true,\n    hideCursorWhileTyping = false,\n    cursorCharacter,\n    cursorClassName = '',\n    cursorColor,\n    cursorBlinkDuration = 0.8,\n    cursorFade = true,\n    cursorStyle = 'pipe',\n    textColors = [],\n    variableSpeed,\n    onSentenceComplete,\n    startOnVisible = false,\n    reverseMode = false,\n    segmentMode = 'grapheme',\n    ...props\n  }: TypewriterEffectProps) => {\n    const Motion = useMotionComponent();\n    const cxStyles = cx;\n    const [displayedText, setDisplayedText] = useState('');\n    const [currentCharIndex, setCurrentCharIndex] = useState(0);\n    const [isDeleting, setIsDeleting] = useState(false);\n    const [currentTextIndex, setCurrentTextIndex] = useState(0);\n    const [isVisible, setIsVisible] = useState(!startOnVisible);\n    const [isDeletePausing, setIsDeletePausing] = useState(false);\n    const containerRef = useRef<HTMLElement>(null);\n\n    const textArray = useMemo(\n      () => (Array.isArray(sentences) ? sentences : [sentences]),\n      [sentences],\n    );\n\n    // Helper function to split text based on segment mode\n    const splitText = useCallback(\n      (text: string): string[] => {\n        // Use Intl.Segmenter if available\n        if (typeof Intl !== 'undefined' && 'Segmenter' in Intl) {\n          const segmenter = new Intl.Segmenter(undefined, { granularity: segmentMode });\n          return Array.from(segmenter.segment(text), (segment) => segment.segment);\n        }\n\n        // Fallback when Intl.Segmenter is not available\n        if (segmentMode === 'word') {\n          // Simple word splitting fallback\n          return text.split(/(\\s+)/).filter(Boolean);\n        }\n\n        // Grapheme fallback\n        return Array.from(text);\n      },\n      [segmentMode],\n    );\n\n    const getRandomSpeed = useCallback(() => {\n      if (!variableSpeed) return typingSpeed;\n      const { min, max } = variableSpeed;\n      return Math.random() * (max - min) + min;\n    }, [variableSpeed, typingSpeed]);\n\n    const getCurrentTextColor = () => {\n      if (textColors.length > 0) {\n        return textColors[currentTextIndex % textColors.length];\n      }\n      return color;\n    };\n\n    const getCurrentCursorColor = () => {\n      return cursorColor || color;\n    };\n\n    useEffect(() => {\n      if (!startOnVisible || !containerRef.current) return;\n\n      const observer = new IntersectionObserver(\n        (entries) => {\n          entries.forEach((entry) => {\n            if (entry.isIntersecting) {\n              setIsVisible(true);\n            }\n          });\n        },\n        { threshold: 0.1 },\n      );\n\n      observer.observe(containerRef.current);\n\n      return () => observer.disconnect();\n    }, [startOnVisible]);\n\n    useEffect(() => {\n      if (!isVisible) return;\n\n      let timeout: ReturnType<typeof setTimeout>;\n\n      const currentText = textArray[currentTextIndex];\n      // Split text based on segment mode\n      const textSegments = splitText(currentText);\n      const processedText = reverseMode ? textSegments.reverse().join('') : currentText;\n\n      // Handle delete pause state\n      if (isDeletePausing) {\n        timeout = setTimeout(() => {\n          setIsDeletePausing(false);\n        }, deletePauseDuration);\n        return () => clearTimeout(timeout);\n      }\n\n      const executeTypingAnimation = () => {\n        if (isDeleting) {\n          if (displayedText === '') {\n            setIsDeleting(false);\n            if (currentTextIndex === textArray.length - 1 && !loop) {\n              return;\n            }\n            if (onSentenceComplete) {\n              onSentenceComplete(textArray[currentTextIndex], currentTextIndex);\n            }\n            setCurrentTextIndex((prev) => (prev + 1) % textArray.length);\n            setCurrentCharIndex(0);\n\n            if (deletePauseDuration > 0) {\n              setIsDeletePausing(true);\n              return;\n            }\n          } else {\n            timeout = setTimeout(() => {\n              setDisplayedText((prev) => {\n                const segments = splitText(prev);\n                return segments.slice(0, -1).join('');\n              });\n            }, deletingSpeed);\n          }\n        } else {\n          const processedSegments = splitText(processedText);\n          if (currentCharIndex < processedSegments.length) {\n            timeout = setTimeout(\n              () => {\n                setDisplayedText((prev) => prev + processedSegments[currentCharIndex]);\n                setCurrentCharIndex((prev) => prev + 1);\n              },\n              variableSpeed ? getRandomSpeed() : typingSpeed,\n            );\n          } else if (textArray.length >= 1) {\n            if (!loop && currentTextIndex === textArray.length - 1) return;\n\n            timeout = setTimeout(() => {\n              setIsDeleting(true);\n            }, pauseDuration);\n          }\n        }\n      };\n\n      if (currentCharIndex === 0 && !isDeleting && displayedText === '') {\n        timeout = setTimeout(executeTypingAnimation, initialDelay);\n      } else {\n        executeTypingAnimation();\n      }\n\n      return () => clearTimeout(timeout);\n    }, [\n      currentCharIndex,\n      displayedText,\n      isDeleting,\n      isDeletePausing,\n      typingSpeed,\n      deletingSpeed,\n      deletePauseDuration,\n      pauseDuration,\n      textArray,\n      currentTextIndex,\n      loop,\n      initialDelay,\n      isVisible,\n      reverseMode,\n      variableSpeed,\n      onSentenceComplete,\n      getRandomSpeed,\n      splitText,\n    ]);\n\n    const getCursorStyle = () => {\n      if (cursorCharacter) return styles.cursorCustom;\n\n      switch (cursorStyle) {\n        case 'block': {\n          return styles.cursorBlock;\n        }\n        case 'dot': {\n          return styles.cursorDot;\n        }\n        case 'underscore': {\n          return styles.cursorUnderscore;\n        }\n        case 'pipe': {\n          return styles.cursor;\n        }\n      }\n    };\n\n    const currentTextLength = splitText(textArray[currentTextIndex]).length;\n    const isTyping = currentCharIndex < currentTextLength && !isDeleting;\n    const isAfterTyping = currentCharIndex === currentTextLength && !isDeleting;\n\n    const shouldHideCursor = (() => {\n      if (hideCursorWhileTyping === true) return true; // 完全隐藏\n      if (hideCursorWhileTyping === 'typing') return isTyping || isDeleting; // 打字和删除时隐藏\n      if (hideCursorWhileTyping === 'afterTyping') return isAfterTyping; // 打字完成后隐藏\n      return false;\n    })();\n\n    const textColor = getCurrentTextColor();\n    const finalCursorColor = getCurrentCursorColor();\n\n    // Split displayed text for animation\n    const characters = splitText(displayedText);\n\n    return createElement(\n      Component,\n      {\n        className: cxStyles(styles.container, className),\n        ref: containerRef,\n        ...props,\n      },\n      <>\n        <span className={styles.text} style={textColor ? { color: textColor } : undefined}>\n          {characters.map((char, index) => (\n            <Motion.span\n              animate={{ opacity: 1 }}\n              initial={{ opacity: 0 }}\n              key={`${currentTextIndex}-${index}`}\n              style={{ display: 'inline-block' }}\n              transition={{\n                duration: typingSpeed / 500,\n                ease: 'easeInOut',\n              }}\n            >\n              {char === ' ' ? '\\u00A0' : char}\n            </Motion.span>\n          ))}\n        </span>\n        {showCursor &&\n          (cursorFade ? (\n            <Motion.span\n              animate={{ opacity: shouldHideCursor ? 0 : 1 }}\n              className={cxStyles(getCursorStyle(), cursorClassName)}\n              initial={{ opacity: 0 }}\n              style={finalCursorColor ? { backgroundColor: finalCursorColor } : undefined}\n              transition={{\n                duration: shouldHideCursor ? 0.2 : cursorBlinkDuration,\n                ease: 'easeInOut',\n                repeat: shouldHideCursor ? 0 : Number.POSITIVE_INFINITY,\n                repeatType: 'reverse',\n              }}\n            >\n              {cursorCharacter}\n            </Motion.span>\n          ) : (\n            <span\n              className={cxStyles(getCursorStyle(), cursorClassName)}\n              style={{\n                backgroundColor: finalCursorColor,\n                opacity: shouldHideCursor ? 0 : 1,\n              }}\n            >\n              {cursorCharacter}\n            </span>\n          ))}\n      </>,\n    );\n  },\n);\n\nTypewriterEffect.displayName = 'TypewriterEffect';\n\nexport default TypewriterEffect;\n"],"mappings":"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"}