Standard.site landing page built in Next.js
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 122 lines 5.4 kB view raw
1'use client' 2 3import { useState, useRef, useEffect } from 'react' 4import { usePathname } from 'next/navigation' 5import { useDocsMarkdown, useDocsAtUri } from './DocsMarkdownContext' 6 7export function DocsPageMenu() { 8 const [open, setOpen] = useState(false) 9 const [copied, setCopied] = useState(false) 10 const menuRef = useRef<HTMLDivElement>(null) 11 const pathname = usePathname() 12 const markdown = useDocsMarkdown() 13 const atUri = useDocsAtUri() 14 15 const slug = (() => { 16 const path = pathname.split(/[?#]/)[0] 17 if (!path.startsWith('/docs/')) return null 18 const parts = path.split('/').filter(Boolean) 19 if (parts.length <= 1) return null 20 return parts.slice(1).join('/') 21 })() 22 23 useEffect(() => { 24 if (!open) return 25 26 function handleClickOutside(e: MouseEvent) { 27 if (menuRef.current && !menuRef.current.contains(e.target as Node)) { 28 setOpen(false) 29 } 30 } 31 32 document.addEventListener('mousedown', handleClickOutside) 33 return () => document.removeEventListener('mousedown', handleClickOutside) 34 }, [open]) 35 36 if (!slug) return null 37 38 const handleCopyMarkdown = async () => { 39 if (!markdown) return 40 41 try { 42 await navigator.clipboard.writeText(markdown) 43 setCopied(true) 44 setTimeout(() => { 45 setCopied(false) 46 setOpen(false) 47 }, 1500) 48 } catch { 49 setOpen(false) 50 } 51 } 52 53 const handleViewMarkdown = () => { 54 if (!markdown) return 55 56 const blob = new Blob([markdown], { type: 'text/plain' }) 57 window.open(URL.createObjectURL(blob), '_blank') 58 setOpen(false) 59 } 60 61 return ( 62 <div ref={menuRef} className="relative"> 63 <button 64 onClick={() => setOpen(!open)} 65 className="flex items-center justify-center text-muted hover:text-base-content transition-colors p-1 rounded-md hover:bg-base-200 mt-1 size-8" 66 aria-label="Page options" 67 > 68 <svg className="size-4" fill="currentColor" viewBox="0 0 20 20"> 69 <path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" /> 70 </svg> 71 </button> 72 73 {open && ( 74 <div className="absolute right-0 top-full mt-2 p-0.5 bg-base-200 border border-border grid gap-px rounded-xl shadow-lg z-20 min-w-48"> 75 <button 76 onClick={handleCopyMarkdown} 77 className="w-full flex items-center gap-2 px-3 py-2 text-sm text-muted hover:text-base-content hover:bg-base-300 rounded-lg transition-colors" 78 > 79 {copied ? ( 80 <> 81 <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> 82 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" /> 83 </svg> 84 Copied! 85 </> 86 ) : ( 87 <> 88 <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> 89 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" /> 90 </svg> 91 Copy as Markdown 92 </> 93 )} 94 </button> 95 <button 96 onClick={handleViewMarkdown} 97 className="w-full flex items-center gap-2 px-3 py-2 text-sm text-muted hover:text-base-content hover:bg-base-300 rounded-lg transition-colors" 98 > 99 <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> 100 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> 101 </svg> 102 View raw Markdown 103 </button> 104 {atUri && ( 105 <a 106 href={`https://pdsls.dev/${atUri}`} 107 target="_blank" 108 rel="noopener noreferrer" 109 onClick={() => setOpen(false)} 110 className="w-full flex items-center gap-2 px-3 py-2 text-sm text-muted hover:text-base-content hover:bg-base-300 rounded-lg transition-colors" 111 > 112 <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> 113 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" /> 114 </svg> 115 View on pdsls.dev 116 </a> 117 )} 118 </div> 119 )} 120 </div> 121 ) 122}