1
- import React , { FC , ReactNode , useState } from 'react'
1
+ import React , { FC , lazy , ReactNode , Suspense , useEffect , useMemo , useState } from 'react'
2
2
import { Highlight , Language } from 'prism-react-renderer'
3
-
4
3
import CIcon from '@coreui/icons-react'
5
4
import { cibCodesandbox , cilCheckAlt , cilCopy } from '@coreui/icons'
6
5
import { CNav , CNavLink , CTooltip , useClipboard } from '@coreui/react'
7
-
8
6
import { openStackBlitzProject } from '../utils/stackblitz'
9
7
import { openCodeSandboxProject } from '../utils/codesandbox'
10
8
@@ -16,8 +14,9 @@ interface CodeSnippets {
16
14
export interface ExampleSnippetProps {
17
15
children : ReactNode
18
16
className ?: string
19
- code : string | CodeSnippets
17
+ code ? : string | CodeSnippets
20
18
codeSandbox ?: boolean
19
+ component ?: string
21
20
componentName ?: string
22
21
pro ?: boolean
23
22
stackBlitz ?: boolean
@@ -28,22 +27,62 @@ const ExampleSnippet: FC<ExampleSnippetProps> = ({
28
27
className = '' ,
29
28
code,
30
29
codeSandbox = true ,
30
+ component,
31
31
componentName,
32
32
pro = false ,
33
33
stackBlitz = true ,
34
34
} ) => {
35
+ const [ codeJS , setCodeJS ] = useState < string > ( )
36
+ const [ codeTS , setCodeTS ] = useState < string > ( )
35
37
const [ language , setLanguage ] = useState < 'js' | 'ts' > ( 'js' )
36
38
const { copy, isCopied } = useClipboard ( )
37
39
38
- // Type Guards to determine the shape of 'code' prop
39
- const isCodeString = typeof code === 'string'
40
- const codeJS = isCodeString ? code : code . js || code . ts
41
- const codeTS = isCodeString ? code : code . ts
42
- const hasJS = Boolean ( codeJS )
43
- const hasTS = Boolean ( codeTS )
40
+ const Preview = useMemo ( ( ) => {
41
+ if ( ! component ) return null
42
+ return lazy ( ( ) =>
43
+ import ( `@example/${ component } .tsx` )
44
+ . then ( ( module ) => ( { default : module [ component ] } ) )
45
+ . catch ( ( error ) => {
46
+ console . error ( `Failed to load Preview component for ${ component } :` , error )
47
+ return { default : ( ) => < div > Preview not available.</ div > }
48
+ } ) ,
49
+ )
50
+ } , [ component ] )
51
+
52
+ useEffect ( ( ) => {
53
+ const loadCode = async ( ) => {
54
+ if ( code ) {
55
+ if ( typeof code === 'string' ) {
56
+ setCodeJS ( code )
57
+ } else {
58
+ setCodeJS ( code . js )
59
+ setCodeTS ( code . ts )
60
+ }
61
+ } else if ( component ) {
62
+ try {
63
+ const tsModule = await import ( `!!raw-loader!@example/${ component } .tsx` )
64
+ setCodeTS ( tsModule . default )
65
+ setCodeJS ( tsModule . default )
66
+ } catch ( error ) {
67
+ console . error ( `Failed to load TypeScript code for component ${ component } :` , error )
68
+ }
69
+
70
+ try {
71
+ const jsModule = await import ( `!!raw-loader!@example/${ component } .jsx` )
72
+ setCodeJS ( jsModule . default )
73
+ } catch {
74
+ // JSX version may not exist
75
+ }
76
+ }
77
+ }
78
+
79
+ loadCode ( )
80
+ } , [ code , component ] )
44
81
45
- // Set initial language based on available code snippets
46
- React . useEffect ( ( ) => {
82
+ const hasJS = codeJS !== undefined && codeJS !== ''
83
+ const hasTS = codeTS !== undefined && codeTS !== ''
84
+
85
+ useEffect ( ( ) => {
47
86
if ( ! hasJS && hasTS ) {
48
87
setLanguage ( 'ts' )
49
88
} else {
@@ -53,20 +92,35 @@ const ExampleSnippet: FC<ExampleSnippetProps> = ({
53
92
54
93
const handleCopy = ( ) => {
55
94
const codeToCopy = language === 'js' ? codeJS : codeTS
56
- if ( codeToCopy ) {
57
- copy ( codeToCopy )
58
- }
95
+ if ( codeToCopy ) copy ( codeToCopy )
59
96
}
60
97
61
98
const prismLanguage : Language = language === 'js' ? 'jsx' : 'tsx'
62
-
63
- // Determine if both languages are available
64
- const showJSTab = hasJS && ( isCodeString || code . js !== code . ts )
99
+ const showJSTab = hasJS && ! ( typeof code === 'object' && code ?. js === code ?. ts )
65
100
const showTSTab = hasTS
66
101
102
+ const getProjectName = ( ) : string => {
103
+ if ( React . isValidElement ( children ) ) {
104
+ const childType = ( children as React . ReactElement ) . type
105
+ if ( typeof childType === 'string' ) return childType
106
+ if ( typeof childType === 'function' && childType . name ) return childType . name
107
+ }
108
+ return 'ExampleProject'
109
+ }
110
+
67
111
return (
68
112
< div className = "docs-example-snippet" >
69
- { children && < div className = { `docs-example ${ className } ` } > { children } </ div > }
113
+ < div className = { `docs-example ${ className } ` } >
114
+ { children ? (
115
+ children
116
+ ) : Preview ? (
117
+ < Suspense fallback = { < div > Loading preview...</ div > } >
118
+ < Preview />
119
+ </ Suspense >
120
+ ) : (
121
+ < div > No component specified.</ div >
122
+ ) }
123
+ </ div >
70
124
< div className = "highlight-toolbar border-top" >
71
125
< CNav className = "px-3" variant = "underline-border" >
72
126
{ showJSTab && (
@@ -88,9 +142,9 @@ const ExampleSnippet: FC<ExampleSnippetProps> = ({
88
142
aria-label = "Try it on CodeSandbox"
89
143
onClick = { ( ) =>
90
144
openCodeSandboxProject ( {
91
- name : React . isValidElement ( children ) && ( children as any ) . type ?. name ,
145
+ name : component || getProjectName ( ) ,
92
146
language,
93
- code : language === 'js' ? codeJS : codeTS || '' ,
147
+ code : language === 'js' ? codeJS || '' : codeTS || '' ,
94
148
componentName,
95
149
pro,
96
150
} )
@@ -109,9 +163,9 @@ const ExampleSnippet: FC<ExampleSnippetProps> = ({
109
163
aria-label = "Try it on StackBlitz"
110
164
onClick = { ( ) =>
111
165
openStackBlitzProject ( {
112
- name : React . isValidElement ( children ) && ( children as any ) . type ?. name ,
166
+ name : component || getProjectName ( ) ,
113
167
language,
114
- code : language === 'js' ? codeJS : codeTS || '' ,
168
+ code : language === 'js' ? codeJS || '' : codeTS || '' ,
115
169
componentName,
116
170
pro,
117
171
} )
@@ -148,25 +202,27 @@ const ExampleSnippet: FC<ExampleSnippetProps> = ({
148
202
</ CNav >
149
203
</ div >
150
204
151
- < div className = "highlight" >
152
- < Highlight
153
- code = { language === 'js' ? codeJS : codeTS || '' }
154
- language = { prismLanguage }
155
- theme = { { plain : { } , styles : [ ] } }
156
- >
157
- { ( { className, style, tokens, getLineProps, getTokenProps } ) => (
158
- < pre className = { className } style = { style } >
159
- { tokens . map ( ( line , i ) => (
160
- < div { ...getLineProps ( { line, key : i } ) } key = { i } >
161
- { line . map ( ( token , key ) => (
162
- < span { ...getTokenProps ( { token, key } ) } key = { key } />
163
- ) ) }
164
- </ div >
165
- ) ) }
166
- </ pre >
167
- ) }
168
- </ Highlight >
169
- </ div >
205
+ { ( hasJS || hasTS ) && (
206
+ < div className = "highlight" >
207
+ < Highlight
208
+ code = { language === 'js' ? codeJS || '' : codeTS || '' }
209
+ language = { prismLanguage }
210
+ theme = { { plain : { } , styles : [ ] } }
211
+ >
212
+ { ( { className : highlightClass , style, tokens, getLineProps, getTokenProps } ) => (
213
+ < pre className = { highlightClass } style = { style } >
214
+ { tokens . map ( ( line , i ) => (
215
+ < div { ...getLineProps ( { line, key : i } ) } key = { i } >
216
+ { line . map ( ( token , key ) => (
217
+ < span { ...getTokenProps ( { token, key } ) } key = { key } />
218
+ ) ) }
219
+ </ div >
220
+ ) ) }
221
+ </ pre >
222
+ ) }
223
+ </ Highlight >
224
+ </ div >
225
+ ) }
170
226
</ div >
171
227
)
172
228
}
0 commit comments