article editor
This commit is contained in:
1792
package-lock.json
generated
1792
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,16 +12,23 @@
|
||||
"dependencies": {
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@reduxjs/toolkit": "^2.9.2",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@types/react-redux": "^7.1.33",
|
||||
"axios": "^1.12.2",
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^11.9.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"monaco-editor": "^0.54.0",
|
||||
"postcss": "^8.4.47",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router-dom": "^7.9.4",
|
||||
"rehype-highlight": "^7.0.2",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"tailwind-cn": "^1.0.2",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tailwindcss": "^3.4.12"
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Route, Routes } from "react-router-dom";
|
||||
import Home from "./pages/Home";
|
||||
import Mission from "./pages/Mission";
|
||||
import UploadMissionForm from "./views/mission/UploadMissionForm";
|
||||
import MarkdownEditor from "./views/articleeditor/Editor";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
@@ -16,7 +17,7 @@ function App() {
|
||||
<Route path="/home/*" element={<Home />} />
|
||||
<Route path="/mission/:missionId" element={<Mission />} />
|
||||
<Route path="/upload" element={<UploadMissionForm/>}/>
|
||||
<Route path="*" element={<Home />} />
|
||||
<Route path="*" element={<MarkdownEditor onChange={(value: string) => {}}/>} />
|
||||
</Routes>
|
||||
|
||||
</div>
|
||||
|
||||
0
src/pages/ArticleEditor.tsx
Normal file
0
src/pages/ArticleEditor.tsx
Normal file
300
src/views/articleeditor/Editor.tsx
Normal file
300
src/views/articleeditor/Editor.tsx
Normal file
@@ -0,0 +1,300 @@
|
||||
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 "highlight.js/styles/github-dark.css";
|
||||
import Header from "../mission/statement/Header";
|
||||
|
||||
interface MarkdownEditorProps {
|
||||
defaultValue?: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
|
||||
const MarkdownEditor: FC<MarkdownEditorProps> = ({
|
||||
defaultValue,
|
||||
onChange,
|
||||
}) => {
|
||||
const [markdown, setMarkdown] = useState<string>(defaultValue ? 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-логотип:
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🧠 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])
|
||||
|
||||
return (
|
||||
<div className="h-screen grid grid-rows-[60px,1fr]">
|
||||
<div className="">
|
||||
<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,
|
||||
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)}
|
||||
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>
|
||||
|
||||
|
||||
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
<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;
|
||||
@@ -11,5 +11,6 @@ export default {
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
plugins: [require('@tailwindcss/typography')],
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user