Skip to content

Commit c43a0cb

Browse files
committed
[Docs Site] Add Copy Page dropdown for Markdown
1 parent f65235a commit c43a0cb

File tree

3 files changed

+108
-0
lines changed

3 files changed

+108
-0
lines changed

src/components/CopyPageButton.tsx

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import {
2+
useFloating,
3+
useInteractions,
4+
useClick,
5+
useDismiss,
6+
shift,
7+
offset,
8+
autoUpdate,
9+
FloatingPortal,
10+
} from "@floating-ui/react";
11+
import { useState } from "react";
12+
import { PiMarkdownLogo, PiClipboardTextLight, PiArrowSquareOutLight } from "react-icons/pi";
13+
14+
export default function CopyPageButton() {
15+
const [isOpen, setIsOpen] = useState(false);
16+
17+
const { refs, floatingStyles, context } = useFloating({
18+
open: isOpen,
19+
onOpenChange: setIsOpen,
20+
middleware: [shift(), offset(5)],
21+
whileElementsMounted: autoUpdate,
22+
});
23+
24+
const click = useClick(context);
25+
const dismiss = useDismiss(context);
26+
27+
const { getReferenceProps, getFloatingProps } = useInteractions([
28+
click,
29+
dismiss,
30+
]);
31+
32+
const handleViewMarkdown = () => {
33+
const markdownUrl = new URL("index.md", window.location.href).toString();
34+
window.open(markdownUrl, "_blank");
35+
};
36+
37+
const handleCopyMarkdown = async () => {
38+
const markdownUrl = new URL("index.md", window.location.href).toString();
39+
try {
40+
const response = await fetch(markdownUrl);
41+
const markdown = await response.text();
42+
await navigator.clipboard.writeText(markdown);
43+
} catch (error) {
44+
console.error("Failed to copy Markdown:", error);
45+
}
46+
};
47+
48+
const options = [
49+
{
50+
label: "Copy Page as Markdown",
51+
description: "Copy the raw Markdown content to clipboard",
52+
icon: PiClipboardTextLight,
53+
onClick: handleCopyMarkdown,
54+
},
55+
{
56+
label: "View Page as Markdown",
57+
description: "Open the Markdown file in a new tab",
58+
icon: PiArrowSquareOutLight,
59+
onClick: handleViewMarkdown,
60+
},
61+
];
62+
63+
return (
64+
<>
65+
<button
66+
ref={refs.setReference}
67+
{...getReferenceProps()}
68+
className="inline-flex bg-transparent items-center justify-center text-sm h-8 gap-2 rounded border border-[--sl-color-hairline] px-3 text-black"
69+
>
70+
Copy Page
71+
<PiMarkdownLogo />
72+
</button>
73+
{isOpen && (
74+
<FloatingPortal>
75+
<ul
76+
ref={refs.setFloating}
77+
style={floatingStyles}
78+
{...getFloatingProps()}
79+
className="list-none rounded border border-[--sl-color-hairline] bg-[--sl-color-bg] pl-0 shadow-md min-w-[240px]"
80+
>
81+
{options.map(({ label, description, icon: Icon, onClick }) => (
82+
<li key={label}>
83+
<button
84+
onClick={onClick}
85+
className="block w-full px-3 bg-transparent py-2 text-left text-black no-underline hover:bg-[--sl-color-bg-nav]"
86+
>
87+
<div className="flex items-center gap-2 text-sm">
88+
<Icon className="h-4 w-4" />
89+
{label}
90+
</div>
91+
<div className="text-xs text-[--sl-color-gray-3] mt-0.5 ml-6">
92+
{description}
93+
</div>
94+
</button>
95+
</li>
96+
))}
97+
</ul>
98+
</FloatingPortal>
99+
)}
100+
</>
101+
);
102+
}

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export { default as AvailableNotifications } from "./AvailableNotifications.astr
1111
export { default as CompatibilityFlag } from "./CompatibilityFlag.astro";
1212
export { default as CompatibilityFlags } from "./CompatibilityFlags.astro";
1313
export { default as ComponentsUsage } from "./ComponentsUsage.astro";
14+
export { default as CopyPageButton } from "./CopyPageButton";
1415
export { default as CURL } from "./CURL.astro";
1516
export { default as Description } from "./Description.astro";
1617
export { default as Details } from "./Details.astro";

src/components/overrides/PageTitle.astro

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Aside } from "@astrojs/starlight/components";
99
import Description from "~/components/Description.astro";
1010
import SpotlightAuthorDetails from "~/components/SpotlightAuthorDetails.astro";
1111
import LastReviewed from "~/components/LastReviewed.astro";
12+
import CopyPageButton from "~/components/CopyPageButton.tsx";
1213
1314
import { getEntry } from "astro:content";
1415
@@ -113,6 +114,10 @@ const hideBreadcrumbs = Astro.locals.starlightRoute.hideBreadcrumbs;
113114

114115
{component && <Aside>To see a list of pages this component is used on, please visit the <a href={`/style-guide/components/usage/#${component.toLowerCase()}`}>usage page</a>.</Aside>}
115116

117+
<div class="flex justify-end items-center">
118+
<CopyPageButton client:idle />
119+
</div>
120+
116121
<style>
117122
:root {
118123
--color-link-breadcrumbs: var(--sl-color-text-accent);

0 commit comments

Comments
 (0)