@@ -10,10 +10,15 @@ export interface TextRevealProps extends ComponentPropsWithoutRef<"span"> {
1010 highlightClassName ?: string
1111}
1212
13- interface WordToken {
14- value : string
15- highlighted : boolean
16- }
13+ type TextToken =
14+ | {
15+ type : "word"
16+ value : string
17+ highlighted : boolean
18+ }
19+ | {
20+ type : "break"
21+ }
1722
1823interface WordProps {
1924 children : ReactNode
@@ -22,17 +27,22 @@ interface WordProps {
2227 className ?: string
2328}
2429
25- function getWordTokens ( text : string ) : WordToken [ ] {
30+ function getTextTokens ( text : string ) : TextToken [ ] {
2631 const parts = text . split ( / ( \* \* .* ?\* \* ) / g)
2732
2833 return parts . flatMap ( ( part ) => {
2934 const highlighted = part . startsWith ( "**" ) && part . endsWith ( "**" ) && part . length > 4
3035 const value = highlighted ? part . slice ( 2 , - 2 ) : part
3136
32- return value
33- . split ( / \s + / )
34- . filter ( Boolean )
35- . map ( ( word ) => ( { value : word , highlighted } ) )
37+ return value . split ( / ( \\ n | \r \n | \n | \r ) / g) . flatMap ( ( linePart ) : TextToken [ ] => {
38+ if ( ! linePart ) return [ ]
39+ if ( / ^ ( \\ n | \r \n | \n | \r ) $ / . test ( linePart ) ) return [ { type : "break" } ]
40+
41+ return linePart
42+ . split ( / [ ^ \S \r \n ] + / )
43+ . filter ( Boolean )
44+ . map ( ( word ) => ( { type : "word" , value : word , highlighted } ) )
45+ } )
3646 } )
3747}
3848
@@ -42,22 +52,29 @@ export const TextReveal: React.FC<TextRevealProps> = ({ children, className, hig
4252 target : textRef ,
4353 offset : [ "start 85%" , "end 55%" ] ,
4454 } )
45- const words = getWordTokens ( children )
55+ const tokens = getTextTokens ( children )
56+ const wordCount = tokens . filter ( ( token ) => token . type === "word" ) . length
57+ let wordIndex = 0
4658
4759 return (
4860 < span ref = { textRef } className = { cn ( "inline" , className ) } { ...props } >
49- { words . map ( ( word , index ) => {
50- const start = index / words . length
51- const end = start + 1 / words . length
61+ { tokens . map ( ( token , index ) => {
62+ if ( token . type === "break" ) {
63+ return < br key = { `break-${ index } ` } />
64+ }
65+
66+ const start = wordIndex / wordCount
67+ const end = start + 1 / wordCount
68+ wordIndex += 1
5269
5370 return (
5471 < Word
55- key = { `${ word . value } -${ index } ` }
72+ key = { `${ token . value } -${ index } ` }
5673 progress = { scrollYProgress }
5774 range = { [ start , end ] }
58- className = { word . highlighted ? highlightClassName : undefined }
75+ className = { token . highlighted ? highlightClassName : undefined }
5976 >
60- { word . value }
77+ { token . value }
6178 </ Word >
6279 )
6380 } ) }
0 commit comments