add img
This commit is contained in:
@@ -4,6 +4,7 @@ import remarkGfm from "remark-gfm";
|
|||||||
import rehypeHighlight from "rehype-highlight";
|
import rehypeHighlight from "rehype-highlight";
|
||||||
import rehypeRaw from "rehype-raw";
|
import rehypeRaw from "rehype-raw";
|
||||||
import rehypeSanitize from "rehype-sanitize";
|
import rehypeSanitize from "rehype-sanitize";
|
||||||
|
import axios from "../../axios";
|
||||||
import "highlight.js/styles/github-dark.css";
|
import "highlight.js/styles/github-dark.css";
|
||||||
import Header from "../mission/statement/Header";
|
import Header from "../mission/statement/Header";
|
||||||
|
|
||||||
@@ -12,12 +13,8 @@ interface MarkdownEditorProps {
|
|||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MarkdownEditor: FC<MarkdownEditorProps> = ({
|
const MarkdownEditor: FC<MarkdownEditorProps> = ({ defaultValue, onChange }) => {
|
||||||
defaultValue,
|
const [markdown, setMarkdown] = useState<string>(defaultValue || `# 🌙 Добро пожаловать в Markdown-редактор
|
||||||
onChange,
|
|
||||||
}) => {
|
|
||||||
const [markdown, setMarkdown] = useState<string>(defaultValue ? defaultValue :
|
|
||||||
`# 🌙 Добро пожаловать в Markdown-редактор
|
|
||||||
|
|
||||||
Добро пожаловать в **Markdown-редактор**!
|
Добро пожаловать в **Markdown-редактор**!
|
||||||
Здесь ты можешь писать в формате Markdown и видеть результат **в реальном времени** 👇
|
Здесь ты можешь писать в формате Markdown и видеть результат **в реальном времени** 👇
|
||||||
@@ -204,16 +201,51 @@ print(greet("Мир"))
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onChange(markdown);
|
onChange(markdown);
|
||||||
}, [markdown])
|
}, [markdown]);
|
||||||
|
|
||||||
|
// Обработчик вставки
|
||||||
|
const handlePaste = async (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
|
||||||
|
const items = e.clipboardData.items;
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
if (item.type.startsWith("image/")) {
|
||||||
|
e.preventDefault(); // предотвращаем вставку картинки как текста
|
||||||
|
|
||||||
|
const file = item.getAsFile();
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post("/media/upload", formData, {
|
||||||
|
headers: { "Content-Type": "multipart/form-data" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const imageUrl = response.data.url;
|
||||||
|
// Вставляем ссылку на картинку в текст
|
||||||
|
const cursorPos = (e.target as HTMLTextAreaElement).selectionStart;
|
||||||
|
const newText =
|
||||||
|
markdown.slice(0, cursorPos) +
|
||||||
|
`` +
|
||||||
|
markdown.slice(cursorPos);
|
||||||
|
|
||||||
|
setMarkdown(newText);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Ошибка загрузки изображения:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen grid grid-rows-[60px,1fr]">
|
<div className="h-screen grid grid-rows-[60px,1fr]">
|
||||||
<div className="">
|
<div>
|
||||||
<Header missionId={1} />
|
<Header missionId={1} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 h-full min-h-0">
|
<div className="grid grid-cols-2 h-full min-h-0">
|
||||||
|
{/* Предпросмотр */}
|
||||||
<div className="overflow-y-auto min-h-0 overflow-hidden">
|
<div className="overflow-y-auto min-h-0 overflow-hidden">
|
||||||
<div className="p-4 border-r border-gray-700 flex flex-col h-full">
|
<div className="p-4 border-r border-gray-700 flex flex-col h-full">
|
||||||
<h2 className="text-lg font-semibold mb-3 text-gray-100">👀 Предпросмотр</h2>
|
<h2 className="text-lg font-semibold mb-3 text-gray-100">👀 Предпросмотр</h2>
|
||||||
@@ -221,11 +253,7 @@ print(greet("Мир"))
|
|||||||
<div className="prose prose-invert max-w-none h-full overflow-auto pr-4 medium-scrollbar">
|
<div className="prose prose-invert max-w-none h-full overflow-auto pr-4 medium-scrollbar">
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
remarkPlugins={[remarkGfm]}
|
remarkPlugins={[remarkGfm]}
|
||||||
rehypePlugins={[
|
rehypePlugins={[rehypeRaw, rehypeSanitize, rehypeHighlight]}
|
||||||
rehypeRaw,
|
|
||||||
rehypeSanitize,
|
|
||||||
rehypeHighlight
|
|
||||||
]}
|
|
||||||
>
|
>
|
||||||
{markdown}
|
{markdown}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
@@ -234,67 +262,24 @@ print(greet("Мир"))
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Редактор */}
|
||||||
<div className="overflow-y-auto min-h-0 overflow-hidden">
|
<div className="overflow-y-auto min-h-0 overflow-hidden">
|
||||||
<div className="p-4 border-r border-gray-700 flex flex-col h-full">
|
<div className="p-4 border-r border-gray-700 flex flex-col h-full">
|
||||||
<h2 className="text-lg font-semibold mb-3 text-gray-100">📝 Редактор</h2>
|
<h2 className="text-lg font-semibold mb-3 text-gray-100">📝 Редактор</h2>
|
||||||
<textarea
|
<textarea
|
||||||
value={markdown}
|
value={markdown}
|
||||||
onChange={(e) => setMarkdown(e.target.value)}
|
onChange={(e) => setMarkdown(e.target.value)}
|
||||||
|
onPaste={handlePaste} // <-- вот сюда обработчик вставки
|
||||||
className="flex-1 w-full bg-[#0d1117] text-gray-200 border border-gray-700
|
className="flex-1 w-full bg-[#0d1117] text-gray-200 border border-gray-700
|
||||||
rounded-lg p-5 font-mono text-sm resize-none focus:outline-none focus:ring-2
|
rounded-lg p-5 font-mono text-sm resize-none focus:outline-none focus:ring-2
|
||||||
medium-scrollbar
|
medium-scrollbar"
|
||||||
"
|
|
||||||
placeholder="Пиши в формате Markdown..."
|
placeholder="Пиши в формате Markdown..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
<div className="grid grid-cols-2 h-screen bg-[#0d1117] text-gray-200 relative">
|
|
||||||
<div className="p-4 border-r border-gray-700 bg-[#161b22] flex flex-col h-screen ">
|
|
||||||
<h2 className="text-lg font-semibold mb-3 text-gray-100">📝 Редактор</h2>
|
|
||||||
<textarea
|
|
||||||
value={markdown}
|
|
||||||
onChange={(e) => setMarkdown(e.target.value)}
|
|
||||||
className="flex-1 w-full bg-[#0d1117] text-gray-200 border border-gray-700
|
|
||||||
rounded-lg p-3 font-mono text-sm resize-none focus:outline-none focus:ring-2
|
|
||||||
medium-scrollbar
|
|
||||||
"
|
|
||||||
placeholder="Пиши в формате Markdown..."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="p-4 bg-[#0d1117] flex flex-col h-full relative">
|
|
||||||
<h2 className="text-lg font-semibold mb-[12px] text-gray-100 h-[28px]">👀 Предпросмотр</h2>
|
|
||||||
|
|
||||||
<div className="flex-1 bg-[#161b22] rounded-lg shadow-lg p-6 h-[calc(100%-40px)]">
|
|
||||||
<div className="prose prose-invert max-w-none h-full overflow-auto pr-4 medium-scrollbar">
|
|
||||||
<ReactMarkdown
|
|
||||||
remarkPlugins={[remarkGfm]}
|
|
||||||
rehypePlugins={[
|
|
||||||
rehypeRaw,
|
|
||||||
rehypeSanitize,
|
|
||||||
rehypeHighlight
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{markdown}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
export default MarkdownEditor;
|
export default MarkdownEditor;
|
||||||
Reference in New Issue
Block a user