Skip to content

Commit 7fd4a97

Browse files
cijiugechucsr632
andauthored
feat(theme-doc): add copy-to-clipboard to CodeBlock (#86)
* feat(theme-doc): add copy-to-clipboard * fix: use css module * simplify copy button styles * fix copy button background * improve code block style * chore: rename copy * border style Co-authored-by: csr632 <[email protected]>
1 parent 719e913 commit 7fd4a97

File tree

6 files changed

+87
-3
lines changed

6 files changed

+87
-3
lines changed

packages/theme-doc/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"babel-plugin-import": "^1.13.3",
4747
"chokidar": "^3.5.1",
4848
"concurrently": "^7.3.0",
49+
"copy-to-clipboard": "^3.3.2",
4950
"fs-extra": "^10.1.0",
5051
"github-markdown-css": "^4.0.0",
5152
"github-slugger": "^1.3.0",

packages/theme-doc/src/Layout/Demo/index.module.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,5 @@
5858
}
5959

6060
.code .codeInner {
61-
padding: 16px 32px;
62-
display: inline-block;
61+
padding: 0px 8px;
6362
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
.pre {
2+
position: relative;
3+
4+
.copy {
5+
opacity: 0;
6+
position: absolute;
7+
top: 8px;
8+
right: 8px;
9+
z-index: 3;
10+
display: block;
11+
border-radius: 4px;
12+
border: 2px solid #c3c5c6;
13+
width: 40px;
14+
height: 40px;
15+
cursor: pointer;
16+
background-position: 50%;
17+
background-size: 20px;
18+
background-repeat: no-repeat;
19+
background-color: rgba(255, 255, 255, 0.05);
20+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E");
21+
transition: opacity 0.4s;
22+
}
23+
24+
.copied {
25+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E");
26+
27+
&::before {
28+
position: absolute;
29+
display: inline-block;
30+
right: 120%;
31+
top: 50%;
32+
transform: translate(0, -50%);
33+
color: #999988;
34+
font-size: 12px;
35+
font-weight: 500;
36+
font-family: consolas;
37+
white-space: nowrap;
38+
content: 'Copied';
39+
}
40+
}
41+
42+
&:hover {
43+
.copy {
44+
opacity: 1;
45+
}
46+
}
47+
}

packages/theme-doc/src/Layout/MDX/CodeBlock.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from 'react'
22
import Highlight, { defaultProps } from 'prism-react-renderer'
33
import theme from 'prism-react-renderer/themes/github'
44
import type { Language } from 'prism-react-renderer'
5+
import { useCopyToClipBoard } from './useCopyToClipBoard'
6+
import s from './CodeBlock.module.less'
57

68
// copied from https://mdxjs.com/guides/syntax-highlighting
79

@@ -18,6 +20,9 @@ const CodeBlock = ({
1820
}: Props) => {
1921
// with ```language\n``` md syntax, mdx will pass language in className
2022
const language = className?.replace(/language-/, '') as Language
23+
24+
const { hasCopied, copyToClipBoard } = useCopyToClipBoard()
25+
2126
return (
2227
<Highlight
2328
{...defaultProps}
@@ -27,9 +32,14 @@ const CodeBlock = ({
2732
>
2833
{({ className, style, tokens, getLineProps, getTokenProps }) => (
2934
<pre
30-
className={className}
35+
className={`${className} ${s.pre}`}
3136
style={propStyle ? { ...style, ...propStyle } : style}
3237
>
38+
<button
39+
className={`${s.copy} ${hasCopied ? s.copied : ''}`}
40+
onClick={() => copyToClipBoard(children)}
41+
></button>
42+
3343
{tokens.map((line, i) => (
3444
<div key={i} {...getLineProps({ line, key: i })}>
3545
{line.map((token, key) => (
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { useState, useRef } from 'react'
2+
import writeText from 'copy-to-clipboard'
3+
4+
const useCopyToClipBoard = () => {
5+
const timer = useRef<any>()
6+
const [hasCopied, setHasCopied] = useState(false)
7+
8+
// allow users to copy multiple times
9+
// in a short interval of time.
10+
const copyToClipBoard = (value: string) => {
11+
setHasCopied(true)
12+
clearTimeout(timer.current)
13+
writeText(value)
14+
timer.current = setTimeout(() => {
15+
setHasCopied(false)
16+
}, 2 * 1000)
17+
}
18+
19+
return {
20+
hasCopied,
21+
copyToClipBoard,
22+
}
23+
}
24+
25+
export { useCopyToClipBoard }

pnpm-lock.yaml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)