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
8c845e28
Commit
8c845e28
authored
Feb 02, 2026
by
Яков
Browse files
update iframe and video
parent
e1b05324
Changes
3
Hide whitespace changes
Inline
Side-by-side
package.json
View file @
8c845e28
{
{
"name"
:
"react-ag-qeditor"
,
"name"
:
"react-ag-qeditor"
,
"version"
:
"1.1.2
4
"
,
"version"
:
"1.1.2
5
"
,
"description"
:
"WYSIWYG html editor"
,
"description"
:
"WYSIWYG html editor"
,
"author"
:
"atma"
,
"author"
:
"atma"
,
"license"
:
"
MIT
"
,
"license"
:
"
MIT
"
,
...
...
src/extensions/Iframe.js
View file @
8c845e28
...
@@ -18,8 +18,17 @@ const getStyleForAlign = (align) => {
...
@@ -18,8 +18,17 @@ const getStyleForAlign = (align) => {
return
style
return
style
}
}
const
getOuterAlignStyle
=
(
align
)
=>
{
return
{
width
:
'
100%
'
,
display
:
'
block
'
,
textAlign
:
align
===
'
center
'
?
'
center
'
:
align
===
'
right
'
?
'
right
'
:
'
left
'
,
}
}
const
ResizableIframeView
=
({
editor
,
node
,
updateAttributes
,
getPos
,
selected
})
=>
{
const
ResizableIframeView
=
({
editor
,
node
,
updateAttributes
,
getPos
,
selected
})
=>
{
const
wrapperRef
=
useRef
(
null
)
const
outerRef
=
useRef
(
null
)
const
innerRef
=
useRef
(
null
)
const
iframeRef
=
useRef
(
null
)
const
iframeRef
=
useRef
(
null
)
const
[
showAlignMenu
,
setShowAlignMenu
]
=
useState
(
false
)
const
[
showAlignMenu
,
setShowAlignMenu
]
=
useState
(
false
)
...
@@ -36,10 +45,9 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
...
@@ -36,10 +45,9 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
const
paddingRight
=
parseFloat
(
editorStyles
.
paddingRight
)
||
0
const
paddingRight
=
parseFloat
(
editorStyles
.
paddingRight
)
||
0
const
availableEditorWidth
=
fullEditorWidth
-
paddingLeft
-
paddingRight
const
availableEditorWidth
=
fullEditorWidth
-
paddingLeft
-
paddingRight
// важно: стартуем от parentElement, чтобы не схватить wrapperRef.current
// важно: стартуем от parentElement, чтобы не схватить innerRef.current
const
startEl
=
wrapperRef
.
current
?.
parentElement
||
editorContent
const
startEl
=
outerRef
.
current
?.
parentElement
||
editorContent
const
container
=
const
container
=
startEl
.
closest
(
'
li, blockquote, td, p, div
'
)
||
editorContent
startEl
.
closest
(
'
li, blockquote, td, p, div
'
)
||
editorContent
const
containerStyles
=
window
.
getComputedStyle
(
container
)
const
containerStyles
=
window
.
getComputedStyle
(
container
)
const
containerPaddingLeft
=
parseFloat
(
containerStyles
.
paddingLeft
)
||
0
const
containerPaddingLeft
=
parseFloat
(
containerStyles
.
paddingLeft
)
||
0
...
@@ -49,7 +57,6 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
...
@@ -49,7 +57,6 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
return
{
width
:
containerWidth
,
availableSpace
:
availableEditorWidth
}
return
{
width
:
containerWidth
,
availableSpace
:
availableEditorWidth
}
}
}
const
safeUpdateAttributes
=
(
newAttrs
)
=>
{
const
safeUpdateAttributes
=
(
newAttrs
)
=>
{
const
{
width
:
containerWidth
,
availableSpace
}
=
getEditorDimensions
()
const
{
width
:
containerWidth
,
availableSpace
}
=
getEditorDimensions
()
...
@@ -57,7 +64,8 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
...
@@ -57,7 +64,8 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
let
height
=
newAttrs
.
height
??
node
.
attrs
.
height
let
height
=
newAttrs
.
height
??
node
.
attrs
.
height
if
(
typeof
width
===
'
number
'
&&
typeof
height
===
'
number
'
)
{
if
(
typeof
width
===
'
number
'
&&
typeof
height
===
'
number
'
)
{
const
maxWidth
=
node
.
attrs
.
align
===
'
center
'
?
containerWidth
:
availableSpace
const
maxWidth
=
(
newAttrs
.
align
??
node
.
attrs
.
align
)
===
'
center
'
?
containerWidth
:
availableSpace
if
(
width
>
maxWidth
)
{
if
(
width
>
maxWidth
)
{
const
ratio
=
maxWidth
/
width
const
ratio
=
maxWidth
/
width
width
=
maxWidth
width
=
maxWidth
...
@@ -117,14 +125,14 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
...
@@ -117,14 +125,14 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
console
.
warn
(
'
getPos() failed:
'
,
err
)
console
.
warn
(
'
getPos() failed:
'
,
err
)
}
}
const
startWidth
=
node
.
attrs
.
width
||
i
frame
Ref
.
current
?.
clientWidth
||
560
const
startWidth
=
node
.
attrs
.
width
||
i
nner
Ref
.
current
?.
clientWidth
||
560
const
startHeight
=
node
.
attrs
.
height
||
i
frame
Ref
.
current
?.
clientHeight
||
315
const
startHeight
=
node
.
attrs
.
height
||
i
nner
Ref
.
current
?.
clientHeight
||
315
const
aspectRatio
=
startWidth
/
startHeight
const
aspectRatio
=
startWidth
/
startHeight
const
startX
=
e
.
clientX
const
startX
=
e
.
clientX
const
startY
=
e
.
clientY
const
startY
=
e
.
clientY
const
{
width
:
containerWidth
,
availableSpace
}
=
getEditorDimensions
()
const
{
width
:
containerWidth
,
availableSpace
}
=
getEditorDimensions
()
const
maxWidth
=
node
.
attrs
.
align
===
'
center
'
?
containerWidth
:
availableSpace
const
maxWidth
=
(
node
.
attrs
.
align
||
'
left
'
)
===
'
center
'
?
containerWidth
:
availableSpace
const
onMouseMove
=
(
ev
)
=>
{
const
onMouseMove
=
(
ev
)
=>
{
requestAnimationFrame
(()
=>
{
requestAnimationFrame
(()
=>
{
...
@@ -177,145 +185,139 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
...
@@ -177,145 +185,139 @@ const ResizableIframeView = ({ editor, node, updateAttributes, getPos, selected
const
align
=
node
.
attrs
.
align
||
'
left
'
const
align
=
node
.
attrs
.
align
||
'
left
'
const
wrapperStyle
=
{
const
outerStyle
=
getOuterAlignStyle
(
align
)
const
innerStyle
=
{
position
:
'
relative
'
,
position
:
'
relative
'
,
display
:
'
inline-block
'
,
width
:
node
.
attrs
.
width
?
`
${
node
.
attrs
.
width
}
px`
:
undefined
,
width
:
node
.
attrs
.
width
?
`
${
node
.
attrs
.
width
}
px`
:
undefined
,
height
:
node
.
attrs
.
height
?
`
${
node
.
attrs
.
height
}
px`
:
undefined
,
height
:
node
.
attrs
.
height
?
`
${
node
.
attrs
.
height
}
px`
:
undefined
,
// align styles inside editor
display
:
align
===
'
center
'
?
'
block
'
:
'
inline-block
'
,
marginLeft
:
align
===
'
center
'
?
'
auto
'
:
undefined
,
marginRight
:
align
===
'
center
'
?
'
auto
'
:
undefined
,
float
:
align
===
'
left
'
?
'
left
'
:
align
===
'
right
'
?
'
right
'
:
'
none
'
,
// чтобы текст красиво обтекал
marginInlineEnd
:
align
===
'
left
'
?
'
1rem
'
:
undefined
,
marginInlineStart
:
align
===
'
right
'
?
'
1rem
'
:
undefined
,
}
}
return
(
return
(
<
NodeViewWrapper
<
NodeViewWrapper
ref
=
{
wrapp
erRef
}
ref
=
{
out
erRef
}
as
=
"
div
"
as
=
"
div
"
className
=
"
atma-iframe-wrapper
"
className
=
"
atma-iframe-wrapper
"
style
=
{
wrapp
erStyle
}
style
=
{
out
erStyle
}
data
-
align
=
{
node
.
attrs
.
align
||
'
left
'
}
data
-
align
=
{
align
}
>
>
<
iframe
<
div
ref
=
{
innerRef
}
style
=
{
innerStyle
}
>
ref
=
{
iframeRef
}
<
iframe
src
=
{
node
.
attrs
.
src
}
ref
=
{
iframeRef
}
frameBorder
=
{
node
.
attrs
.
frameborder
??
0
}
src
=
{
node
.
attrs
.
src
}
allowFullScreen
frameBorder
=
{
node
.
attrs
.
frameborder
??
0
}
allow
=
"
fullscreen
"
allowFullScreen
style
=
{{
allow
=
"
fullscreen
"
width
:
'
100%
'
,
style
=
{{
height
:
'
100%
'
,
width
:
'
100%
'
,
pointerEvents
:
editor
.
isEditable
?
'
none
'
:
'
auto
'
,
height
:
'
100%
'
,
}}
pointerEvents
:
editor
.
isEditable
?
'
none
'
:
'
auto
'
,
/
>
}}
/
>
{(
selected
||
isResizing
)
&&
(
<
Fragment
>
{(
selected
||
isResizing
)
&&
(
<
button
<
Fragment
>
type
=
"
button
"
<
button
onClick
=
{
deleteNode
}
type
=
"
button
"
style
=
{{
onClick
=
{
deleteNode
}
position
:
'
absolute
'
,
top
:
4
,
right
:
4
,
zIndex
:
30
,
backgroundColor
:
'
white
'
,
border
:
'
1px solid #d9d9d9
'
,
borderRadius
:
'
50%
'
,
width
:
20
,
height
:
20
,
fontSize
:
12
,
lineHeight
:
1
,
padding
:
'
0px 0px 2px 0px
'
,
cursor
:
'
pointer
'
,
}}
>
×
<
/button
>
{[
'
nw
'
,
'
ne
'
,
'
sw
'
,
'
se
'
].
map
((
d
)
=>
(
<
div
key
=
{
d
}
onMouseDown
=
{
handleResizeStart
(
d
)}
style
=
{{
style
=
{{
position
:
'
absolute
'
,
position
:
'
absolute
'
,
width
:
12
,
top
:
4
,
height
:
12
,
right
:
4
,
backgroundColor
:
BORDER_COLOR
,
zIndex
:
30
,
border
:
'
1px solid white
'
,
backgroundColor
:
'
white
'
,
[
d
[
0
]
===
'
n
'
?
'
top
'
:
'
bottom
'
]:
-
6
,
border
:
'
1px solid #d9d9d9
'
,
[
d
[
1
]
===
'
w
'
?
'
left
'
:
'
right
'
]:
-
6
,
borderRadius
:
'
50%
'
,
cursor
:
`
${
d
}
-resize`
,
width
:
20
,
zIndex
:
10
,
height
:
20
,
fontSize
:
12
,
lineHeight
:
1
,
padding
:
'
0px 0px 2px 0px
'
,
cursor
:
'
pointer
'
,
}}
>
×
<
/button
>
{[
'
nw
'
,
'
ne
'
,
'
sw
'
,
'
se
'
].
map
((
d
)
=>
(
<
div
key
=
{
d
}
onMouseDown
=
{
handleResizeStart
(
d
)}
style
=
{{
position
:
'
absolute
'
,
width
:
12
,
height
:
12
,
backgroundColor
:
BORDER_COLOR
,
border
:
'
1px solid white
'
,
[
d
[
0
]
===
'
n
'
?
'
top
'
:
'
bottom
'
]:
-
6
,
[
d
[
1
]
===
'
w
'
?
'
left
'
:
'
right
'
]:
-
6
,
cursor
:
`
${
d
}
-resize`
,
zIndex
:
10
,
}}
/
>
))}
{
showAlignMenu
&&
(
<
div
style
=
{{
position
:
'
absolute
'
,
top
:
-
40
,
left
:
'
50%
'
,
transform
:
'
translateX(-50%)
'
,
backgroundColor
:
'
white
'
,
boxShadow
:
'
0 2px 8px rgba(0,0,0,0.15)
'
,
borderRadius
:
4
,
padding
:
4
,
zIndex
:
20
,
display
:
'
flex
'
,
}}
>
{
ALIGN_OPTIONS
.
map
((
a
)
=>
(
<
button
type
=
"
button
"
key
=
{
a
}
onClick
=
{()
=>
handleAlign
(
a
)}
style
=
{{
margin
:
'
0 2px
'
,
padding
:
'
10px 8px
'
,
background
:
align
===
a
?
'
#e6f7ff
'
:
'
transparent
'
,
border
:
'
1px solid #d9d9d9
'
,
borderRadius
:
2
,
cursor
:
'
pointer
'
,
}}
>
{
a
}
<
/button
>
))}
<
/div
>
)}
<
button
type
=
"
button
"
onClick
=
{(
e
)
=>
{
e
.
stopPropagation
()
setShowAlignMenu
((
v
)
=>
!
v
)
}}
}}
/
>
))}
{
showAlignMenu
&&
(
<
div
style
=
{{
style
=
{{
position
:
'
absolute
'
,
position
:
'
absolute
'
,
top
:
-
4
0
,
top
:
-
3
0
,
left
:
'
50%
'
,
left
:
'
calc(50% - 6px)
'
,
transform
:
'
translateX(-50%)
'
,
transform
:
'
translateX(-50%)
'
,
backgroundColor
:
'
white
'
,
backgroundColor
:
'
white
'
,
bo
xShadow
:
'
0 2px 8px rgba(0,0,0,0.15)
'
,
bo
rder
:
`1px solid
${
BORDER_COLOR
}
`
,
borderRadius
:
4
,
borderRadius
:
4
,
padding
:
4
,
padding
:
'
8px 8px
'
,
zIndex
:
20
,
cursor
:
'
pointer
'
,
display
:
'
flex
'
,
fontSize
:
12
,
zIndex
:
10
,
}}
}}
>
>
{
ALIGN_OPTIONS
.
map
((
align
)
=>
(
Align
<
button
<
/button
>
type
=
"
button
"
<
/Fragment
>
key
=
{
align
}
)}
onClick
=
{()
=>
handleAlign
(
align
)}
<
/div
>
style
=
{{
margin
:
'
0 2px
'
,
padding
:
'
10px 8px
'
,
background
:
node
.
attrs
.
align
===
align
?
'
#e6f7ff
'
:
'
transparent
'
,
border
:
'
1px solid #d9d9d9
'
,
borderRadius
:
2
,
cursor
:
'
pointer
'
,
}}
>
{
align
}
<
/button
>
))}
<
/div
>
)}
<
button
type
=
"
button
"
onClick
=
{(
e
)
=>
{
e
.
stopPropagation
()
setShowAlignMenu
((
v
)
=>
!
v
)
}}
style
=
{{
position
:
'
absolute
'
,
top
:
-
30
,
left
:
'
calc(50% - 6px)
'
,
transform
:
'
translateX(-50%)
'
,
backgroundColor
:
'
white
'
,
border
:
`1px solid
${
BORDER_COLOR
}
`
,
borderRadius
:
4
,
padding
:
'
8px 8px
'
,
cursor
:
'
pointer
'
,
fontSize
:
12
,
zIndex
:
10
,
}}
>
Align
<
/button
>
<
/Fragment
>
)}
<
/NodeViewWrapper
>
<
/NodeViewWrapper
>
)
)
}
}
...
...
src/extensions/Video.js
View file @
8c845e28
...
@@ -18,8 +18,17 @@ const getStyleForAlign = (align) => {
...
@@ -18,8 +18,17 @@ const getStyleForAlign = (align) => {
return
style
return
style
}
}
const
getOuterAlignStyle
=
(
align
)
=>
{
return
{
width
:
'
100%
'
,
display
:
'
block
'
,
textAlign
:
align
===
'
center
'
?
'
center
'
:
align
===
'
right
'
?
'
right
'
:
'
left
'
,
}
}
const
ResizableVideoView
=
({
editor
,
node
,
updateAttributes
,
getPos
,
selected
})
=>
{
const
ResizableVideoView
=
({
editor
,
node
,
updateAttributes
,
getPos
,
selected
})
=>
{
const
wrapperRef
=
useRef
(
null
)
const
outerRef
=
useRef
(
null
)
const
innerRef
=
useRef
(
null
)
const
videoRef
=
useRef
(
null
)
const
videoRef
=
useRef
(
null
)
const
[
showAlignMenu
,
setShowAlignMenu
]
=
useState
(
false
)
const
[
showAlignMenu
,
setShowAlignMenu
]
=
useState
(
false
)
...
@@ -36,7 +45,9 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }
...
@@ -36,7 +45,9 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }
const
paddingRight
=
parseFloat
(
editorStyles
.
paddingRight
)
||
0
const
paddingRight
=
parseFloat
(
editorStyles
.
paddingRight
)
||
0
const
availableEditorWidth
=
fullEditorWidth
-
paddingLeft
-
paddingRight
const
availableEditorWidth
=
fullEditorWidth
-
paddingLeft
-
paddingRight
const
container
=
wrapperRef
.
current
?.
closest
(
'
li, blockquote, td, p, div
'
)
||
editorContent
const
startEl
=
outerRef
.
current
?.
parentElement
||
editorContent
const
container
=
startEl
.
closest
(
'
li, blockquote, td, p, div
'
)
||
editorContent
const
containerStyles
=
window
.
getComputedStyle
(
container
)
const
containerStyles
=
window
.
getComputedStyle
(
container
)
const
containerPaddingLeft
=
parseFloat
(
containerStyles
.
paddingLeft
)
||
0
const
containerPaddingLeft
=
parseFloat
(
containerStyles
.
paddingLeft
)
||
0
const
containerPaddingRight
=
parseFloat
(
containerStyles
.
paddingRight
)
||
0
const
containerPaddingRight
=
parseFloat
(
containerStyles
.
paddingRight
)
||
0
...
@@ -52,7 +63,8 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }
...
@@ -52,7 +63,8 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }
let
height
=
newAttrs
.
height
??
node
.
attrs
.
height
let
height
=
newAttrs
.
height
??
node
.
attrs
.
height
if
(
typeof
width
===
'
number
'
&&
typeof
height
===
'
number
'
)
{
if
(
typeof
width
===
'
number
'
&&
typeof
height
===
'
number
'
)
{
const
maxWidth
=
node
.
attrs
.
align
===
'
center
'
?
containerWidth
:
availableSpace
const
maxWidth
=
(
newAttrs
.
align
??
node
.
attrs
.
align
)
===
'
center
'
?
containerWidth
:
availableSpace
if
(
width
>
maxWidth
)
{
if
(
width
>
maxWidth
)
{
const
ratio
=
maxWidth
/
width
const
ratio
=
maxWidth
/
width
width
=
maxWidth
width
=
maxWidth
...
@@ -112,14 +124,14 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }
...
@@ -112,14 +124,14 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }
console
.
warn
(
'
getPos() failed:
'
,
err
)
console
.
warn
(
'
getPos() failed:
'
,
err
)
}
}
const
startWidth
=
node
.
attrs
.
width
||
video
Ref
.
current
?.
clientWidth
||
640
const
startWidth
=
node
.
attrs
.
width
||
inner
Ref
.
current
?.
clientWidth
||
640
const
startHeight
=
node
.
attrs
.
height
||
video
Ref
.
current
?.
clientHeight
||
360
const
startHeight
=
node
.
attrs
.
height
||
inner
Ref
.
current
?.
clientHeight
||
360
const
aspectRatio
=
startWidth
/
startHeight
const
aspectRatio
=
startWidth
/
startHeight
const
startX
=
e
.
clientX
const
startX
=
e
.
clientX
const
startY
=
e
.
clientY
const
startY
=
e
.
clientY
const
{
width
:
containerWidth
,
availableSpace
}
=
getEditorDimensions
()
const
{
width
:
containerWidth
,
availableSpace
}
=
getEditorDimensions
()
const
maxWidth
=
node
.
attrs
.
align
===
'
center
'
?
containerWidth
:
availableSpace
const
maxWidth
=
(
node
.
attrs
.
align
||
'
left
'
)
===
'
center
'
?
containerWidth
:
availableSpace
const
onMouseMove
=
(
ev
)
=>
{
const
onMouseMove
=
(
ev
)
=>
{
requestAnimationFrame
(()
=>
{
requestAnimationFrame
(()
=>
{
...
@@ -170,134 +182,140 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }
...
@@ -170,134 +182,140 @@ const ResizableVideoView = ({ editor, node, updateAttributes, getPos, selected }
}
}
}
}
const
wrapperStyle
=
{
const
align
=
node
.
attrs
.
align
||
'
left
'
const
outerStyle
=
getOuterAlignStyle
(
align
)
const
innerStyle
=
{
position
:
'
relative
'
,
position
:
'
relative
'
,
display
:
node
.
attrs
.
align
===
'
center
'
?
'
block
'
:
'
inline-block
'
,
display
:
'
inline-block
'
,
width
:
node
.
attrs
.
width
?
`
${
node
.
attrs
.
width
}
px`
:
undefined
,
width
:
node
.
attrs
.
width
?
`
${
node
.
attrs
.
width
}
px`
:
undefined
,
height
:
node
.
attrs
.
height
?
`
${
node
.
attrs
.
height
}
px`
:
undefined
,
height
:
node
.
attrs
.
height
?
`
${
node
.
attrs
.
height
}
px`
:
undefined
,
}
}
return
(
return
(
<
NodeViewWrapper
<
NodeViewWrapper
ref
=
{
wrapp
erRef
}
ref
=
{
out
erRef
}
as
=
"
div
"
as
=
"
div
"
className
=
"
atma-video-wrapper
"
className
=
"
atma-video-wrapper
"
style
=
{
wrapp
erStyle
}
style
=
{
out
erStyle
}
data
-
align
=
{
node
.
attrs
.
align
||
'
left
'
}
data
-
align
=
{
align
}
>
>
<
video
<
div
ref
=
{
innerRef
}
style
=
{
innerStyle
}
>
ref
=
{
videoRef
}
<
video
src
=
{
node
.
attrs
.
src
}
ref
=
{
videoRef
}
poster
=
{
node
.
attrs
.
poster
}
src
=
{
node
.
attrs
.
src
}
controls
=
{
node
.
attrs
.
controls
!==
false
}
poster
=
{
node
.
attrs
.
poster
}
style
=
{{
controls
=
{
node
.
attrs
.
controls
!==
false
}
width
:
'
100%
'
,
style
=
{{
height
:
'
100%
'
,
width
:
'
100%
'
,
pointerEvents
:
editor
.
isEditable
?
'
none
'
:
'
auto
'
,
height
:
'
100%
'
,
}}
pointerEvents
:
editor
.
isEditable
?
'
none
'
:
'
auto
'
,
/
>
}}
/
>
{(
selected
||
isResizing
)
&&
(
<
Fragment
>
{(
selected
||
isResizing
)
&&
(
<
button
<
Fragment
>
type
=
"
button
"
<
button
onClick
=
{
deleteNode
}
type
=
"
button
"
style
=
{{
onClick
=
{
deleteNode
}
position
:
'
absolute
'
,
top
:
4
,
right
:
4
,
zIndex
:
30
,
backgroundColor
:
'
white
'
,
border
:
'
1px solid #d9d9d9
'
,
borderRadius
:
'
50%
'
,
width
:
20
,
height
:
20
,
fontSize
:
12
,
lineHeight
:
1
,
padding
:
'
0px 0px 2px 0px
'
,
cursor
:
'
pointer
'
,
}}
>
×
<
/button
>
{[
'
nw
'
,
'
ne
'
,
'
sw
'
,
'
se
'
].
map
((
d
)
=>
(
<
div
key
=
{
d
}
onMouseDown
=
{
handleResizeStart
(
d
)}
style
=
{{
style
=
{{
position
:
'
absolute
'
,
position
:
'
absolute
'
,
width
:
12
,
top
:
4
,
height
:
12
,
right
:
4
,
backgroundColor
:
BORDER_COLOR
,
zIndex
:
30
,
border
:
'
1px solid white
'
,
backgroundColor
:
'
white
'
,
[
d
[
0
]
===
'
n
'
?
'
top
'
:
'
bottom
'
]:
-
6
,
border
:
'
1px solid #d9d9d9
'
,
[
d
[
1
]
===
'
w
'
?
'
left
'
:
'
right
'
]:
-
6
,
borderRadius
:
'
50%
'
,
cursor
:
`
${
d
}
-resize`
,
width
:
20
,
zIndex
:
10
,
height
:
20
,
fontSize
:
12
,
lineHeight
:
1
,
padding
:
'
0px 0px 2px 0px
'
,
cursor
:
'
pointer
'
,
}}
>
×
<
/button
>
{[
'
nw
'
,
'
ne
'
,
'
sw
'
,
'
se
'
].
map
((
d
)
=>
(
<
div
key
=
{
d
}
onMouseDown
=
{
handleResizeStart
(
d
)}
style
=
{{
position
:
'
absolute
'
,
width
:
12
,
height
:
12
,
backgroundColor
:
BORDER_COLOR
,
border
:
'
1px solid white
'
,
[
d
[
0
]
===
'
n
'
?
'
top
'
:
'
bottom
'
]:
-
6
,
[
d
[
1
]
===
'
w
'
?
'
left
'
:
'
right
'
]:
-
6
,
cursor
:
`
${
d
}
-resize`
,
zIndex
:
10
,
}}
/
>
))}
{
showAlignMenu
&&
(
<
div
style
=
{{
position
:
'
absolute
'
,
top
:
-
40
,
left
:
'
50%
'
,
transform
:
'
translateX(-50%)
'
,
backgroundColor
:
'
white
'
,
boxShadow
:
'
0 2px 8px rgba(0,0,0,0.15)
'
,
borderRadius
:
4
,
padding
:
4
,
zIndex
:
20
,
display
:
'
flex
'
,
}}
>
{
ALIGN_OPTIONS
.
map
((
a
)
=>
(
<
button
type
=
"
button
"
key
=
{
a
}
onClick
=
{()
=>
handleAlign
(
a
)}
style
=
{{
margin
:
'
0 2px
'
,
padding
:
'
10px 8px
'
,
background
:
align
===
a
?
'
#e6f7ff
'
:
'
transparent
'
,
border
:
'
1px solid #d9d9d9
'
,
borderRadius
:
2
,
cursor
:
'
pointer
'
,
}}
>
{
a
}
<
/button
>
))}
<
/div
>
)}
<
button
type
=
"
button
"
onClick
=
{(
e
)
=>
{
e
.
stopPropagation
()
setShowAlignMenu
((
v
)
=>
!
v
)
}}
}}
/
>
))}
{
showAlignMenu
&&
(
<
div
style
=
{{
style
=
{{
position
:
'
absolute
'
,
position
:
'
absolute
'
,
top
:
-
4
0
,
top
:
-
3
0
,
left
:
'
50%
'
,
left
:
'
calc(50% - 6px)
'
,
transform
:
'
translateX(-50%)
'
,
transform
:
'
translateX(-50%)
'
,
backgroundColor
:
'
white
'
,
backgroundColor
:
'
white
'
,
bo
xShadow
:
'
0 2px 8px rgba(0,0,0,0.15)
'
,
bo
rder
:
`1px solid
${
BORDER_COLOR
}
`
,
borderRadius
:
4
,
borderRadius
:
4
,
padding
:
4
,
padding
:
'
8px 8px
'
,
zIndex
:
20
,
cursor
:
'
pointer
'
,
display
:
'
flex
'
,
fontSize
:
12
,
zIndex
:
10
,
}}
}}
>
>
{
ALIGN_OPTIONS
.
map
((
align
)
=>
(
Align
<
button
<
/button
>
type
=
"
button
"
<
/Fragment
>
key
=
{
align
}
)}
onClick
=
{()
=>
handleAlign
(
align
)}
<
/div
>
style
=
{{
margin
:
'
0 2px
'
,
padding
:
'
10px 8px
'
,
background
:
node
.
attrs
.
align
===
align
?
'
#e6f7ff
'
:
'
transparent
'
,
border
:
'
1px solid #d9d9d9
'
,
borderRadius
:
2
,
cursor
:
'
pointer
'
,
}}
>
{
align
}
<
/button
>
))}
<
/div
>
)}
<
button
type
=
"
button
"
onClick
=
{(
e
)
=>
{
e
.
stopPropagation
()
setShowAlignMenu
((
v
)
=>
!
v
)
}}
style
=
{{
position
:
'
absolute
'
,
top
:
-
30
,
left
:
'
calc(50% - 6px)
'
,
transform
:
'
translateX(-50%)
'
,
backgroundColor
:
'
white
'
,
border
:
`1px solid
${
BORDER_COLOR
}
`
,
borderRadius
:
4
,
padding
:
'
8px 8px
'
,
cursor
:
'
pointer
'
,
fontSize
:
12
,
zIndex
:
10
,
}}
>
Align
<
/button
>
<
/Fragment
>
)}
<
/NodeViewWrapper
>
<
/NodeViewWrapper
>
)
)
}
}
...
...
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