Files
tasknote/client/src/components/ModalMarkdown/index.tsx
T
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>RMCamposrmcampos
f41e34486a Add Source and Copy buttons to note markdown modal (#21)
* Initial plan

* Add Source and Copy buttons to ModalMarkdown for note source code view

Co-authored-by: RMCampos <2219519+RMCampos@users.noreply.github.com>

* feat: add healthcheck do native image

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: RMCampos <2219519+RMCampos@users.noreply.github.com>
Co-authored-by: Ricardo Campos <ricardompcampos@gmail.com>
2026-02-27 19:13:53 -03:00

100 lines
3.0 KiB
TypeScript

import React, { useState } from 'react';
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import './style.css';
type Props = {
show: boolean;
title: string;
markdownText: string;
onHide: () => void;
};
/**
* Renders a modal with Markdown format text.
*
* @param {Props} props The ModalMarkdown props with show, title and text.
* @param {boolean} props.show Defines when to display the modal.
* @param {string} props.title The modal title to be displayed.
* @param {string} props.markdownText The Markdown text to be rendered.
* @param {Function} props.onHide The function to be called when closing the modal.
* @returns {React.ReactNode} the Markdown component rendered.
*/
const ModalMarkdown: React.FC<Props> = (props: Props): React.ReactNode => {
const [showSource, setShowSource] = useState<boolean>(false);
const [copied, setCopied] = useState<boolean>(false);
const handleToggleSource = () => setShowSource(prev => !prev);
const handleCopy = () => {
navigator.clipboard.writeText(props.markdownText).then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}).catch(() => {
// clipboard write failed silently; no state change
});
};
const handleHide = () => {
setShowSource(false);
setCopied(false);
props.onHide();
};
return props.show
? (
<Modal
show={props.show}
onHide={handleHide}
backdrop="static"
keyboard={false}
size="xl"
aria-labelledby="markdown-modal-content"
>
<Modal.Header closeButton>
<Modal.Title
id="markdown-modal-content"
data-testid="modal-header-title"
>
{props.title === '' ? 'No title' : props.title}
</Modal.Title>
</Modal.Header>
<Modal.Body className="markdown-modal">
{showSource
? (
<pre className="markdown-source" data-testid="markdown-source-view">
{props.markdownText}
</pre>
)
: (
<Markdown remarkPlugins={[remarkGfm]}>{props.markdownText}</Markdown>
)}
</Modal.Body>
<Modal.Footer className="d-flex flex-wrap gap-2 justify-content-end">
<Button variant="outline-secondary" onClick={handleHide}>
Close
</Button>
<Button
variant={showSource ? 'info' : 'outline-info'}
onClick={handleToggleSource}
data-testid="modal-source-button"
>
Source
</Button>
<Button
variant="outline-primary"
onClick={handleCopy}
data-testid="modal-copy-button"
>
{copied ? 'Copied!' : 'Copy'}
</Button>
</Modal.Footer>
</Modal>
)
: null;
};
export default ModalMarkdown;