feat: add copy-to-clipboard button to code blocks
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { createFileRoute, Link } from '@tanstack/react-router'
|
import { createFileRoute, Link } from '@tanstack/react-router'
|
||||||
|
import { useState } from 'react'
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
import remarkGfm from 'remark-gfm'
|
import remarkGfm from 'remark-gfm'
|
||||||
import SyntaxHighlighter from 'react-syntax-highlighter'
|
import SyntaxHighlighter from 'react-syntax-highlighter'
|
||||||
@@ -51,12 +52,44 @@ function CodeBlock({
|
|||||||
readonly className?: string
|
readonly className?: string
|
||||||
readonly children?: React.ReactNode
|
readonly children?: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
|
const [copied, setCopied] = useState(false)
|
||||||
const match = /language-(\w+)/.exec(className ?? '')
|
const match = /language-(\w+)/.exec(className ?? '')
|
||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
return <code className={className}>{children}</code>
|
return <code className={className}>{children}</code>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleCopy = () => {
|
||||||
|
void navigator.clipboard.writeText(String(children).replace(/\n$/, ''))
|
||||||
|
setCopied(true)
|
||||||
|
setTimeout(() => setCopied(false), 2000)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div style={{ position: 'relative', margin: '1.5rem 0' }}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleCopy}
|
||||||
|
aria-label="Copy code"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '0.6rem',
|
||||||
|
right: '0.6rem',
|
||||||
|
zIndex: 10,
|
||||||
|
background: 'var(--surface-strong)',
|
||||||
|
border: '1px solid var(--line)',
|
||||||
|
borderRadius: '0.4rem',
|
||||||
|
color: copied ? 'var(--lagoon)' : 'var(--sea-ink-soft)',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '0.7rem',
|
||||||
|
fontFamily: 'Manrope, sans-serif',
|
||||||
|
letterSpacing: '0.05em',
|
||||||
|
padding: '0.25rem 0.6rem',
|
||||||
|
transition: 'color 0.2s',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{copied ? 'Copied!' : 'Copy'}
|
||||||
|
</button>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
style={atomOneDark}
|
style={atomOneDark}
|
||||||
language={match[1]}
|
language={match[1]}
|
||||||
@@ -79,6 +112,7 @@ function CodeBlock({
|
|||||||
>
|
>
|
||||||
{String(children).replace(/\n$/, '')}
|
{String(children).replace(/\n$/, '')}
|
||||||
</SyntaxHighlighter>
|
</SyntaxHighlighter>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user