1
1
import { Lock , LockOpen , TextFields } from "@mui/icons-material" ;
2
2
import { Box , Stack } from "@mui/material" ;
3
3
import type { EditorOptions } from "@tiptap/core" ;
4
+ import { EditorView } from "@tiptap/pm/view" ;
4
5
import { FC , useCallback , useState } from "react" ;
5
6
6
- import { LinkBubbleMenu , RichTextEditor } from "shared/lib/mui-tiptap" ;
7
+ import {
8
+ insertFiles ,
9
+ insertImages ,
10
+ LinkBubbleMenu ,
11
+ RichTextEditor ,
12
+ } from "shared/lib/mui-tiptap" ;
7
13
import { TableBubbleMenu , MenuButton } from "shared/lib/mui-tiptap/controls" ;
14
+ import { extractFileId } from "shared/helpers" ;
8
15
9
16
import { EditorMenuControls } from "./ui" ;
10
17
import { fileListToImageFiles } from "../utils/file-list-to-image-files" ;
11
18
import useExtensions from "../hooks/use-extensions" ;
12
19
import { ITextEditor } from "../types" ;
13
20
14
- const CommentEditor : FC < ITextEditor > = ( { rteRef, content } ) => {
21
+ const CommentEditor : FC < ITextEditor > = ( {
22
+ rteRef,
23
+ content,
24
+ setPendingFiles,
25
+ source,
26
+ handleDeleteFile,
27
+ } ) => {
15
28
const extensions = useExtensions ( {
16
29
placeholder : "Введите текст..." ,
17
30
} ) ;
@@ -43,6 +56,81 @@ const CommentEditor: FC<ITextEditor> = ({ rteRef, content }) => {
43
56
return pastedImageFiles . length > 0 ;
44
57
} , [ ] ) ;
45
58
59
+ const handleNewImageFiles = useCallback (
60
+ ( files : File [ ] , insertPosition ?: number ) : void => {
61
+ if ( ! rteRef . current ?. editor ) return ;
62
+
63
+ const filesWithUrl = files . map ( ( file ) => ( {
64
+ file,
65
+ localUrl : URL . createObjectURL ( file ) ,
66
+ source,
67
+ } ) ) ;
68
+
69
+ setPendingFiles ?.( ( prev ) => [ ...prev , ...filesWithUrl ] ) ;
70
+
71
+ const attributesForImageFiles = filesWithUrl . map (
72
+ ( { file, localUrl } ) => ( {
73
+ src : localUrl ,
74
+ alt : file . name ,
75
+ } )
76
+ ) ;
77
+
78
+ insertImages ( {
79
+ images : attributesForImageFiles ,
80
+ editor : rteRef . current . editor ,
81
+ position : insertPosition ,
82
+ } ) ;
83
+ } ,
84
+ [ rteRef , setPendingFiles ]
85
+ ) ;
86
+
87
+ const handleNewFiles = useCallback (
88
+ ( files : File [ ] , insertPosition ?: number ) : void => {
89
+ if ( ! rteRef . current ?. editor ) {
90
+ return ;
91
+ }
92
+
93
+ const filesWithUrl = files . map ( ( file ) => ( {
94
+ file,
95
+ localUrl : URL . createObjectURL ( file ) ,
96
+ source,
97
+ } ) ) ;
98
+
99
+ setPendingFiles ?.( ( prev ) => [ ...prev , ...filesWithUrl ] ) ;
100
+
101
+ const attributesForFiles = filesWithUrl . map ( ( { localUrl, file } ) => ( {
102
+ href : localUrl ,
103
+ fileName : file . name ,
104
+ } ) ) ;
105
+
106
+ insertFiles ( {
107
+ files : attributesForFiles ,
108
+ editor : rteRef . current . editor ,
109
+ position : insertPosition ,
110
+ } ) ;
111
+ } ,
112
+ [ rteRef , setPendingFiles ]
113
+ ) ;
114
+
115
+ const handleKeyDown = useCallback (
116
+ ( view : EditorView , event : { key : string } ) => {
117
+ if ( event . key === "Backspace" || event . key === "Delete" ) {
118
+ const content = rteRef . current ?. editor ?. getHTML ( ) ;
119
+
120
+ if ( content ) {
121
+ const fileIds = extractFileId ( content ) ;
122
+
123
+ if ( fileIds . length > 0 ) {
124
+ handleDeleteFile ?.( content ) ;
125
+ }
126
+ }
127
+ }
128
+
129
+ return false ;
130
+ } ,
131
+ [ handleDeleteFile ]
132
+ ) ;
133
+
46
134
return (
47
135
< >
48
136
< Box
@@ -62,8 +150,16 @@ const CommentEditor: FC<ITextEditor> = ({ rteRef, content }) => {
62
150
editorProps = { {
63
151
handleDrop,
64
152
handlePaste,
153
+ handleDOMEvents : {
154
+ keydown : handleKeyDown ,
155
+ } ,
65
156
} }
66
- renderControls = { ( ) => < EditorMenuControls /> }
157
+ renderControls = { ( ) => (
158
+ < EditorMenuControls
159
+ onUploadImageFiles = { handleNewImageFiles }
160
+ onUploadFiles = { handleNewFiles }
161
+ />
162
+ ) }
67
163
RichTextFieldProps = { {
68
164
variant : "outlined" ,
69
165
MenuBarProps : {
0 commit comments