{"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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"}