Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 29e5aff

Browse filesBrowse files
committed
docs: add ExampleSnippet component
1 parent 9680566 commit 29e5aff
Copy full SHA for 29e5aff

File tree

13 files changed

+536
-31
lines changed
Filter options

13 files changed

+536
-31
lines changed
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import { useClipboard } from './useClipboard'
12
import { useColorModes } from './useColorModes'
23
import { useForkedRef } from './useForkedRef'
34
import { usePopper } from './usePopper'
45

5-
export { useColorModes, useForkedRef, usePopper }
6+
export { useClipboard, useColorModes, useForkedRef, usePopper }
+38Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { useState, useCallback } from 'react'
2+
3+
/**
4+
* useClipboard Hook
5+
*
6+
* Provides functionality to copy text to the clipboard and track the copy status.
7+
*
8+
* @returns An object containing the copy function, copy status, and any error encountered.
9+
*/
10+
export const useClipboard = () => {
11+
const [isCopied, setIsCopied] = useState<boolean>(false)
12+
const [error, setError] = useState<Error | null>(null)
13+
14+
/**
15+
* Copies the provided text to the clipboard.
16+
*
17+
* @param text - The text to be copied to the clipboard.
18+
*/
19+
const copy = useCallback(async (text: string) => {
20+
if (!navigator?.clipboard) {
21+
setError(new Error('Clipboard API is not available'))
22+
return
23+
}
24+
25+
try {
26+
await navigator.clipboard.writeText(text)
27+
setIsCopied(true)
28+
setError(null)
29+
// Reset the isCopied state after 2 seconds
30+
setTimeout(() => setIsCopied(false), 2000)
31+
} catch (_error) {
32+
setError(_error as Error)
33+
setIsCopied(false)
34+
}
35+
}, [])
36+
37+
return { copy, isCopied, error }
38+
}

‎packages/docs/gatsby-node.mjs

Copy file name to clipboardExpand all lines: packages/docs/gatsby-node.mjs
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export const createPages = async ({
6868
context: {
6969
id: node.id,
7070
route: node.frontmatter.route,
71+
regex: `/^${node.frontmatter.route}/`,
7172
},
7273
})
7374
})

‎packages/docs/package.json

Copy file name to clipboardExpand all lines: packages/docs/package.json
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"author": "The CoreUI Team (https://github.com/orgs/coreui/people)",
1616
"scripts": {
1717
"api": "rimraf \"content/api/*\" & node build/api.mjs",
18-
"build": "gatsby build",
18+
"build": "gatsby build --prefix-paths",
1919
"develop": "gatsby develop",
2020
"dist": "run-s api build",
2121
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
@@ -33,6 +33,7 @@
3333
"@docsearch/css": "^3.6.2",
3434
"@mdx-js/mdx": "^3.1.0",
3535
"@mdx-js/react": "^3.1.0",
36+
"@stackblitz/sdk": "^1.11.0",
3637
"@types/react-helmet": "^6.1.11",
3738
"gatsby": "^5.13.7",
3839
"gatsby-plugin-google-tagmanager": "^5.13.1",
@@ -58,7 +59,6 @@
5859
"react-helmet": "^6.1.0",
5960
"react-imask": "^7.6.1",
6061
"react-markdown": "^9.0.1",
61-
"remark-mdx": "^1.6.22",
6262
"rimraf": "^6.0.1",
6363
"sass": "^1.80.4"
6464
},
+172Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import React, { FC, ReactNode, useState } from 'react'
2+
import { Highlight, Language } from 'prism-react-renderer'
3+
4+
import CIcon from '@coreui/icons-react'
5+
import { cibCodesandbox, cilCheckAlt, cilCopy } from '@coreui/icons'
6+
import { CNav, CNavLink, CTooltip, useClipboard } from '@coreui/react'
7+
8+
import { openStackBlitzProject } from '../utils/stackblitz'
9+
import { openCodeSandboxProject } from '../utils/codesandbox'
10+
11+
interface CodeSnippets {
12+
js?: string
13+
ts?: string
14+
}
15+
16+
interface ExampleSnippetProps {
17+
children: ReactNode
18+
className?: string
19+
code: string | CodeSnippets
20+
codeSandbox?: boolean
21+
componentName?: string
22+
stackBlitz?: boolean
23+
}
24+
25+
const ExampleSnippet: FC<ExampleSnippetProps> = ({
26+
children,
27+
className = '',
28+
code,
29+
codeSandbox = true,
30+
componentName,
31+
stackBlitz = true,
32+
}) => {
33+
const [language, setLanguage] = useState<'js' | 'ts'>('js')
34+
const { copy, isCopied } = useClipboard()
35+
36+
// Type Guards to determine the shape of 'code' prop
37+
const isCodeString = typeof code === 'string'
38+
const codeJS = isCodeString ? code : code.js || code.ts
39+
const codeTS = isCodeString ? code : code.ts
40+
const hasJS = Boolean(codeJS)
41+
const hasTS = Boolean(codeTS)
42+
43+
// Set initial language based on available code snippets
44+
React.useEffect(() => {
45+
if (!hasJS && hasTS) {
46+
setLanguage('ts')
47+
} else {
48+
setLanguage('js')
49+
}
50+
}, [hasJS, hasTS])
51+
52+
const handleCopy = () => {
53+
const codeToCopy = language === 'js' ? codeJS : codeTS
54+
if (codeToCopy) {
55+
copy(codeToCopy)
56+
}
57+
}
58+
59+
const prismLanguage: Language = language === 'js' ? 'jsx' : 'tsx'
60+
61+
// Determine if both languages are available
62+
const showJSTab = hasJS && (isCodeString || code.js !== code.ts)
63+
const showTSTab = hasTS
64+
65+
return (
66+
<div className="docs-example-snippet">
67+
{children && <div className={`docs-example ${className}`}>{children}</div>}
68+
<div className="highlight-toolbar border-top">
69+
<CNav className="px-3" variant="underline-border">
70+
{showJSTab && (
71+
<CNavLink as="button" active={language === 'js'} onClick={() => setLanguage('js')}>
72+
JavaScript
73+
</CNavLink>
74+
)}
75+
{showTSTab && (
76+
<CNavLink as="button" active={language === 'ts'} onClick={() => setLanguage('ts')}>
77+
TypeScript
78+
</CNavLink>
79+
)}
80+
<span className="ms-auto"></span>
81+
{codeSandbox && (
82+
<CTooltip content="Try it on CodeSandbox">
83+
<button
84+
type="button"
85+
className="btn btn-transparent"
86+
aria-label="Try it on CodeSandbox"
87+
onClick={() =>
88+
openCodeSandboxProject({
89+
name: React.isValidElement(children) && (children as any).type?.name,
90+
language: language,
91+
code: language === 'js' ? codeJS : codeTS || '',
92+
componentName,
93+
})
94+
}
95+
disabled={language === 'ts' && !hasTS}
96+
>
97+
<CIcon icon={cibCodesandbox} />
98+
</button>
99+
</CTooltip>
100+
)}
101+
{stackBlitz && (
102+
<CTooltip content="Try it on StackBlitz">
103+
<button
104+
type="button"
105+
className="btn btn-transparent px-1"
106+
aria-label="Try it on StackBlitz"
107+
onClick={() =>
108+
openStackBlitzProject({
109+
name: React.isValidElement(children) && (children as any).type?.name,
110+
language: language,
111+
code: language === 'js' ? codeJS : codeTS || '',
112+
componentName,
113+
})
114+
}
115+
disabled={language === 'ts' && !hasTS}
116+
>
117+
<svg
118+
className="icon"
119+
width="56"
120+
height="78"
121+
viewBox="0 0 56 78"
122+
fill="none"
123+
xmlns="http://www.w3.org/2000/svg"
124+
>
125+
<path
126+
d="M23.4273 48.2853C23.7931 47.5845 23.0614 46.8837 22.3298 46.8837H1.11228C0.0148224 46.8837 -0.350997 45.8326 0.380642 45.1318L40.9866 0.282084C41.7182 -0.418693 43.1815 0.282084 42.8157 1.33325L32.9386 30.0651C32.5727 30.7659 32.9386 31.4666 33.6702 31.4666H54.8877C55.9852 31.4666 56.351 32.5178 55.6194 33.2186L15.0134 77.7179C14.2818 78.4187 12.8185 77.7179 13.1843 76.6667L23.4273 48.2853Z"
127+
fill="currentColor"
128+
/>
129+
</svg>
130+
</button>
131+
</CTooltip>
132+
)}
133+
<CTooltip content={isCopied ? 'Copied' : 'Copy to clipboard'}>
134+
<button
135+
type="button"
136+
className="btn btn-transparent px-1"
137+
aria-label="Copy to clipboard"
138+
onClick={handleCopy}
139+
disabled={(language === 'js' && !hasJS) || (language === 'ts' && !hasTS)}
140+
>
141+
<CIcon icon={isCopied ? cilCheckAlt : cilCopy} />
142+
</button>
143+
</CTooltip>
144+
</CNav>
145+
</div>
146+
147+
<div className="highlight">
148+
<Highlight
149+
code={language === 'js' ? codeJS : codeTS || ''}
150+
language={prismLanguage}
151+
theme={{ plain: {}, styles: [] }}
152+
>
153+
{({ className, style, tokens, getLineProps, getTokenProps }) => (
154+
<pre className={className} style={style}>
155+
{tokens.map((line, i) => (
156+
<div {...getLineProps({ line, key: i })} key={i}>
157+
{line.map((token, key) => (
158+
<span {...getTokenProps({ token, key })} key={key} />
159+
))}
160+
</div>
161+
))}
162+
</pre>
163+
)}
164+
</Highlight>
165+
</div>
166+
</div>
167+
)
168+
}
169+
170+
ExampleSnippet.displayName = 'ExampleSnippet'
171+
172+
export default ExampleSnippet

‎packages/docs/src/components/index.ts

Copy file name to clipboardExpand all lines: packages/docs/src/components/index.ts
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Callout from './Callout'
44
import CodeBlock from './CodeBlock'
55
import ClassNamesDocs from './ClassNamesDocs'
66
import Example from './Example'
7+
import ExampleSnippet from './ExampleSnippet'
78
import Footer from './Footer'
89
import Header from './Header'
910
import JSXDocs from './JSXDocs'
@@ -20,6 +21,7 @@ export {
2021
CodeBlock,
2122
ClassNamesDocs,
2223
Example,
24+
ExampleSnippet,
2325
Footer,
2426
Header,
2527
JSXDocs,

‎packages/docs/src/pages/404.tsx

Copy file name to clipboardExpand all lines: packages/docs/src/pages/404.tsx
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react'
22
import { graphql, useStaticQuery } from 'gatsby'
33
import { CButton } from '@coreui/react/src/index'
44

5-
import Seo from './../components/Seo'
5+
import Seo from '../components/Seo'
66

77
const NotFoundPage = () => {
88
const { site } = useStaticQuery(query)

‎packages/docs/src/styles/_component-examples.scss

Copy file name to clipboardExpand all lines: packages/docs/src/styles/_component-examples.scss
+31-1Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,39 @@
2727
.docs-example-snippet {
2828
border: solid var(--cui-border-color);
2929
border-width: 1px 0;
30+
margin: 0 ($cd-gutter-x * -.5) 1rem ($cd-gutter-x * -.5);
31+
padding: 0;
32+
@include border-radius(0);
3033

31-
@include media-breakpoint-up(md) {
34+
.docs-example {
35+
margin: 0;
36+
padding: 1rem;
37+
border-width: 0 1px 0 0;
38+
}
39+
40+
.highlight-toolbar {
41+
border-top: 1px solid var(--cui-border-color);
42+
}
43+
44+
.highlight {
45+
margin: 0;
46+
padding: 1rem;
47+
}
48+
49+
.docs-example,
50+
.highlight {
51+
border: 0
52+
}
53+
54+
.highlight {
55+
margin-bottom: 0;
56+
@include border-top-radius(0);
57+
}
58+
59+
@include media-breakpoint-up(sm) {
60+
margin: 0 0 1rem 0;
3261
border-width: 1px;
62+
@include border-radius(var(--cui-border-radius));
3363
}
3464
}
3565

‎packages/docs/src/templates/DocsLayout.tsx

Copy file name to clipboardExpand all lines: packages/docs/src/templates/DocsLayout.tsx
+8-11Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,13 @@ const DocsLayout: FC<DocsLayoutProps> = ({ children, data, location, pageContext
3232
const frameworks = other_frameworks ? other_frameworks.split(', ') : false
3333
const otherFrameworks = JSON.parse(JSON.stringify(jsonData))
3434

35-
const hasNav = data.allMdx.edges.length > 1
36-
const hasNavAccesibility = data.allMdx.edges.filter((edge: any) =>
37-
edge.node.frontmatter.title.includes('Accesibility'),
38-
).length
39-
const hasNavAPI = data.allMdx.edges.filter((edge: any) =>
40-
edge.node.frontmatter.title.includes('API'),
41-
).length
42-
const hasNavCustomizing = data.allMdx.edges.filter((edge: any) =>
43-
edge.node.frontmatter.title.includes('Customizing'),
44-
).length
35+
const hasNav = data?.allMdx?.edges.length > 1
36+
const hasNavAccesibility =
37+
hasNav && data.allMdx.edges.some((edge: any) => edge.node.fields.slug.includes('accesibility'))
38+
const hasNavAPI =
39+
hasNav && data.allMdx.edges.some((edge: any) => edge.node.fields.slug.includes('api'))
40+
const hasNavCustomizing =
41+
hasNav && data.allMdx.edges.some((edge: any) => edge.node.fields.slug.includes('customizing'))
4542

4643
return (
4744
<>
@@ -52,7 +49,7 @@ const DocsLayout: FC<DocsLayoutProps> = ({ children, data, location, pageContext
5249
<CNav className="ms-lg-4 docs-nav bg-body" variant="underline-border">
5350
<CNavItem>
5451
<CNavLink href={`${route}`} active={route === location.pathname}>
55-
Overview
52+
Features
5653
</CNavLink>
5754
</CNavItem>
5855
{hasNavAPI && (

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.