Files
LiquidCode_Frontend/src/views/articleeditor/Editor.tsx
Виталий Лавшонок 04dbc5eeb1 add css in mdedfitor
2025-11-04 13:17:59 +03:00

311 lines
8.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { FC, useEffect, useState } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import rehypeHighlight from "rehype-highlight";
import rehypeRaw from "rehype-raw";
import rehypeSanitize from "rehype-sanitize";
import axios from "../../axios";
import "highlight.js/styles/github-dark.css";
import Header from "../mission/statement/Header";
import { defaultSchema } from "hast-util-sanitize";
const schema = {
...defaultSchema,
attributes: {
...defaultSchema.attributes,
div: [
...(defaultSchema.attributes?.div || []),
["style"] // разрешаем атрибут style на div
]
}
};
interface MarkdownEditorProps {
defaultValue?: string;
onChange: (value: string) => void;
}
const MarkdownEditor: FC<MarkdownEditorProps> = ({ defaultValue, onChange }) => {
const [markdown, setMarkdown] = useState<string>(defaultValue || `# 🌙 Добро пожаловать в Markdown-редактор
Добро пожаловать в **Markdown-редактор**!
Здесь ты можешь писать в формате Markdown и видеть результат **в реальном времени** 👇
---
## 🧱 1. Форматирование текста
Вот примеры базового форматирования:
- **Жирный текст**
- *Курсивный текст*
- ***Жирный курсив***
- ~~Зачёркнутый~~
> 💬 _Цитаты_ можно использовать для выделения текста, заметок или описаний.
---
## 🧩 2. Списки
### 🔹 Маркированный список
- Один
- Два
- Вложенный уровень
- Ещё глубже
- Три
### 🔸 Нумерованный список
1. Первый
2. Второй
3. Третий
1. Вложенный
2. Ещё один
---
## ✅ 3. Чеклисты (GFM)
- [x] Поддержка Markdown
- [x] Подсветка кода
- [x] Таблицы
- [x] Эмодзи 😎
- [ ] Экспорт в PDF (в будущем)
---
## 💻 4. Код и подсветка
Пример **TypeScript**:
\`\`\`tsx
type User = {
name: string;
role: "Разработчик" | "Помощник";
};
function greet(user: User) {
return \`Привет, \${user.name}! 👋 Роль: \${user.role}\`;
}
console.log(greet({ name: "Ты", role: "Разработчик" }));
\`\`\`
Пример **JavaScript**:
\`\`\`js
const sum = (a, b) => a + b;
console.log(sum(2, 3)); // 5
\`\`\`
Пример **Python**:
\`\`\`python
def greet(name):
return f"Привет, {name}! 👋"
print(greet("Мир"))
\`\`\`
---
## 📊 5. Таблицы (GFM)
| Имя | Роль | Активен | Эмодзи |
|-------------|----------------|----------|--------|
| ChatGPT | Помощник 🤖 | ✅ | 🤓 |
| Ты | Разработчик 💻 | ✅ | 🚀 |
| TailwindCSS | Стилизация 🎨 | 🟢 | 💅 |
> Таблицы поддерживают **жирный текст**, _курсив_ и даже \`инлайн-код\` внутри ячеек.
---
## 🔗 6. Ссылки
- [Документация Markdown](https://www.markdownguide.org/)
- [React Markdown на GitHub](https://github.com/remarkjs/react-markdown)
- Автоматическая ссылка: https://github.com
---
## 🖼️ 7. Изображения
### Markdown-логотип:
![Markdown Logo](https://upload.wikimedia.org/wikipedia/commons/4/48/Markdown-mark.svg)
или
<img src=\"https://upload.wikimedia.org/wikipedia/commons/4/48/Markdown-mark.svg\" alt=\"img\"/>
или если нужно выравнивание по центру
<div style=\"display: flex; items-align: center; justify-content: center; background: gray;\">
<img src=\"https://upload.wikimedia.org/wikipedia/commons/4/48/Markdown-mark.svg\" alt=\"img\"/>
</div>
---
## 🧠 8. Цитаты и вложенность
> 💭 Это обычная цитата.
>
> > А это — **вложенная цитата**.
> >
> > > Можно вкладывать сколько угодно уровней!
---
## ⚙️ 9. Горизонтальные линии
---
***
---
## 🧮 10. Таблица внутри цитаты
> Вот таблица прямо внутри блока цитаты:
>
> | Язык | Назначение |
> |-------|-------------|
> | JS | Web-разработка |
> | TS | Строгая типизация |
> | PY | Скрипты и AI |
---
## 🧩 11. Встроенный HTML
<details>
<summary>📂 Раскрывающийся блок</summary>
Этот текст виден только после раскрытия!
<ul>
<li>HTML списки работают</li>
<li>И даже <b>жирный текст</b></li>
</ul>
</details>
---
## 🎨 12. Вложенные списки с кодом
- Этапы:
1. Создай проект
2. Добавь зависимости:
\`\`\`bash
npm install react-markdown remark-gfm rehype-highlight highlight.js
\`\`\`
3. Импортируй стили:
\`\`\`tsx
import "highlight.js/styles/github-dark.css";
\`\`\`
4. Готово!
---
## 🚀 13. Финал
Поздравляю! 🎉
Ты только что увидел все ключевые возможности **Markdown + GFM** в действии.
> ✨ Используй этот текст как шаблон для тестирования рендерера.
> 💡 Совет: попробуй поменять тему \`highlight.js\` (например \`monokai.css\` или \`atom-one-dark.css\`).
---
**🖤 Конец демонстрации. Спасибо, что используешь Markdown-редактор!**
`);
useEffect(() => {
onChange(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) +
`<img src=\"${imageUrl}\" alt=\"img\"/>` +
markdown.slice(cursorPos);
setMarkdown(newText);
} catch (err) {
console.error("Ошибка загрузки изображения:", err);
}
}
}
};
return (
<div className="h-screen grid grid-rows-[60px,1fr]">
<div>
<Header missionId={1} />
</div>
<div className="grid grid-cols-2 h-full min-h-0">
{/* Предпросмотр */}
<div className="overflow-y-auto min-h-0 overflow-hidden">
<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>
<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, schema], rehypeHighlight]}
>
{markdown}
</ReactMarkdown>
</div>
</div>
</div>
</div>
{/* Редактор */}
<div className="overflow-y-auto min-h-0 overflow-hidden">
<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>
<textarea
value={markdown}
onChange={(e) => setMarkdown(e.target.value)}
onPaste={handlePaste} // <-- вот сюда обработчик вставки
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
medium-scrollbar"
placeholder="Пиши в формате Markdown..."
/>
</div>
</div>
</div>
</div>
);
};
export default MarkdownEditor;