Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
lib
react-ag-qeditor
Commits
c25a5de8
Commit
c25a5de8
authored
Jun 27, 2025
by
Яков
Browse files
fix drag and drop
parent
c79e85c3
Changes
3
Hide whitespace changes
Inline
Side-by-side
example/src/App.js
View file @
c25a5de8
...
...
@@ -12,7 +12,7 @@ const App = () => {
// console.log(value);
}}
uploadOptions
=
{{
url
:
'
https://cdn.atmaguru.online/upload/?sid=atmacompany&md5=0cETbV4BquHkqAdG9cK9MA&expires=1742192970
'
,
url
:
'
/upload
'
,
errorMessage
:
'
Загрузка временно невозможна
'
}}
style
=
{{
...
...
src/QEditor.jsx
View file @
c25a5de8
...
...
@@ -512,8 +512,19 @@ const QEditor = ({
Superscript
,
Subscript
,
DragAndDrop
.
configure
({
linkUpload
:
uploadOptions
.
url
}),
uploadUrl
:
uploadOptions
.
url
,
allowedFileTypes
:
[
'
image/jpeg
'
,
'
image/png
'
,
'
video/mp4
'
],
onUploadSuccess
:
(
fileUrl
)
=>
{
console
.
log
(
'
File uploaded:
'
,
fileUrl
);
},
onUploadError
:
(
error
)
=>
{
console
.
error
(
'
Upload error:
'
,
error
);
}
})
],
content
:
value
,
onUpdate
:
({
editor
})
=>
onChange
(
editor
.
getHTML
()),
...
...
src/extensions/DragAndDrop.js
View file @
c25a5de8
import
{
Extension
}
from
'
@tiptap/core
'
;
import
{
Plugin
,
PluginKey
}
from
'
prosemirror-state
'
;
import
{
EditorView
}
from
'
prosemirror-view
'
;
import
axios
from
'
axios
'
;
export
const
DragAndDrop
=
Extension
.
create
({
...
...
@@ -9,132 +8,153 @@ export const DragAndDrop = Extension.create({
addOptions
()
{
return
{
uploadUrl
:
''
,
// URL для загрузки файлов
uploadHandler
:
null
,
// Альтернативный обработчик загрузки
types
:
[
'
image
'
],
// Поддерживаемые типы файлов
uploadHandler
:
null
,
// Кастомный обработчик загрузки
allowedFileTypes
:
[
// Разрешенные MIME-типы
'
image/jpeg
'
,
'
image/png
'
,
'
image/gif
'
,
'
image/webp
'
,
'
video/mp4
'
,
'
video/webm
'
,
'
audio/mpeg
'
],
headers
:
{},
// Дополнительные заголовки
onUploadError
:
(
error
)
=>
console
.
error
(
'
Upload failed:
'
,
error
),
onUploadSuccess
:
()
=>
{}
// Колбек при успешной загрузке
};
},
addProseMirrorPlugins
()
{
if
(
!
this
.
options
.
uploadUrl
&&
!
this
.
options
.
uploadHandler
)
{
return
[];
}
const
isRealFile
=
(
item
)
=>
{
// Игнорируем текстовые форматы и специфичные для Word
if
(
item
.
type
.
startsWith
(
'
text/
'
)
||
item
.
type
.
startsWith
(
'
application/x-mso
'
)
||
item
.
type
===
'
text/html
'
||
item
.
type
===
'
text/rtf
'
)
{
const
extension
=
this
;
// Проверяем, является ли файл реальным (не из Word)
const
isRealFile
=
(
file
)
=>
{
if
(
!
file
||
!
file
.
type
)
return
false
;
// Игнорируем специфичные для Word типы
const
wordTypes
=
[
'
application/x-mso
'
,
'
ms-office
'
,
'
wordprocessingml
'
,
'
application/rtf
'
,
'
text/rtf
'
,
'
text/html
'
];
if
(
wordTypes
.
some
(
type
=>
file
.
type
.
includes
(
type
)))
{
return
false
;
}
// Разрешенные файловые типы
return
[
'
image/
'
,
'
video/
'
,
'
audio/
'
,
'
application/octet-stream
'
,
'
application/pdf
'
,
'
application/zip
'
].
some
(
type
=>
item
.
type
.
startsWith
(
type
));
// Проверяем разрешенные типы
return
extension
.
options
.
allowedFileTypes
.
includes
(
file
.
type
);
};
const
uploadFile
=
async
(
file
)
=>
{
if
(
this
.
options
.
uploadHandler
)
{
return
await
this
.
options
.
uploadHandler
(
file
);
}
// Определяем тип ноды для вставки
const
getNodeType
=
(
mimeType
)
=>
{
if
(
mimeType
.
startsWith
(
'
image/
'
))
return
'
image
'
;
if
(
mimeType
.
startsWith
(
'
video/
'
))
return
'
video
'
;
if
(
mimeType
.
startsWith
(
'
audio/
'
))
return
'
audio
'
;
return
null
;
};
const
formData
=
new
FormData
();
formData
.
append
(
'
file
'
,
file
);
// Обработчик загрузки файла
const
handleFileUpload
=
async
(
file
,
view
,
position
)
=>
{
try
{
let
fileUrl
;
if
(
extension
.
options
.
uploadHandler
)
{
fileUrl
=
await
extension
.
options
.
uploadHandler
(
file
);
}
else
{
const
formData
=
new
FormData
();
formData
.
append
(
'
file
'
,
file
);
const
response
=
await
axios
.
post
(
extension
.
options
.
uploadUrl
,
formData
,
{
headers
:
{
'
Content-Type
'
:
'
multipart/form-data
'
,
...
extension
.
options
.
headers
,
},
}
);
if
(
!
response
.
data
?.
file_path
)
{
throw
new
Error
(
'
Invalid server response
'
);
}
fileUrl
=
response
.
data
.
file_path
;
}
const
headers
=
{
'
Content-Type
'
:
'
multipart/form-data
'
,
...
this
.
options
.
headers
};
if
(
!
fileUrl
)
return
;
const
response
=
await
axios
.
post
(
this
.
options
.
uploadUrl
,
formData
,
{
headers
}
);
const
{
state
,
dispatch
}
=
view
;
const
type
=
getNodeType
(
file
.
type
);
if
(
!
type
)
return
;
if
(
!
response
.
data
)
throw
new
Error
(
'
Upload failed
'
);
return
response
.
data
.
file_path
;
const
node
=
state
.
schema
.
nodes
[
type
].
create
({
src
:
fileUrl
});
dispatch
(
state
.
tr
.
insert
(
position
,
node
));
extension
.
options
.
onUploadSuccess
(
fileUrl
);
}
catch
(
error
)
{
extension
.
options
.
onUploadError
(
error
);
}
};
const
handleUpload
=
async
(
file
,
view
,
pos
)
=>
{
try
{
const
filePath
=
await
uploadFile
(
file
);
if
(
!
filePath
)
return
;
const
{
state
}
=
view
;
const
{
tr
}
=
state
;
let
node
;
if
(
file
.
type
.
startsWith
(
'
image/
'
)
&&
this
.
options
.
types
.
includes
(
'
image
'
))
{
node
=
state
.
schema
.
nodes
.
image
?.
create
({
src
:
filePath
});
}
else
if
(
file
.
type
.
startsWith
(
'
video/
'
)
&&
this
.
options
.
types
.
includes
(
'
video
'
))
{
node
=
state
.
schema
.
nodes
.
video
?.
create
({
src
:
filePath
});
}
else
if
(
file
.
type
.
startsWith
(
'
audio/
'
)
&&
this
.
options
.
types
.
includes
(
'
audio
'
))
{
node
=
state
.
schema
.
nodes
.
audio
?.
create
({
src
:
filePath
});
}
// Обработчик вставки (paste)
const
handlePaste
=
(
view
,
event
)
=>
{
const
items
=
Array
.
from
(
event
.
clipboardData
?.
items
||
[]);
const
htmlData
=
event
.
clipboardData
.
getData
(
'
text/html
'
);
if
(
node
)
{
view
.
dispatch
(
tr
.
insert
(
pos
,
node
));
}
}
catch
(
error
)
{
console
.
error
(
'
Upload error:
'
,
error
);
// Если есть HTML и это контент из Word - пропускаем
if
(
htmlData
.
includes
(
'
urn:schemas-microsoft-com
'
))
{
return
false
;
}
// Фильтруем только реальные файлы
const
files
=
items
.
filter
(
item
=>
item
.
kind
===
'
file
'
)
.
map
(
item
=>
item
.
getAsFile
())
.
filter
(
file
=>
file
&&
isRealFile
(
file
));
if
(
files
.
length
===
0
)
return
false
;
event
.
preventDefault
();
const
pos
=
view
.
state
.
selection
.
from
;
files
.
forEach
(
file
=>
{
handleFileUpload
(
file
,
view
,
pos
);
});
return
true
;
};
// Обработчик перетаскивания (drop)
const
handleDrop
=
(
view
,
event
)
=>
{
const
files
=
Array
.
from
(
event
.
dataTransfer
?.
files
||
[])
.
filter
(
file
=>
isRealFile
(
file
));
if
(
files
.
length
===
0
)
return
false
;
event
.
preventDefault
();
const
pos
=
view
.
posAtCoords
({
left
:
event
.
clientX
,
top
:
event
.
clientY
,
})?.
pos
;
if
(
!
pos
)
return
false
;
files
.
forEach
(
file
=>
{
handleFileUpload
(
file
,
view
,
pos
);
});
return
true
;
};
return
[
new
Plugin
({
key
:
new
PluginKey
(
'
dragAndDrop
'
),
props
:
{
handleDOMEvents
:
{
drop
(
view
,
event
)
{
const
files
=
event
.
dataTransfer
?.
files
;
if
(
!
files
||
files
.
length
===
0
)
return
false
;
const
coordinates
=
view
.
posAtCoords
({
left
:
event
.
clientX
,
top
:
event
.
clientY
,
});
if
(
!
coordinates
)
return
false
;
event
.
preventDefault
();
Array
.
from
(
files
).
forEach
(
file
=>
{
if
(
isRealFile
({
kind
:
'
file
'
,
type
:
file
.
type
}))
{
handleUpload
(
file
,
view
,
coordinates
.
pos
);
}
});
return
true
;
},
},
handlePaste
(
view
,
event
)
{
const
items
=
event
.
clipboardData
?.
items
;
if
(
!
items
)
return
false
;
// Проверяем наличие реальных файлов
const
files
=
Array
.
from
(
items
)
.
filter
(
item
=>
item
.
kind
===
'
file
'
&&
isRealFile
(
item
))
.
map
(
item
=>
item
.
getAsFile
())
.
filter
(
Boolean
);
if
(
files
.
length
===
0
)
return
false
;
event
.
preventDefault
();
const
{
state
}
=
view
;
const
pos
=
state
.
selection
.
$from
.
pos
;
files
.
forEach
(
file
=>
{
handleUpload
(
file
,
view
,
pos
);
});
return
true
;
},
handlePaste
,
handleDrop
,
},
}),
];
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment