1
1
"use client"
2
2
3
- import { Suspense , useState } from "react"
3
+ import { useCallback , useEffect , useState } from "react"
4
4
import { Clipboard , ClipboardCheck } from "lucide-react"
5
5
import { useLocale } from "next-intl"
6
6
@@ -29,17 +29,64 @@ type CodeExamplesProps = {
29
29
eventCategory : string
30
30
}
31
31
32
+ const AccordionCodeBlock = ( {
33
+ code,
34
+ codeLanguage,
35
+ } : {
36
+ code : string
37
+ codeLanguage : string
38
+ } ) => (
39
+ < >
40
+ < Codeblock
41
+ codeLanguage = { codeLanguage }
42
+ allowCollapse = { false }
43
+ className = "[&>div]:-m-//2 [&>div]:rounded-none [&_*]:!text-xs [&_pre]:p-4"
44
+ fromHomepage
45
+ >
46
+ { code }
47
+ </ Codeblock >
48
+ < CopyToClipboard
49
+ text = { code ?? "" }
50
+ className = "absolute end-2 top-2 rounded p-2 hover:bg-primary/10 hover:text-primary"
51
+ >
52
+ { ( hasCopied ) => ( hasCopied ? < ClipboardCheck /> : < Clipboard /> ) }
53
+ </ CopyToClipboard >
54
+ </ >
55
+ )
56
+
32
57
const CodeExamples = ( { title, codeExamples } : CodeExamplesProps ) => {
33
58
const locale = useLocale ( )
34
59
35
60
const [ isModalOpen , setModalOpen ] = useState ( false )
36
61
const [ activeCode , setActiveCode ] = useState ( 0 )
62
+ const [ fetchedCodes , setFetchedCodes ] = useState < { [ key : number ] : string } > (
63
+ { }
64
+ )
37
65
38
66
const eventCategory = `Homepage - ${ locale } `
39
67
40
- const toggleCodeExample = ( id : number ) : void => {
41
- setActiveCode ( id )
42
- setModalOpen ( true )
68
+ const getCode = useCallback (
69
+ ( idx : number ) => {
70
+ const example = codeExamples [ idx ]
71
+ if ( ! fetchedCodes [ idx ] ) {
72
+ fetch ( example . codeUrl )
73
+ . then ( ( res ) => res . text ( ) )
74
+ . then ( ( text ) => setFetchedCodes ( ( prev ) => ( { ...prev , [ idx ] : text } ) ) )
75
+ }
76
+ } ,
77
+ [ codeExamples , fetchedCodes ]
78
+ )
79
+
80
+ // For modal: fetch code when opened if needed
81
+ useEffect ( ( ) => {
82
+ if ( isModalOpen ) {
83
+ getCode ( activeCode )
84
+ }
85
+ } , [ isModalOpen , activeCode , getCode ] )
86
+
87
+ // For accordion: fetch code when expanded if needed
88
+ const handleAccordionOpen = ( idx : number ) => {
89
+ getCode ( idx )
43
90
}
44
91
45
92
return (
@@ -54,7 +101,8 @@ const CodeExamples = ({ title, codeExamples }: CodeExamplesProps) => {
54
101
isModalOpen && idx === activeCode && "bg-background-highlight"
55
102
) }
56
103
onClick = { ( ) => {
57
- toggleCodeExample ( idx )
104
+ setActiveCode ( idx )
105
+ setModalOpen ( true )
58
106
trackCustomEvent ( {
59
107
eventCategory,
60
108
eventAction : "Code Examples" ,
@@ -68,9 +116,12 @@ const CodeExamples = ({ title, codeExamples }: CodeExamplesProps) => {
68
116
) ) }
69
117
{ /* Mobile */ }
70
118
< Accordion type = "single" collapsible className = "md:hidden" >
71
- { codeExamples . map ( ( { title, description, code , codeLanguage } ) => (
119
+ { codeExamples . map ( ( { title, description, codeLanguage } , idx ) => (
72
120
< AccordionItem key = { title } value = { title } className = "relative" >
73
- < AccordionTrigger className = "flex border-t px-6 py-4 hover:bg-background-highlight" >
121
+ < AccordionTrigger
122
+ className = "flex border-t px-6 py-4 hover:bg-background-highlight"
123
+ onClick = { ( ) => handleAccordionOpen ( idx ) }
124
+ >
74
125
< div className = "flex flex-col items-start gap-y-0.5" >
75
126
< p className = "text-start text-md font-bold text-body" >
76
127
{ title }
@@ -81,26 +132,16 @@ const CodeExamples = ({ title, codeExamples }: CodeExamplesProps) => {
81
132
</ div >
82
133
</ AccordionTrigger >
83
134
< AccordionContent className = "relative border-t" dir = "ltr" >
84
- < Suspense fallback = { < SkeletonLines noOfLines = { 16 } /> } >
85
- < div className = "-m-2 max-h-[50vh] overflow-auto" >
86
- < Codeblock
135
+ < div className = "-m-2 max-h-[50vh] overflow-auto" >
136
+ { ! fetchedCodes [ idx ] ? (
137
+ < SkeletonLines noOfLines = { 16 } />
138
+ ) : (
139
+ < AccordionCodeBlock
140
+ code = { fetchedCodes [ idx ] }
87
141
codeLanguage = { codeLanguage }
88
- allowCollapse = { false }
89
- className = "[&>div]:-m-//2 [&>div]:rounded-none [&_*]:!text-xs [&_pre]:p-4"
90
- fromHomepage
91
- >
92
- { code }
93
- </ Codeblock >
94
- < CopyToClipboard
95
- text = { code }
96
- className = "absolute end-2 top-2 rounded p-2 hover:bg-primary/10 hover:text-primary"
97
- >
98
- { ( hasCopied ) =>
99
- hasCopied ? < ClipboardCheck /> : < Clipboard />
100
- }
101
- </ CopyToClipboard >
102
- </ div >
103
- </ Suspense >
142
+ />
143
+ ) }
144
+ </ div >
104
145
</ AccordionContent >
105
146
</ AccordionItem >
106
147
) ) }
@@ -112,16 +153,18 @@ const CodeExamples = ({ title, codeExamples }: CodeExamplesProps) => {
112
153
setIsOpen = { setModalOpen }
113
154
title = { codeExamples [ activeCode ] . title }
114
155
>
115
- < Suspense fallback = { < SkeletonLines noOfLines = { 16 } dir = "ltr" /> } >
156
+ { ! fetchedCodes [ activeCode ] ? (
157
+ < SkeletonLines noOfLines = { 16 } />
158
+ ) : (
116
159
< Codeblock
117
160
codeLanguage = { codeExamples [ activeCode ] . codeLanguage }
118
161
allowCollapse = { false }
119
162
className = "[&_pre]:p-6"
120
163
fromHomepage
121
164
>
122
- { codeExamples [ activeCode ] . code }
165
+ { fetchedCodes [ activeCode ] }
123
166
</ Codeblock >
124
- </ Suspense >
167
+ ) }
125
168
</ CodeModal >
126
169
) }
127
170
</ div >
0 commit comments