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
a2bb3efa
Commit
a2bb3efa
authored
May 21, 2026
by
Яков
Browse files
update fix issue
parent
18e10c9c
Changes
3
Hide whitespace changes
Inline
Side-by-side
package.json
View file @
a2bb3efa
{
{
"name"
:
"react-ag-qeditor"
,
"name"
:
"react-ag-qeditor"
,
"version"
:
"1.1.
49
"
,
"version"
:
"1.1.
50
"
,
"description"
:
"WYSIWYG html editor"
,
"description"
:
"WYSIWYG html editor"
,
"author"
:
"atma"
,
"author"
:
"atma"
,
"license"
:
"
MIT
"
,
"license"
:
"
MIT
"
,
...
...
src/QEditor.jsx
View file @
a2bb3efa
...
@@ -53,7 +53,7 @@ import { isMobile } from 'react-device-detect'
...
@@ -53,7 +53,7 @@ import { isMobile } from 'react-device-detect'
import
{
ExportPdf
}
from
'
./extensions/ExportPdf
'
import
{
ExportPdf
}
from
'
./extensions/ExportPdf
'
import
{
mergeAttributes
}
from
"
@tiptap/core
"
;
import
{
mergeAttributes
}
from
"
@tiptap/core
"
;
import
Upload
from
"
rc-upload
"
;
import
Upload
from
"
rc-upload
"
;
import
{
NodeSelection
}
from
'
prosemirror-state
'
import
{
NodeSelection
,
TextSelection
}
from
'
prosemirror-state
'
// const CustomImage = Image.extend({
// const CustomImage = Image.extend({
// options: {inline: true},
// options: {inline: true},
...
@@ -1540,7 +1540,13 @@ const QEditor = ({
...
@@ -1540,7 +1540,13 @@ const QEditor = ({
align
:
'
center
'
,
align
:
'
center
'
,
'
data-node-id
'
:
`img-
${
Date
.
now
()}
-
${
Math
.
random
().
toString
(
36
).
slice
(
2
,
10
)}
`
'
data-node-id
'
:
`img-
${
Date
.
now
()}
-
${
Math
.
random
().
toString
(
36
).
slice
(
2
,
10
)}
`
})
})
.
createParagraphNear
()
.
command
(({
tr
,
state
})
=>
{
// После setImage курсор — NodeSelection на картинке.
// Переводим в TextSelection сразу после неё.
const
{
$to
}
=
state
.
selection
tr
.
setSelection
(
TextSelection
.
create
(
tr
.
doc
,
$to
.
pos
))
return
true
})
.
run
()
.
run
()
}
}
})
})
...
...
src/components/Uploader.js
View file @
a2bb3efa
...
@@ -2,15 +2,90 @@ import React, { Fragment } from "react";
...
@@ -2,15 +2,90 @@ import React, { Fragment } from "react";
import
Upload
from
"
rc-upload
"
;
import
Upload
from
"
rc-upload
"
;
import
axios
from
"
axios
"
;
import
axios
from
"
axios
"
;
// Читает EXIF-тег ориентации из JPEG-файла (только первые 64 КБ)
function
getExifOrientation
(
file
)
{
return
new
Promise
((
resolve
)
=>
{
const
reader
=
new
FileReader
()
reader
.
onload
=
(
e
)
=>
{
const
view
=
new
DataView
(
e
.
target
.
result
)
if
(
view
.
getUint16
(
0
,
false
)
!==
0xFFD8
)
{
resolve
(
1
);
return
}
let
offset
=
2
while
(
offset
<
view
.
byteLength
)
{
const
marker
=
view
.
getUint16
(
offset
,
false
)
offset
+=
2
if
(
marker
===
0xFFE1
)
{
if
(
view
.
getUint32
(
offset
+
2
,
false
)
!==
0x45786966
)
{
resolve
(
1
);
return
}
const
little
=
view
.
getUint16
(
offset
+
8
,
false
)
===
0x4949
const
ifdStart
=
offset
+
8
+
view
.
getUint32
(
offset
+
12
,
little
)
const
entries
=
view
.
getUint16
(
ifdStart
,
little
)
for
(
let
i
=
0
;
i
<
entries
;
i
++
)
{
if
(
view
.
getUint16
(
ifdStart
+
2
+
12
*
i
,
little
)
===
0x0112
)
{
resolve
(
view
.
getUint16
(
ifdStart
+
2
+
12
*
i
+
8
,
little
))
return
}
}
resolve
(
1
);
return
}
else
if
((
marker
&
0xFF00
)
!==
0xFF00
)
{
break
}
else
{
offset
+=
view
.
getUint16
(
offset
,
false
)
}
}
resolve
(
1
)
}
reader
.
readAsArrayBuffer
(
file
.
slice
(
0
,
65536
))
})
}
// Поворачивает JPEG на canvas согласно EXIF-ориентации, возвращает исправленный File
function
fixImageOrientation
(
file
)
{
if
(
!
file
.
type
.
match
(
/^image
\/(
jpeg|jpg
)
/i
))
return
Promise
.
resolve
(
file
)
return
getExifOrientation
(
file
).
then
(
orientation
=>
{
if
(
orientation
<=
1
)
return
file
return
new
Promise
(
resolve
=>
{
const
img
=
new
window
.
Image
()
img
.
onload
=
()
=>
{
const
{
naturalWidth
:
w
,
naturalHeight
:
h
}
=
img
const
canvas
=
document
.
createElement
(
'
canvas
'
)
const
ctx
=
canvas
.
getContext
(
'
2d
'
)
// Для поворотов 5–8 меняем местами ширину и высоту
if
(
orientation
>=
5
)
{
canvas
.
width
=
h
;
canvas
.
height
=
w
}
else
{
canvas
.
width
=
w
;
canvas
.
height
=
h
}
const
transforms
=
{
2
:
[
-
1
,
0
,
0
,
1
,
w
,
0
],
3
:
[
-
1
,
0
,
0
,
-
1
,
w
,
h
],
4
:
[
1
,
0
,
0
,
-
1
,
0
,
h
],
5
:
[
0
,
1
,
1
,
0
,
0
,
0
],
6
:
[
0
,
1
,
-
1
,
0
,
h
,
0
],
7
:
[
0
,
-
1
,
-
1
,
0
,
h
,
w
],
8
:
[
0
,
-
1
,
1
,
0
,
0
,
w
],
}
if
(
transforms
[
orientation
])
ctx
.
transform
(...
transforms
[
orientation
])
ctx
.
drawImage
(
img
,
0
,
0
)
URL
.
revokeObjectURL
(
img
.
src
)
canvas
.
toBlob
(
blob
=>
resolve
(
new
File
([
blob
],
file
.
name
,
{
type
:
file
.
type
,
lastModified
:
Date
.
now
()
})),
file
.
type
,
0.92
)
}
img
.
src
=
URL
.
createObjectURL
(
file
)
})
})
}
class
Item
extends
React
.
Component
{
class
Item
extends
React
.
Component
{
get
src
()
{
get
src
()
{
return
this
.
props
.
src
;
return
this
.
props
.
src
;
}
}
render
()
{
render
()
{
switch
(
this
.
src
)
{
switch
(
this
.
src
)
{
default
:
default
:
return
null
;
return
null
;
}
}
...
@@ -37,10 +112,6 @@ export default class Uploader extends React.Component {
...
@@ -37,10 +112,6 @@ export default class Uploader extends React.Component {
this
.
files
=
{};
this
.
files
=
{};
}
}
// componentDidUpdate (prevProps, prevState, snapshot) {
// console.log(this.state.files);
// }
get
action
()
{
get
action
()
{
return
this
.
props
.
action
;
return
this
.
props
.
action
;
}
}
...
@@ -73,6 +144,10 @@ export default class Uploader extends React.Component {
...
@@ -73,6 +144,10 @@ export default class Uploader extends React.Component {
return
this
.
props
.
modalType
;
return
this
.
props
.
modalType
;
}
}
get
processingMessage
()
{
return
this
.
props
.
processingMessage
;
}
mediaItem
(){
mediaItem
(){
}
}
...
@@ -102,12 +177,8 @@ export default class Uploader extends React.Component {
...
@@ -102,12 +177,8 @@ export default class Uploader extends React.Component {
}
}
}
}
get
processingMessage
()
{
return
this
.
props
.
processingMessage
;
}
render
()
{
render
()
{
let
{
disabledFileUpload
,
isUpload
,
progress
,
dropFiles
}
=
this
.
state
;
let
{
disabledFileUpload
,
isUpload
}
=
this
.
state
;
if
(
this
.
action
===
null
){
if
(
this
.
action
===
null
){
return
<
div
style
=
{{
textAlign
:
'
left
'
}}
>
{
this
.
errorMessage
}
<
/div
>
return
<
div
style
=
{{
textAlign
:
'
left
'
}}
>
{
this
.
errorMessage
}
<
/div
>
...
@@ -153,24 +224,35 @@ export default class Uploader extends React.Component {
...
@@ -153,24 +224,35 @@ export default class Uploader extends React.Component {
action
=
{
this
.
action
}
action
=
{
this
.
action
}
multiple
=
{
this
.
multiple
}
multiple
=
{
this
.
multiple
}
disabled
=
{
disabledFileUpload
}
disabled
=
{
disabledFileUpload
}
beforeUpload
=
{(
a
,
files
)
=>
{
beforeUpload
=
{(
a
)
=>
{
this
.
files
[
a
.
uid
]
=
{...
a
,
percent
:
0
,
uploaded
:
false
};
this
.
files
[
a
.
uid
]
=
{...
a
,
percent
:
0
,
uploaded
:
false
};
this
.
setState
({
isUpload
:
true
,
files
:
this
.
files
});
this
.
setState
({
isUpload
:
true
,
files
:
this
.
files
});
}}
}}
onStart
=
{(
a
)
=>
{
onStart
=
{()
=>
{
this
.
setState
({
disabledFileUpload
:
true
,
isUpload
:
true
});
this
.
setState
({
disabledFileUpload
:
true
,
isUpload
:
true
});
// console.log('start');
}}
customRequest
=
{({
file
,
action
,
onSuccess
,
onError
,
onProgress
})
=>
{
fixImageOrientation
(
file
).
then
(
correctedFile
=>
{
const
data
=
new
FormData
()
data
.
append
(
'
file
'
,
correctedFile
,
correctedFile
.
name
)
axios
.
post
(
action
,
data
,
{
headers
:
{
'
Content-Type
'
:
'
multipart/form-data
'
},
withCredentials
:
true
,
onUploadProgress
:
(
e
)
=>
{
const
percent
=
e
.
total
?
Math
.
round
((
e
.
loaded
*
100
)
/
e
.
total
)
:
0
onProgress
({
percent
},
file
)
}
})
.
then
(
response
=>
onSuccess
(
response
.
data
,
file
))
.
catch
(
err
=>
onError
(
err
))
})
}}
}}
onSuccess
=
{(
resp
,
file
)
=>
{
onSuccess
=
{(
resp
,
file
)
=>
{
if
(
resp
.
state
===
'
success
'
&&
resp
.
file_path
)
{
if
(
resp
.
state
===
'
success
'
&&
resp
.
file_path
)
{
let
curFile
=
this
.
files
[
file
.
uid
];
let
curFile
=
this
.
files
[
file
.
uid
];
curFile
[
'
uploaded
'
]
=
true
;
curFile
[
'
uploaded
'
]
=
true
;
curFile
[
'
path
'
]
=
resp
.
poster
||
resp
.
file_path
;
curFile
[
'
path
'
]
=
resp
.
poster
||
resp
.
file_path
;
curFile
[
'
name
'
]
=
resp
.
file_name
;
curFile
[
'
name
'
]
=
resp
.
file_name
;
curFile
[
'
size
'
]
=
resp
.
file_size
;
curFile
[
'
size
'
]
=
resp
.
file_size
;
...
@@ -195,22 +277,16 @@ export default class Uploader extends React.Component {
...
@@ -195,22 +277,16 @@ export default class Uploader extends React.Component {
}
}
});
});
}
}
if
(
resp
.
state
===
'
success
'
&&
resp
.
html
)
{
if
(
resp
.
state
===
'
success
'
&&
resp
.
html
)
{
this
.
setState
({
html
:
resp
.
html
,
uploaderSuccess
:
true
},
()
=>
this
.
onSuccess
(
undefined
,
resp
.
html
))
this
.
setState
({
html
:
resp
.
html
,
uploaderSuccess
:
true
},
()
=>
this
.
onSuccess
(
undefined
,
resp
.
html
))
}
}
}}
}}
onProgress
=
{(
o
,
file
)
=>
{
onProgress
=
{(
o
,
file
)
=>
{
if
(
this
.
files
[
file
.
uid
])
{
if
(
this
.
files
[
file
.
uid
])
{
this
.
files
[
file
.
uid
].
percent
=
parseInt
(
o
.
percent
);
this
.
files
[
file
.
uid
].
percent
=
parseInt
(
o
.
percent
);
}
}
//
this
.
setState
({
files
:
this
.
files
})
this
.
setState
({
files
:
this
.
files
})
// console.log(o, file);
}}
}}
component
=
{
'
div
'
}
component
=
{
'
div
'
}
type
=
"
drag
"
type
=
"
drag
"
...
...
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