Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions src/components/docPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {serverContext} from 'sentry-docs/serverContext';
import {FrontMatter} from 'sentry-docs/types';
import {PaginationNavNode} from 'sentry-docs/types/paginationNavNode';
import {isNotNil} from 'sentry-docs/utils';
import {getMarkdownContent} from 'sentry-docs/utils/getMarkdownContent';
import {getUnversionedPath} from 'sentry-docs/versioning';

import './type.scss';
Expand All @@ -15,6 +16,7 @@ import {CodeContextProvider} from '../codeContext';
import {DocFeedback} from '../docFeedback';
import {GitHubCTA} from '../githubCTA';
import {Header} from '../header';
import {MarkdownButton} from '../markdownButton';
import Mermaid from '../mermaid';
import {PaginationNav} from '../paginationNav';
import {PlatformSdkDetail} from '../platformSdkDetail';
Expand All @@ -34,7 +36,7 @@ type Props = {
sidebar?: ReactNode;
};

export function DocPage({
export async function DocPage({
children,
frontMatter,
notoc = false,
Expand All @@ -58,6 +60,9 @@ export function DocPage({

const leafNode = nodeForPath(rootNode, unversionedPath);

// Fetch markdown content for the current page
const markdownContent = await getMarkdownContent();

return (
<div className="tw-app">
<Header pathname={pathname} searchPlatforms={searchPlatforms} />
Expand All @@ -83,10 +88,15 @@ export function DocPage({
</div>
{leafNode && <Breadcrumbs leafNode={leafNode} />}
<div>
<hgroup>
<h1>{frontMatter.title}</h1>
<h2>{frontMatter.description}</h2>
</hgroup>
<div className="flex items-start justify-between">
<hgroup>
<h1>{frontMatter.title}</h1>
<h2>{frontMatter.description}</h2>
</hgroup>
<div className="flex-shrink-0 mt-11">
<MarkdownButton markdownContent={markdownContent} />
</div>
</div>
{/* This exact id is important for Algolia indexing */}
<div id="main">
<CodeContextProvider>{children}</CodeContextProvider>
Expand Down
128 changes: 128 additions & 0 deletions src/components/markdownButton/client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
'use client';

import {useEffect, useRef, useState} from 'react';
import {Button} from '@radix-ui/themes';

type MarkdownButtonClientProps = {
markdownContent: string | null;
};

export function MarkdownButtonClient({markdownContent}: MarkdownButtonClientProps) {
const [showDropdown, setShowDropdown] = useState(false);
const [copied, setCopied] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);

const isMarkdownAvailable = !!markdownContent;

useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setShowDropdown(false);
}
}

document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);

const handleCopyMarkdown = async () => {
if (!markdownContent) return;

try {
await navigator.clipboard.writeText(markdownContent);
setCopied(true);
setShowDropdown(false);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
return;
}
};

const handleViewMarkdown = () => {
if (!markdownContent) return;

const blob = new Blob([markdownContent], {type: 'text/markdown'});
const url = URL.createObjectURL(blob);
window.open(url, '_blank');
setShowDropdown(false);
};

const toggleDropdown = () => {
setShowDropdown(!showDropdown);
};

return (
<div className="markdown-button-container">
<div className="relative" ref={dropdownRef}>
<Button
variant="ghost"
color="gray"
size="4"
radius="medium"
onClick={toggleDropdown}
className={`inline-flex items-center px-4 py-2 font-medium rounded-md ${
isMarkdownAvailable
? ' text-white'
: ' text-white opacity-50 cursor-not-allowed'
}`}
aria-label="Copy Page"
disabled={!isMarkdownAvailable}
>
{copied ? 'Copied!' : isMarkdownAvailable ? 'Copy Page' : 'Copy Unavailable'}
<svg
className="w-4 h-4 ml-1.5"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 9L12 15L18 9"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</Button>

{showDropdown && (
<div className="absolute right-0 mt-1 w-72 bg-black rounded-md shadow-lg z-10 border border-gray-700">
<button
onClick={handleCopyMarkdown}
className="w-full text-left px-4 py-3 text-white hover:bg-gray-800 flex items-start"
disabled={!isMarkdownAvailable}
>
<div>
<div className="font-medium">Copy Page as Markdown</div>
<div className="text-sm text-gray-400">
Copy the raw Markdown content to clipboard
</div>
</div>
</button>

<button
onClick={handleViewMarkdown}
className="w-full text-left px-4 py-3 text-white hover:bg-gray-800 flex items-start"
disabled={!isMarkdownAvailable}
>
<div>
<div className="font-medium">View Page as Markdown</div>
<div className="text-sm text-gray-400">
Open the Markdown file in a new tab
</div>
</div>
</button>
</div>
)}
</div>

<style>{`
.markdown-button-container {
position: relative;
}
`}</style>
</div>
);
}
9 changes: 9 additions & 0 deletions src/components/markdownButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {MarkdownButtonClient} from './client';

type MarkdownButtonProps = {
markdownContent: string | null;
};

export function MarkdownButton({markdownContent}: MarkdownButtonProps) {
return <MarkdownButtonClient markdownContent={markdownContent} />;
}
Loading
Loading