diff --git a/package.json b/package.json
index 5b9e402..68cd839 100644
--- a/package.json
+++ b/package.json
@@ -19,12 +19,14 @@
"@refinedev/react-router": "^1.0.0",
"@refinedev/simple-rest": "^5.0.1",
"axios": "^1.7.9",
+ "easymde": "^2.19.0",
"i18next": "^24.2.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-hook-form": "^7.30.0",
"react-i18next": "^15.4.1",
- "react-router": "^7.0.2"
+ "react-router": "^7.0.2",
+ "react-simplemde-editor": "^5.2.0"
},
"devDependencies": {
"@types/node": "^18.16.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 47ce003..e830601 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -53,6 +53,9 @@ importers:
axios:
specifier: ^1.7.9
version: 1.7.9
+ easymde:
+ specifier: ^2.19.0
+ version: 2.19.0
i18next:
specifier: ^24.2.2
version: 24.2.2(typescript@5.7.3)
@@ -71,6 +74,9 @@ importers:
react-router:
specifier: ^7.0.2
version: 7.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react-simplemde-editor:
+ specifier: ^5.2.0
+ version: 5.2.0(easymde@2.19.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
devDependencies:
'@types/node':
specifier: ^18.16.2
@@ -936,9 +942,15 @@ packages:
'@types/babel__traverse@7.20.6':
resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
+ '@types/codemirror@5.60.15':
+ resolution: {integrity: sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==}
+
'@types/cookie@0.6.0':
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
+ '@types/estree@1.0.6':
+ resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+
'@types/hast@2.3.10':
resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==}
@@ -948,6 +960,9 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+ '@types/marked@4.3.2':
+ resolution: {integrity: sha512-a79Yc3TOk6dGdituy8hmTTJXjOkZ7zsFYV10L337ttq/rec8lRMDBpV7fL3uLx6TgbFCa5DU/h8FmIBQPSbU0w==}
+
'@types/mdast@3.0.15':
resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==}
@@ -976,6 +991,9 @@ packages:
'@types/semver@7.5.8':
resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
+ '@types/tern@0.23.9':
+ resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==}
+
'@types/unist@2.0.11':
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
@@ -1279,6 +1297,12 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
+ codemirror-spell-checker@1.1.2:
+ resolution: {integrity: sha512-2Tl6n0v+GJRsC9K3MLCdLaMOmvWL0uukajNJseorZJsslaxZyZMgENocPU8R0DyoTAiKsyqiemSOZo7kjGV0LQ==}
+
+ codemirror@5.65.18:
+ resolution: {integrity: sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA==}
+
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@@ -1439,6 +1463,9 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+ easymde@2.19.0:
+ resolution: {integrity: sha512-4F1aNImqse+9xIjLh9ttfpOVenecjFPxUmKbl1tGp72Z+OyIqLZPE/SgNyy88c/xU0mOy0WC3+tfbZDQ5PDWhg==}
+
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
@@ -2605,6 +2632,13 @@ packages:
react-dom:
optional: true
+ react-simplemde-editor@5.2.0:
+ resolution: {integrity: sha512-GkTg1MlQHVK2Rks++7sjuQr/GVS/xm6y+HchZ4GPBWrhcgLieh4CjK04GTKbsfYorSRYKa0n37rtNSJmOzEDkQ==}
+ peerDependencies:
+ easymde: '>= 2.0.0 < 3.0.0'
+ react: '>=16.8.2'
+ react-dom: '>=16.8.2'
+
react-transition-group@4.4.5:
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
peerDependencies:
@@ -2970,6 +3004,9 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
+ typo-js@1.2.5:
+ resolution: {integrity: sha512-F45vFWdGX8xahIk/sOp79z2NJs8ETMYsmMChm9D5Hlx3+9j7VnCyQyvij5MOCrNY3NNe8noSyokRjQRfq+Bc7A==}
+
uglify-js@3.19.3:
resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==}
engines: {node: '>=0.8.0'}
@@ -4149,8 +4186,14 @@ snapshots:
dependencies:
'@babel/types': 7.26.5
+ '@types/codemirror@5.60.15':
+ dependencies:
+ '@types/tern': 0.23.9
+
'@types/cookie@0.6.0': {}
+ '@types/estree@1.0.6': {}
+
'@types/hast@2.3.10':
dependencies:
'@types/unist': 2.0.11
@@ -4161,6 +4204,8 @@ snapshots:
'@types/json-schema@7.0.15': {}
+ '@types/marked@4.3.2': {}
+
'@types/mdast@3.0.15':
dependencies:
'@types/unist': 2.0.11
@@ -4188,6 +4233,10 @@ snapshots:
'@types/semver@7.5.8': {}
+ '@types/tern@0.23.9':
+ dependencies:
+ '@types/estree': 1.0.6
+
'@types/unist@2.0.11': {}
'@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3)':
@@ -4522,6 +4571,12 @@ snapshots:
clsx@2.1.1: {}
+ codemirror-spell-checker@1.1.2:
+ dependencies:
+ typo-js: 1.2.5
+
+ codemirror@5.65.18: {}
+
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
@@ -4664,6 +4719,14 @@ snapshots:
eastasianwidth@0.2.0: {}
+ easymde@2.19.0:
+ dependencies:
+ '@types/codemirror': 5.60.15
+ '@types/marked': 4.3.2
+ codemirror: 5.65.18
+ codemirror-spell-checker: 1.1.2
+ marked: 4.3.0
+
ee-first@1.1.1: {}
electron-to-chromium@1.5.83: {}
@@ -5932,6 +5995,13 @@ snapshots:
optionalDependencies:
react-dom: 18.3.1(react@18.3.1)
+ react-simplemde-editor@5.2.0(easymde@2.19.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ '@types/codemirror': 5.60.15
+ easymde: 2.19.0
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+
react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@babel/runtime': 7.26.0
@@ -6290,6 +6360,8 @@ snapshots:
typescript@5.7.3: {}
+ typo-js@1.2.5: {}
+
uglify-js@3.19.3:
optional: true
diff --git a/src/globals.css b/src/globals.css
index f598f80..b6de5a7 100644
--- a/src/globals.css
+++ b/src/globals.css
@@ -1,3 +1,38 @@
.refine-list-button {
display: none !important;
}
+
+.my-markdown-editor .editor-statusbar {
+ display: none !important;
+}
+
+.my-markdown-editor .CodeMirror {
+ background-color: transparent !important;
+ color: white !important;
+}
+
+.my-markdown-editor .CodeMirror-gutters {
+ background-color: transparent !important;
+ border-right: 1px solid #444;
+ color: white !important;
+}
+
+.my-markdown-editor .CodeMirror-cursor {
+ border-left: 1px solid white !important;
+}
+
+.my-markdown-editor .editor-toolbar i {
+ color: white !important;
+}
+
+.my-markdown-editor .editor-toolbar button:hover i {
+ color: #000 !important;
+}
+
+.my-markdown-editor .editor-toolbar button.active i {
+ color: #000 !important;
+}
+
+.my-markdown-editor .editor-toolbar .separator {
+ color: transparent !important;
+}
diff --git a/src/pages/article/create.tsx b/src/pages/article/create.tsx
index 2afca28..e2bb0d2 100644
--- a/src/pages/article/create.tsx
+++ b/src/pages/article/create.tsx
@@ -1,12 +1,20 @@
import {Box, TextField} from '@mui/material'
import {Create} from '@refinedev/mui'
import {useForm} from '@refinedev/react-hook-form'
+import {Controller} from 'react-hook-form'
+import React from 'react'
+
+import SimpleMDE from 'react-simplemde-editor'
+import 'easymde/dist/easymde.min.css'
+
+const MemoizedSimpleMDE = React.memo(SimpleMDE)
export const ArticleCreate = () => {
const {
saveButtonProps,
refineCore: {formLoading},
register,
+ control,
formState: {errors},
} = useForm({
refineCoreProps: {
@@ -14,6 +22,14 @@ export const ArticleCreate = () => {
},
})
+ const simpleMDEOptions = React.useMemo(
+ () => ({
+ placeholder: 'Введите контент в формате Markdown...',
+ spellChecker: false,
+ }),
+ [],
+ )
+
return (
@@ -30,19 +46,8 @@ export const ArticleCreate = () => {
label={'Заголовок'}
name="heading"
/>
-
+
+ } />
)
diff --git a/src/pages/article/edit.tsx b/src/pages/article/edit.tsx
index 7ca0d05..b91d2e6 100644
--- a/src/pages/article/edit.tsx
+++ b/src/pages/article/edit.tsx
@@ -1,13 +1,29 @@
import {Box, TextField} from '@mui/material'
import {Edit} from '@refinedev/mui'
import {useForm} from '@refinedev/react-hook-form'
+import {Controller} from 'react-hook-form'
+import React from 'react'
+
+import SimpleMDE from 'react-simplemde-editor'
+import 'easymde/dist/easymde.min.css'
+
+const MemoizedSimpleMDE = React.memo(SimpleMDE)
export const ArticleEdit = () => {
const {
saveButtonProps,
register,
+ control,
formState: {errors},
- } = useForm({})
+ } = useForm()
+
+ const simpleMDEOptions = React.useMemo(
+ () => ({
+ placeholder: 'Введите контент в формате Markdown...',
+ spellChecker: false,
+ }),
+ [],
+ )
return (
@@ -22,22 +38,11 @@ export const ArticleEdit = () => {
fullWidth
InputLabelProps={{shrink: true}}
type="text"
- label={'Заголовок'}
+ label="Заголовок"
name="heading"
/>
-
+
+ } />
)