Skip to content

Commit 180364f

Browse files
authored
Add Copy Page button (#8341)
1 parent 427f24d commit 180364f

File tree

1 file changed

+48
-1
lines changed

1 file changed

+48
-1
lines changed

src/components/PageHeading.tsx

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@ import Tag from 'components/Tag';
1414
import {H1} from './MDX/Heading';
1515
import type {RouteTag, RouteItem} from './Layout/getRouteMeta';
1616
import * as React from 'react';
17+
import {useState, useEffect} from 'react';
18+
import {useRouter} from 'next/router';
1719
import {IconCanary} from './Icon/IconCanary';
1820
import {IconExperimental} from './Icon/IconExperimental';
21+
import {IconCopy} from './Icon/IconCopy';
22+
import {Button} from './Button';
1923

2024
interface PageHeadingProps {
2125
title: string;
@@ -27,6 +31,44 @@ interface PageHeadingProps {
2731
breadcrumbs: RouteItem[];
2832
}
2933

34+
function CopyAsMarkdownButton() {
35+
const {asPath} = useRouter();
36+
const [copied, setCopied] = useState(false);
37+
38+
useEffect(() => {
39+
if (!copied) return;
40+
const timer = setTimeout(() => setCopied(false), 2000);
41+
return () => clearTimeout(timer);
42+
}, [copied]);
43+
44+
async function handleCopy() {
45+
const cleanPath = asPath.split(/[?#]/)[0];
46+
try {
47+
const res = await fetch(cleanPath + '.md');
48+
if (!res.ok) return;
49+
const text = await res.text();
50+
await navigator.clipboard.writeText(text);
51+
setCopied(true);
52+
} catch {
53+
// Silently fail
54+
}
55+
}
56+
57+
return (
58+
<Button onClick={handleCopy} className="text-sm py-1 px-3">
59+
<IconCopy className="w-3.5 h-3.5 me-1.5" />
60+
{copied ? (
61+
'Copied!'
62+
) : (
63+
<>
64+
<span className="hidden sm:inline">Copy page</span>
65+
<span className="sm:hidden">Copy</span>
66+
</>
67+
)}
68+
</Button>
69+
);
70+
}
71+
3072
function PageHeading({
3173
title,
3274
status,
@@ -37,7 +79,12 @@ function PageHeading({
3779
return (
3880
<div className="px-5 sm:px-12 pt-3.5">
3981
<div className="max-w-4xl ms-0 2xl:mx-auto">
40-
{breadcrumbs ? <Breadcrumbs breadcrumbs={breadcrumbs} /> : null}
82+
<div className="flex justify-between items-start">
83+
<div className="flex-1">
84+
{breadcrumbs ? <Breadcrumbs breadcrumbs={breadcrumbs} /> : null}
85+
</div>
86+
<CopyAsMarkdownButton />
87+
</div>
4188
<H1 className="mt-0 text-primary dark:text-primary-dark -mx-.5 break-words">
4289
{title}
4390
{version === 'canary' && (

0 commit comments

Comments
 (0)