Skip to content

Commit 818031a

Browse files
committed
docs: improve ExampleSnippet component
1 parent 842f2e2 commit 818031a

File tree

5 files changed

+196
-87
lines changed

5 files changed

+196
-87
lines changed

packages/docs/gatsby-node.mjs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
1-
import { resolve } from 'node:path'
1+
import { dirname, resolve } from 'node:path'
2+
import { fileURLToPath } from 'node:url'
23
import { createFilePath } from 'gatsby-source-filesystem'
4+
import { glob } from 'glob'
5+
6+
const __dirname = dirname(fileURLToPath(import.meta.url))
7+
8+
export const onCreateWebpackConfig = ({ actions }) => {
9+
const { setWebpackConfig } = actions
10+
11+
// Find all 'examples' directories
12+
const examplePaths = glob.sync(resolve(__dirname, 'content/**/**/examples'))
13+
14+
// Create Webpack alias
15+
setWebpackConfig({
16+
resolve: {
17+
alias: {
18+
'@assets': resolve(__dirname, 'content/assets'),
19+
'@example': examplePaths, // Adds all paths to a single alias
20+
},
21+
},
22+
})
23+
}
324

425
export const onCreateNode = async ({
526
node,

packages/docs/src/components/ExampleSnippet.tsx

Lines changed: 98 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import React, { FC, ReactNode, useState } from 'react'
1+
import React, { FC, lazy, ReactNode, Suspense, useEffect, useMemo, useState } from 'react'
22
import { Highlight, Language } from 'prism-react-renderer'
3-
43
import CIcon from '@coreui/icons-react'
54
import { cibCodesandbox, cilCheckAlt, cilCopy } from '@coreui/icons'
65
import { CNav, CNavLink, CTooltip, useClipboard } from '@coreui/react'
7-
86
import { openStackBlitzProject } from '../utils/stackblitz'
97
import { openCodeSandboxProject } from '../utils/codesandbox'
108

@@ -16,8 +14,9 @@ interface CodeSnippets {
1614
export interface ExampleSnippetProps {
1715
children: ReactNode
1816
className?: string
19-
code: string | CodeSnippets
17+
code?: string | CodeSnippets
2018
codeSandbox?: boolean
19+
component?: string
2120
componentName?: string
2221
pro?: boolean
2322
stackBlitz?: boolean
@@ -28,22 +27,62 @@ const ExampleSnippet: FC<ExampleSnippetProps> = ({
2827
className = '',
2928
code,
3029
codeSandbox = true,
30+
component,
3131
componentName,
3232
pro = false,
3333
stackBlitz = true,
3434
}) => {
35+
const [codeJS, setCodeJS] = useState<string>()
36+
const [codeTS, setCodeTS] = useState<string>()
3537
const [language, setLanguage] = useState<'js' | 'ts'>('js')
3638
const { copy, isCopied } = useClipboard()
3739

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])
4481

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(() => {
4786
if (!hasJS && hasTS) {
4887
setLanguage('ts')
4988
} else {
@@ -53,20 +92,35 @@ const ExampleSnippet: FC<ExampleSnippetProps> = ({
5392

5493
const handleCopy = () => {
5594
const codeToCopy = language === 'js' ? codeJS : codeTS
56-
if (codeToCopy) {
57-
copy(codeToCopy)
58-
}
95+
if (codeToCopy) copy(codeToCopy)
5996
}
6097

6198
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)
65100
const showTSTab = hasTS
66101

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+
67111
return (
68112
<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>
70124
<div className="highlight-toolbar border-top">
71125
<CNav className="px-3" variant="underline-border">
72126
{showJSTab && (
@@ -88,9 +142,9 @@ const ExampleSnippet: FC<ExampleSnippetProps> = ({
88142
aria-label="Try it on CodeSandbox"
89143
onClick={() =>
90144
openCodeSandboxProject({
91-
name: React.isValidElement(children) && (children as any).type?.name,
145+
name: component || getProjectName(),
92146
language,
93-
code: language === 'js' ? codeJS : codeTS || '',
147+
code: language === 'js' ? codeJS || '' : codeTS || '',
94148
componentName,
95149
pro,
96150
})
@@ -109,9 +163,9 @@ const ExampleSnippet: FC<ExampleSnippetProps> = ({
109163
aria-label="Try it on StackBlitz"
110164
onClick={() =>
111165
openStackBlitzProject({
112-
name: React.isValidElement(children) && (children as any).type?.name,
166+
name: component || getProjectName(),
113167
language,
114-
code: language === 'js' ? codeJS : codeTS || '',
168+
code: language === 'js' ? codeJS || '' : codeTS || '',
115169
componentName,
116170
pro,
117171
})
@@ -148,25 +202,27 @@ const ExampleSnippet: FC<ExampleSnippetProps> = ({
148202
</CNav>
149203
</div>
150204

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+
)}
170226
</div>
171227
)
172228
}

packages/docs/src/utils/codesandbox.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// openCodeSandboxProject.ts
2-
31
import {
42
ProjectOptions,
53
generateTitle,
@@ -25,15 +23,15 @@ export const openCodeSandboxProject = async (options: CodeSandboxOptions) => {
2523
const indexHTML = generateIndexHTML(title)
2624
const indexExtension = language === 'ts' ? 'tsx' : 'js'
2725
const indexJS = generateIndexJS(name, language, pro, 'codesandbox')
28-
const packageJSON = generatePackageJSON(title, description, language, pro, 'codesandbox')
26+
const packageJSON = generatePackageJSON(title, description, language, pro, code, 'codesandbox')
2927

3028
// Define the files structure
3129
const files: Record<string, { content: string }> = {
3230
'public/index.html': {
3331
content: indexHTML,
3432
},
3533
[`src/${name}.${language}x`]: {
36-
content: code,
34+
content: code.replaceAll('@assets/images/', '@coreui/projects-assets/images/'),
3735
},
3836
[`src/index.${indexExtension}`]: {
3937
content: indexJS,

0 commit comments

Comments
 (0)