upgrade create & edit for /article route using react-simplemde-editor

This commit is contained in:
maxim 2025-02-21 21:21:26 +03:00
parent 4a0440cd6e
commit 9d590c994f
5 changed files with 148 additions and 29 deletions

View File

@ -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",

View File

@ -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

View File

@ -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;
}

View File

@ -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 (
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Box component="form" sx={{display: 'flex', flexDirection: 'column'}} autoComplete="off">
@ -30,19 +46,8 @@ export const ArticleCreate = () => {
label={'Заголовок'}
name="heading"
/>
<TextField
{...register('body', {
required: 'Это поле является обязательным',
})}
error={!!(errors as any)?.body}
helperText={(errors as any)?.body?.message}
margin="normal"
fullWidth
InputLabelProps={{shrink: true}}
type="text"
label={'Контент'}
name="body"
/>
<Controller control={control} name="body" rules={{required: 'Это поле является обязательным'}} defaultValue="" render={({field: {onChange, value}}) => <MemoizedSimpleMDE value={value} onChange={onChange} options={simpleMDEOptions} className="my-markdown-editor" />} />
</Box>
</Create>
)

View File

@ -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 (
<Edit saveButtonProps={saveButtonProps}>
@ -22,22 +38,11 @@ export const ArticleEdit = () => {
fullWidth
InputLabelProps={{shrink: true}}
type="text"
label={'Заголовок'}
label="Заголовок"
name="heading"
/>
<TextField
{...register('body', {
required: 'Это поле является обязательным',
})}
error={!!(errors as any)?.body}
helperText={(errors as any)?.body?.message}
margin="normal"
fullWidth
InputLabelProps={{shrink: true}}
type="text"
label={'Контент'}
name="body"
/>
<Controller control={control} name="body" rules={{required: 'Это поле является обязательным'}} defaultValue="" render={({field: {onChange, value}}) => <MemoizedSimpleMDE value={value} onChange={onChange} options={simpleMDEOptions} className="my-markdown-editor" />} />
</Box>
</Edit>
)