Skip to content

Commit 36d080b

Browse files
committed
feat: section decorator for breaking lines
1 parent 195596d commit 36d080b

2 files changed

Lines changed: 33 additions & 17 deletions

File tree

src/components/sections/CardRowSection.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ export function CardRowSection({ content, children }: CardRowSectionProps) {
5454
funnelType={content?.sectionLayout ?? "left"}
5555
animationPreset="none"
5656
>
57-
<div aria-hidden="true" className="pointer-events-none absolute -bottom-40 -top-16 inset-x-0 [background:radial-gradient(circle,rgba(114,201,248,0.1),transparent_50%)]" />
5857

5958
<motion.div
6059
className="grid grid-cols-1 gap-16 lg:grid-cols-3 lg:gap-8 z-10"

src/components/ui/TextReveal.tsx

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -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

1823
interface 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

Comments
 (0)