@@ -4,7 +4,9 @@ import ReactMarkdown from 'react-markdown';
44import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' ;
55import { customTheme } from '../utils/customTheme' ;
66import PostMetadata from '../components/PostMetadata' ;
7- import { ArrowLeftIcon , ArrowSquareOutIcon } from '@phosphor-icons/react' ;
7+ import CodeModal from '../components/CodeModal' ;
8+ import Toast from '../components/Toast' ;
9+ import { ArrowLeftIcon , ArrowSquareOutIcon , ClipboardIcon , ArrowsOutSimpleIcon } from '@phosphor-icons/react' ;
810
911const LinkRenderer = ( { href, children } ) => {
1012 const isExternal = href . startsWith ( 'http' ) || href . startsWith ( 'https' ) ;
@@ -15,22 +17,35 @@ const LinkRenderer = ({ href, children }) => {
1517 ) ;
1618} ;
1719
18-
19-
20- const CodeBlock = ( { node, inline, className, children, ...props } ) => {
20+ const CodeBlock = ( { node, inline, className, children, openModal, showToast, ...props } ) => {
2121 const match = / l a n g u a g e - ( \w + ) / . exec ( className || '' ) ;
22+ const handleCopy = ( ) => {
23+ navigator . clipboard . writeText ( String ( children ) ) ;
24+ showToast ( 'Success' , 'Copied to clipboard!' ) ;
25+ } ;
26+
2227 return ! inline && match ? (
23- < SyntaxHighlighter
24- style = { customTheme }
25- language = { match [ 1 ] }
26- PreTag = "div"
27- { ...props }
28- codeTagProps = { { style : { fontFamily : "'JetBrains Mono', monospace" } } }
29- >
30- { String ( children ) . replace ( / \n $ / , '' ) }
31- </ SyntaxHighlighter >
28+ < div className = "relative" >
29+ < div className = "absolute top-2 right-2 flex gap-2" >
30+ < button onClick = { ( ) => openModal ( String ( children ) . replace ( / \n $ / , '' ) ) } className = "text-white bg-gray-700 p-1 rounded opacity-75 hover:opacity-100" >
31+ < ArrowsOutSimpleIcon size = { 20 } />
32+ </ button >
33+ < button onClick = { handleCopy } className = "text-white bg-gray-700 p-1 rounded opacity-75 hover:opacity-100" >
34+ < ClipboardIcon size = { 20 } />
35+ </ button >
36+ </ div >
37+ < SyntaxHighlighter
38+ style = { customTheme }
39+ language = { match [ 1 ] }
40+ PreTag = "div"
41+ { ...props }
42+ codeTagProps = { { style : { fontFamily : "'JetBrains Mono', monospace" } } }
43+ >
44+ { String ( children ) . replace ( / \n $ / , '' ) }
45+ </ SyntaxHighlighter >
46+ </ div >
3247 ) : (
33- < code className = { `${ className } font-mono bg-gray-800 ` } { ...props } >
48+ < code className = { `${ className } font-mono` } { ...props } >
3449 { children }
3550 </ code >
3651 ) ;
@@ -43,6 +58,23 @@ const BlogPostPage = () => {
4358 const [ readingProgress , setReadingProgress ] = useState ( 0 ) ;
4459 const [ isAtTop , setIsAtTop ] = useState ( true ) ; // New state for tracking if at top
4560 const contentRef = useRef ( null ) ;
61+ const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
62+ const [ modalContent , setModalContent ] = useState ( '' ) ;
63+ const [ toastMessage , setToastMessage ] = useState ( '' ) ;
64+
65+ const openModal = ( content ) => {
66+ setModalContent ( content ) ;
67+ setIsModalOpen ( true ) ;
68+ } ;
69+
70+ const closeModal = ( ) => {
71+ setIsModalOpen ( false ) ;
72+ setModalContent ( '' ) ;
73+ } ;
74+
75+ const showToast = ( title , message ) => {
76+ setToastMessage ( { title, message } ) ;
77+ } ;
4678
4779 useEffect ( ( ) => {
4880 const fetchPost = async ( ) => {
@@ -144,14 +176,18 @@ const BlogPostPage = () => {
144176 < ArrowLeftIcon size = { 24 } /> Back to Home
145177 </ Link >
146178 < div ref = { contentRef } className = "prose prose-xl prose-dark max-w-none" >
147- < ReactMarkdown components = { { a : LinkRenderer , code : CodeBlock } } > { post . body } </ ReactMarkdown >
179+ < ReactMarkdown components = { { a : LinkRenderer , code : ( props ) => < CodeBlock { ... props } openModal = { openModal } showToast = { showToast } /> } } > { post . body } </ ReactMarkdown >
148180 </ div >
149181 </ div >
150182 < div className = "hidden lg:block" >
151183 < PostMetadata metadata = { post . attributes } readingProgress = { readingProgress } isAtTop = { isAtTop } overrideDate = { post . attributes . date } updatedDate = { post . attributes . updated } />
152184 </ div >
153185 </ div >
154186 </ div >
187+ < CodeModal isOpen = { isModalOpen } onClose = { closeModal } >
188+ { modalContent }
189+ </ CodeModal >
190+ { toastMessage && < Toast title = { toastMessage . title } message = { toastMessage . message } duration = { 3000 } onClose = { ( ) => setToastMessage ( null ) } /> }
155191 </ div >
156192 ) ;
157193} ;
0 commit comments