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
0b426860
Commit
0b426860
authored
Apr 17, 2026
by
Яков
Browse files
update image
parent
a123742c
Changes
2
Show whitespace changes
Inline
Side-by-side
package.json
View file @
0b426860
{
{
"name"
:
"react-ag-qeditor"
,
"name"
:
"react-ag-qeditor"
,
"version"
:
"1.1.3
5
"
,
"version"
:
"1.1.3
6
"
,
"description"
:
"WYSIWYG html editor"
,
"description"
:
"WYSIWYG html editor"
,
"author"
:
"atma"
,
"author"
:
"atma"
,
"license"
:
"
MIT
"
,
"license"
:
"
MIT
"
,
...
...
src/extensions/Image.jsx
View file @
0b426860
...
@@ -8,17 +8,18 @@ const {Text} = Typography;
...
@@ -8,17 +8,18 @@ const {Text} = Typography;
const
MIN_WIDTH
=
60
;
const
MIN_WIDTH
=
60
;
const
BORDER_COLOR
=
'
#0096fd
'
;
const
BORDER_COLOR
=
'
#0096fd
'
;
const
ALIGN_OPTIONS
=
[
'
left
'
,
'
center
'
,
'
right
'
,
'
text
'
];
const
ALIGN_OPTIONS
=
[
'
left
'
,
'
center
'
,
'
right
'
];
const
ResizableImageTemplate
=
({
node
,
updateAttributes
,
editor
,
getPos
,
selected
})
=>
{
const
ResizableImageTemplate
=
({
node
,
updateAttributes
,
editor
,
getPos
,
selected
})
=>
{
const
imgRef
=
useRef
(
null
);
const
imgRef
=
useRef
(
null
);
const
wrapperRef
=
useRef
(
null
);
const
wrapperRef
=
useRef
(
null
);
const
[
showAlignMenu
,
setShowAlignMenu
]
=
useState
(
false
);
const
isInitialized
=
useRef
(
false
);
const
isInitialized
=
useRef
(
false
);
const
[
isResizing
,
setIsResizing
]
=
useState
(
false
);
const
[
isResizing
,
setIsResizing
]
=
useState
(
false
);
const
[
altModalVisible
,
setAltModalVisible
]
=
useState
(
false
);
const
[
altModalVisible
,
setAltModalVisible
]
=
useState
(
false
);
const
[
tempAlt
,
setTempAlt
]
=
useState
(
node
.
attrs
.
alt
||
''
);
const
[
tempAlt
,
setTempAlt
]
=
useState
(
node
.
attrs
.
alt
||
''
);
const
[
tempFrontAlt
,
setTempFrontAlt
]
=
useState
(
node
.
attrs
.
frontAlt
||
''
);
const
[
tempFrontAlt
,
setTempFrontAlt
]
=
useState
(
node
.
attrs
.
frontAlt
||
''
);
// wrap=false + left/right: outer wrapper is full-width block, inner div holds image+handles
const
isNoWrap
=
!
node
.
attrs
.
wrap
&&
(
node
.
attrs
.
align
===
'
left
'
||
node
.
attrs
.
align
===
'
right
'
);
// Добавляем прозрачный нулевой пробел после изображения
// Добавляем прозрачный нулевой пробел после изображения
...
@@ -370,55 +371,68 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
...
@@ -370,55 +371,68 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
console
.
warn
(
'
getPos() failed:
'
,
e
)
console
.
warn
(
'
getPos() failed:
'
,
e
)
}
}
},
50
);
},
50
);
setShowAlignMenu
(
false
);
};
};
// Стили для обертки изображения
// Внешняя обёртка (NodeViewWrapper): управляет float/block-layout и отступами
const
getWrapperStyle
=
()
=>
{
const
getOuterStyle
=
()
=>
{
const
baseStyle
=
{
const
{
align
,
wrap
,
width
}
=
node
.
attrs
;
display
:
'
inline-block
'
,
const
w
=
width
?
`
${
width
}
px`
:
'
auto
'
;
lineHeight
:
0
,
const
sharedMargin
=
{
marginTop
:
'
0.5rem
'
,
marginBottom
:
'
0.5rem
'
};
position
:
'
relative
'
,
outline
:
(
selected
||
isResizing
)
?
`1px dashed
${
BORDER_COLOR
}
`
:
'
none
'
,
verticalAlign
:
'
top
'
,
margin
:
'
0.5rem 0
'
,
};
if
(
node
.
attrs
.
align
===
'
center
'
)
{
if
(
align
===
'
center
'
)
{
return
{
...
sharedMargin
,
display
:
'
block
'
,
lineHeight
:
0
};
}
if
(
!
wrap
)
{
// no-wrap: float:left + width:100% — занимает всю строку, текст не может встать рядом.
// Используем float (а не display:block) чтобы layout-алгоритм был одинаковым
// с wrap=true и не было прыжка при переключении обтекания.
return
{
return
{
...
baseStyle
,
...
sharedMargin
,
lineHeight
:
0
,
display
:
'
block
'
,
display
:
'
inline-block
'
,
marginLeft
:
'
auto
'
,
float
:
'
left
'
,
marginRight
:
'
auto
'
,
clear
:
'
both
'
,
width
:
node
.
attrs
.
width
?
`
${
node
.
attrs
.
width
}
px`
:
'
fit-content
'
,
width
:
'
100%
'
,
maxWidth
:
'
100%
'
,
...(
align
===
'
right
'
?
{
textAlign
:
'
right
'
}
:
{
textAlign
:
'
left
'
}),
textAlign
:
'
center
'
};
};
}
}
// wrap: true — узкий float с шириной картинки, текст обтекает
return
{
return
{
...
baseStyle
,
...
sharedMargin
,
lineHeight
:
0
,
...(
node
.
attrs
.
align
===
'
left
'
&&
{
float
:
'
left
'
,
marginRight
:
'
1rem
'
,
width
:
node
.
attrs
.
width
?
`
${
node
.
attrs
.
width
}
px`
:
'
auto
'
,
maxWidth
:
'
100%
'
,
}),
...(
node
.
attrs
.
align
===
'
right
'
&&
{
float
:
'
right
'
,
marginLeft
:
'
1rem
'
,
width
:
node
.
attrs
.
width
?
`
${
node
.
attrs
.
width
}
px`
:
'
auto
'
,
maxWidth
:
'
100%
'
,
}),
...(
node
.
attrs
.
align
===
'
text
'
&&
{
display
:
'
inline-block
'
,
display
:
'
inline-block
'
,
float
:
'
none
'
,
float
:
align
===
'
left
'
?
'
left
'
:
'
right
'
,
margin
:
'
0 0.2rem
'
,
...(
align
===
'
left
'
?
{
marginRight
:
'
1rem
'
}
:
{
marginLeft
:
'
1rem
'
}),
verticalAlign
:
'
middle
'
,
width
:
w
,
maxWidth
:
'
100%
'
,
width
:
node
.
attrs
.
width
?
`
${
node
.
attrs
.
width
}
px`
:
'
auto
'
,
};
};
// Внутренний контейнер: всегда inline-block — надёжно получает высоту от дочернего img
const
getInnerStyle
=
()
=>
{
const
{
align
,
width
}
=
node
.
attrs
;
const
w
=
width
?
`
${
width
}
px`
:
'
auto
'
;
const
base
=
{
position
:
'
relative
'
,
display
:
'
inline-block
'
,
verticalAlign
:
'
top
'
,
lineHeight
:
0
,
outline
:
(
selected
||
isResizing
)
?
`1px dashed
${
BORDER_COLOR
}
`
:
'
none
'
,
width
:
w
,
maxWidth
:
'
100%
'
,
maxWidth
:
'
100%
'
,
}),
};
};
if
(
align
===
'
center
'
)
{
return
{
...
base
,
display
:
'
block
'
,
marginLeft
:
'
auto
'
,
marginRight
:
'
auto
'
,
width
:
width
?
`
${
width
}
px`
:
'
fit-content
'
};
}
return
base
;
};
const
handleNodeClick
=
(
e
)
=>
{
e
.
stopPropagation
();
try
{
const
pos
=
getPos
?.();
if
(
typeof
pos
===
'
number
'
)
editor
.
commands
.
setNodeSelection
(
pos
);
}
catch
(
err
)
{
console
.
warn
(
'
getPos() failed:
'
,
err
);
}
};
};
// Стили для самого изображения
// Стили для самого изображения
...
@@ -434,49 +448,16 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
...
@@ -434,49 +448,16 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
objectFit
:
'
contain
'
objectFit
:
'
contain
'
});
});
console
.
log
(
node
.
attrs
.
frontAlt
);
// Inner content shared between both rendering paths
return
(
const
imageContent
=
(
<
NodeViewWrapper
<>
as
=
"div"
style
=
{
getWrapperStyle
()
}
ref
=
{
wrapperRef
}
onClick
=
{
(
e
)
=>
{
e
.
stopPropagation
();
try
{
const
pos
=
getPos
?.()
if
(
typeof
pos
===
'
number
'
)
{
editor
.
commands
.
setNodeSelection
(
pos
)
}
}
catch
(
e
)
{
console
.
warn
(
'
getPos() failed:
'
,
e
)
}
// editor.commands.setNodeSelection(getPos());
}
}
contentEditable
=
{
false
}
data
-
image
-
wrapper
>
<
img
<
img
{
...
node
.
attrs
}
{
...
node
.
attrs
}
ref
=
{
imgRef
}
ref
=
{
imgRef
}
draggable
=
{
true
}
draggable
=
{
true
}
style
=
{
getImageStyle
()
}
style
=
{
getImageStyle
()
}
// onLoad={() => {
// if (imgRef.current && !isInitialized.current && !node.attrs.width && !node.attrs.height) {
// const { width: editorWidth } = getEditorDimensions();
// const naturalWidth = imgRef.current.naturalWidth;
// const naturalHeight = imgRef.current.naturalHeight;
//
// safeUpdateAttributes({
// width: naturalWidth,
// height: naturalHeight,
// 'data-node-id': node.attrs['data-node-id'] || Math.random().toString(36).substr(2, 9)
// });
// isInitialized.current = true;
// }
// }}
/>
/>
{
{
node
.
attrs
.
frontAlt
?.
length
>
0
&&
(
node
.
attrs
.
frontAlt
?.
length
>
0
&&
<
div
<
div
style
=
{
{
style
=
{
{
backgroundColor
:
'
#FDE674
'
,
backgroundColor
:
'
#FDE674
'
,
...
@@ -495,7 +476,7 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
...
@@ -495,7 +476,7 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
whiteSpace
:
'
pre-line
'
whiteSpace
:
'
pre-line
'
}
}
}
}
>
{
node
.
attrs
.
frontAlt
}
</
div
>
>
{
node
.
attrs
.
frontAlt
}
</
div
>
}
)
}
<
Button
<
Button
size
=
"default"
size
=
"default"
shape
=
{
'
circle
'
}
shape
=
{
'
circle
'
}
...
@@ -506,12 +487,7 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
...
@@ -506,12 +487,7 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
setTempFrontAlt
(
node
.
attrs
.
frontAlt
||
''
);
setTempFrontAlt
(
node
.
attrs
.
frontAlt
||
''
);
setAltModalVisible
(
true
);
setAltModalVisible
(
true
);
}
}
}
}
style
=
{
{
style
=
{
{
position
:
'
absolute
'
,
top
:
4
,
right
:
'
30px
'
,
zIndex
:
15
}
}
position
:
'
absolute
'
,
top
:
4
,
right
:
'
30px
'
,
zIndex
:
15
,
}
}
>
>
<
FontSizeOutlined
/>
<
FontSizeOutlined
/>
</
Button
>
</
Button
>
...
@@ -521,33 +497,21 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
...
@@ -521,33 +497,21 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
danger
danger
size
=
"small"
size
=
"small"
onClick
=
{
(
e
)
=>
{
onClick
=
{
(
e
)
=>
{
e
.
stopPropagation
()
e
.
stopPropagation
();
const
pos
=
getPos
?.();
const
pos
=
getPos
?.()
if
(
typeof
pos
===
'
number
'
)
{
if
(
typeof
pos
===
'
number
'
)
{
editor
.
view
.
dispatch
(
editor
.
view
.
dispatch
(
editor
.
view
.
state
.
tr
.
delete
(
pos
,
pos
+
node
.
nodeSize
)
editor
.
view
.
state
.
tr
.
delete
(
pos
,
pos
+
node
.
nodeSize
)
)
)
;
}
}
}
}
}
}
style
=
{
{
style
=
{
{
position
:
'
absolute
'
,
position
:
'
absolute
'
,
top
:
4
,
right
:
4
,
zIndex
:
30
,
top
:
4
,
backgroundColor
:
'
white
'
,
border
:
'
1px solid #d9d9d9
'
,
right
:
4
,
borderRadius
:
'
50%
'
,
width
:
20
,
height
:
20
,
zIndex
:
30
,
fontSize
:
12
,
lineHeight
:
1
,
padding
:
'
0px 0px 2px 0px
'
,
cursor
:
'
pointer
'
backgroundColor
:
'
white
'
,
border
:
'
1px solid #d9d9d9
'
,
borderRadius
:
'
50%
'
,
width
:
20
,
height
:
20
,
fontSize
:
12
,
lineHeight
:
1
,
padding
:
'
0px 0px 2px 0px
'
,
cursor
:
'
pointer
'
}
}
}
}
>
>
×
</
Button
>
×
</
Button
>
)
}
)
}
{
(
selected
||
isResizing
)
&&
(
{
(
selected
||
isResizing
)
&&
(
<
Fragment
>
<
Fragment
>
...
@@ -558,103 +522,126 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
...
@@ -558,103 +522,126 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
onTouchStart
=
{
handleResizeStart
(
dir
)
}
onTouchStart
=
{
handleResizeStart
(
dir
)
}
style
=
{
{
style
=
{
{
position
:
'
absolute
'
,
position
:
'
absolute
'
,
width
:
12
,
width
:
12
,
height
:
12
,
height
:
12
,
backgroundColor
:
BORDER_COLOR
,
backgroundColor
:
BORDER_COLOR
,
border
:
'
1px solid white
'
,
border
:
'
1px solid white
'
,
[
dir
[
0
]
===
'
n
'
?
'
top
'
:
'
bottom
'
]:
-
6
,
[
dir
[
0
]
===
'
n
'
?
'
top
'
:
'
bottom
'
]:
-
6
,
[
dir
[
1
]
===
'
w
'
?
'
left
'
:
'
right
'
]:
node
.
attrs
.
align
===
'
center
'
?
'
50%
'
:
-
6
,
[
dir
[
1
]
===
'
w
'
?
'
left
'
:
'
right
'
]:
node
.
attrs
.
align
===
'
center
'
?
'
50%
'
:
-
6
,
transform
:
node
.
attrs
.
align
===
'
center
'
?
transform
:
node
.
attrs
.
align
===
'
center
'
`translateX(
${
dir
[
1
]
===
'
w
'
?
'
-100%
'
:
'
0%
'
}
)`
:
'
none
'
,
?
`translateX(
${
dir
[
1
]
===
'
w
'
?
'
-100%
'
:
'
0%
'
}
)`
:
'
none
'
,
cursor
:
`
${
dir
}
-resize`
,
cursor
:
`
${
dir
}
-resize`
,
zIndex
:
10
zIndex
:
10
}
}
}
}
/>
/>
))
}
))
}
{
showAlignMenu
&&
(
<
div
style
=
{
{
<
div
style
=
{
{
position
:
'
absolute
'
,
position
:
'
absolute
'
,
top
:
-
36
,
left
:
'
50%
'
,
top
:
-
40
,
left
:
'
50%
'
,
transform
:
'
translateX(-50%)
'
,
transform
:
'
translateX(-50%)
'
,
backgroundColor
:
'
white
'
,
backgroundColor
:
'
white
'
,
boxShadow
:
'
0 2px 8px rgba(0,0,0,0.15)
'
,
boxShadow
:
'
0 2px 8px rgba(0,0,0,0.15)
'
,
borderRadius
:
4
,
borderRadius
:
4
,
padding
:
4
,
zIndex
:
20
,
padding
:
4
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
gap
:
2
,
whiteSpace
:
'
nowrap
'
,
zIndex
:
20
,
display
:
'
flex
'
}
}
>
}
}
>
{
ALIGN_OPTIONS
.
map
(
a
lign
=>
(
{
ALIGN_OPTIONS
.
map
(
a
=>
(
<
button
<
button
type
=
"button"
type
=
"button"
key
=
{
align
}
key
=
{
a
}
onClick
=
{
()
=>
handleAlign
(
align
)
}
title
=
{
a
===
'
left
'
?
'
По левому краю
'
:
a
===
'
center
'
?
'
По центру
'
:
'
По правому краю
'
}
onClick
=
{
()
=>
handleAlign
(
a
)
}
style
=
{
{
style
=
{
{
margin
:
'
0 2px
'
,
padding
:
'
4px 6px
'
,
padding
:
'
10px 8px
'
,
background
:
node
.
attrs
.
align
===
a
?
'
#e6f7ff
'
:
'
transparent
'
,
background
:
node
.
attrs
.
align
===
align
?
'
#e6f7ff
'
:
'
transparent
'
,
border
:
`1px solid
${
node
.
attrs
.
align
===
a
?
BORDER_COLOR
:
'
#d9d9d9
'
}
`
,
border
:
'
1px solid #d9d9d9
'
,
borderRadius
:
2
,
cursor
:
'
pointer
'
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
borderRadius
:
2
,
cursor
:
'
pointer
'
}
}
}
}
>
>
{
align
}
{
a
===
'
left
'
&&
(
<
svg
width
=
"16"
height
=
"14"
viewBox
=
"0 0 16 14"
fill
=
"none"
xmlns
=
"http://www.w3.org/2000/svg"
>
<
rect
x
=
"0"
y
=
"0"
width
=
"16"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
<
rect
x
=
"0"
y
=
"4"
width
=
"10"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
<
rect
x
=
"0"
y
=
"8"
width
=
"16"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
<
rect
x
=
"0"
y
=
"12"
width
=
"10"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
</
svg
>
)
}
{
a
===
'
center
'
&&
(
<
svg
width
=
"16"
height
=
"14"
viewBox
=
"0 0 16 14"
fill
=
"none"
xmlns
=
"http://www.w3.org/2000/svg"
>
<
rect
x
=
"0"
y
=
"0"
width
=
"16"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
<
rect
x
=
"3"
y
=
"4"
width
=
"10"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
<
rect
x
=
"0"
y
=
"8"
width
=
"16"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
<
rect
x
=
"3"
y
=
"12"
width
=
"10"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
</
svg
>
)
}
{
a
===
'
right
'
&&
(
<
svg
width
=
"16"
height
=
"14"
viewBox
=
"0 0 16 14"
fill
=
"none"
xmlns
=
"http://www.w3.org/2000/svg"
>
<
rect
x
=
"0"
y
=
"0"
width
=
"16"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
<
rect
x
=
"6"
y
=
"4"
width
=
"10"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
<
rect
x
=
"0"
y
=
"8"
width
=
"16"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
<
rect
x
=
"6"
y
=
"12"
width
=
"10"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
</
svg
>
)
}
</
button
>
</
button
>
))
}
))
}
</
div
>
{
node
.
attrs
.
align
!==
'
center
'
&&
(
)
}
<>
<
div
style
=
{
{
width
:
1
,
background
:
'
#d9d9d9
'
,
alignSelf
:
'
stretch
'
,
margin
:
'
0 2px
'
}
}
/>
<
button
<
button
type
=
"button"
type
=
"button"
title
=
{
node
.
attrs
.
wrap
?
'
Обтекание включено
'
:
'
Обтекание выключено
'
}
onClick
=
{
(
e
)
=>
{
onClick
=
{
(
e
)
=>
{
e
.
stopPropagation
();
e
.
stopPropagation
();
setShowAlignMenu
(
!
showAlignMenu
);
safeUpdateAttributes
({
wrap
:
!
node
.
attrs
.
wrap
});
requestAnimationFrame
(()
=>
{
try
{
const
pos
=
getPos
?.();
if
(
typeof
pos
===
'
number
'
)
editor
.
commands
.
setNodeSelection
(
pos
);
}
catch
{}
});
}
}
}
}
style
=
{
{
style
=
{
{
position
:
'
absolute
'
,
padding
:
'
4px 6px
'
,
top
:
-
30
,
background
:
node
.
attrs
.
wrap
?
'
#e6f7ff
'
:
'
transparent
'
,
left
:
'
calc(50% - 6px)
'
,
border
:
`1px solid
${
node
.
attrs
.
wrap
?
BORDER_COLOR
:
'
#d9d9d9
'
}
`
,
transform
:
'
translateX(-50%)
'
,
borderRadius
:
2
,
cursor
:
'
pointer
'
,
fontSize
:
11
,
backgroundColor
:
'
white
'
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
gap
:
3
,
border
:
`1px solid
${
BORDER_COLOR
}
`
,
borderRadius
:
4
,
padding
:
'
8px 8px
'
,
cursor
:
'
pointer
'
,
fontSize
:
12
,
zIndex
:
10
}
}
}
}
>
>
Align
<
svg
width
=
"16"
height
=
"14"
viewBox
=
"0 0 16 14"
fill
=
"none"
xmlns
=
"http://www.w3.org/2000/svg"
>
<
rect
x
=
"0"
y
=
"0"
width
=
"7"
height
=
"7"
rx
=
"1"
fill
=
"currentColor"
opacity
=
"0.5"
/>
<
rect
x
=
"9"
y
=
"0"
width
=
"7"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
<
rect
x
=
"9"
y
=
"4"
width
=
"5"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
<
rect
x
=
"0"
y
=
"9"
width
=
"16"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
<
rect
x
=
"0"
y
=
"12"
width
=
"12"
height
=
"2"
rx
=
"1"
fill
=
"currentColor"
/>
</
svg
>
Обтекание
</
button
>
</
button
>
</>
)
}
</
div
>
</
Fragment
>
</
Fragment
>
)
}
)
}
<
Modal
<
Modal
title
=
"Текст на картинке"
title
=
"Текст на картинке"
open
=
{
altModalVisible
}
open
=
{
altModalVisible
}
onOk
=
{
()
=>
{
onOk
=
{
()
=>
{
updateAttributes
({
alt
:
tempAlt
,
frontAlt
:
tempFrontAlt
});
setAltModalVisible
(
false
);
}
}
updateAttributes
({
alt
:
tempAlt
,
frontAlt
:
tempFrontAlt
});
setAltModalVisible
(
false
);
}
}
onCancel
=
{
()
=>
setAltModalVisible
(
false
)
}
onCancel
=
{
()
=>
setAltModalVisible
(
false
)
}
okText
=
"Применить"
okText
=
"Применить"
cancelText
=
"Отмена"
cancelText
=
"Отмена"
>
>
<
div
style
=
{
{
marginBottom
:
'
5px
'
}
}
><
Text
>
Лицевая сторона
</
Text
></
div
>
<
div
style
=
{
{
marginBottom
:
'
5px
'
}
}
><
Text
>
Лицевая сторона
</
Text
></
div
>
<
TextArea
<
TextArea
value
=
{
tempFrontAlt
}
onChange
=
{
(
e
)
=>
setTempFrontAlt
(
e
.
target
.
value
)
}
rows
=
{
4
}
placeholder
=
"Введите текст"
/>
value
=
{
tempFrontAlt
}
onChange
=
{
(
e
)
=>
setTempFrontAlt
(
e
.
target
.
value
)
}
rows
=
{
4
}
placeholder
=
"Введите текст"
/>
<
div
style
=
{
{
marginTop
:
'
15px
'
,
marginBottom
:
'
5px
'
}
}
><
Text
>
Обратная сторона
</
Text
></
div
>
<
div
style
=
{
{
marginTop
:
'
15px
'
,
marginBottom
:
'
5px
'
}
}
><
Text
>
Обратная сторона
</
Text
></
div
>
<
TextArea
<
TextArea
value
=
{
tempAlt
}
onChange
=
{
(
e
)
=>
setTempAlt
(
e
.
target
.
value
)
}
rows
=
{
4
}
placeholder
=
"Введите текст"
/>
value
=
{
tempAlt
}
onChange
=
{
(
e
)
=>
setTempAlt
(
e
.
target
.
value
)
}
rows
=
{
4
}
placeholder
=
"Введите текст"
/>
</
Modal
>
</
Modal
>
</>
);
// Единая структура NodeViewWrapper > div > content — img всегда на одной глубине,
// поэтому при смене выравнивания React не размонтирует img и не перезагружает его.
return
(
<
NodeViewWrapper
as
=
"div"
style
=
{
getOuterStyle
()
}
contentEditable
=
{
false
}
data
-
image
-
wrapper
>
<
div
ref
=
{
wrapperRef
}
style
=
{
getInnerStyle
()
}
onClick
=
{
handleNodeClick
}
>
{
imageContent
}
</
div
>
</
NodeViewWrapper
>
</
NodeViewWrapper
>
);
);
};
};
...
@@ -704,6 +691,11 @@ const ResizableImageExtension = TipTapImage.extend({
...
@@ -704,6 +691,11 @@ const ResizableImageExtension = TipTapImage.extend({
parseHTML
:
element
=>
element
.
getAttribute
(
'
data-align
'
)
||
'
left
'
,
parseHTML
:
element
=>
element
.
getAttribute
(
'
data-align
'
)
||
'
left
'
,
renderHTML
:
attributes
=>
({
'
data-align
'
:
attributes
.
align
})
renderHTML
:
attributes
=>
({
'
data-align
'
:
attributes
.
align
})
},
},
wrap
:
{
default
:
false
,
parseHTML
:
element
=>
element
.
getAttribute
(
'
data-wrap
'
)
===
'
true
'
,
renderHTML
:
attributes
=>
attributes
.
wrap
?
{
'
data-wrap
'
:
'
true
'
}
:
{}
},
'
data-node-id
'
:
{
'
data-node-id
'
:
{
default
:
null
,
default
:
null
,
parseHTML
:
element
=>
element
.
getAttribute
(
'
data-node-id
'
),
parseHTML
:
element
=>
element
.
getAttribute
(
'
data-node-id
'
),
...
@@ -722,18 +714,20 @@ const ResizableImageExtension = TipTapImage.extend({
...
@@ -722,18 +714,20 @@ const ResizableImageExtension = TipTapImage.extend({
}
=
HTMLAttributes
;
}
=
HTMLAttributes
;
const
align
=
node
.
attrs
.
align
||
'
left
'
;
const
align
=
node
.
attrs
.
align
||
'
left
'
;
const
wrap
=
node
.
attrs
.
wrap
||
false
;
const
style
=
[];
const
style
=
[];
if
(
align
===
'
center
'
)
{
if
(
align
===
'
center
'
)
{
style
.
push
(
'
display: block
'
,
'
margin-left: auto
'
,
'
margin-right: auto
'
);
style
.
push
(
'
display: block
'
,
'
margin-left: auto
'
,
'
margin-right: auto
'
);
}
else
if
(
align
===
'
left
'
)
{
}
else
if
(
align
===
'
left
'
)
{
style
.
push
(
'
float: left
'
,
'
margin-right: 1rem
'
);
wrap
?
style
.
push
(
'
float: left
'
,
'
margin-right: 1rem
'
)
:
style
.
push
(
'
display: block
'
,
'
margin-right: auto
'
);
}
else
if
(
align
===
'
right
'
)
{
}
else
if
(
align
===
'
right
'
)
{
style
.
push
(
'
float: right
'
,
'
margin-left: 1rem
'
);
wrap
}
else
if
(
align
===
'
text
'
)
{
?
style
.
push
(
'
float: right
'
,
'
margin-left: 1rem
'
)
style
.
push
(
'
display:
inline-
block
'
,
'
vertical-align: middle
'
,
'
margin: 0 0.2rem
'
);
:
style
.
push
(
'
display: block
'
,
'
margin-left: auto
'
);
}
}
if
(
width
)
style
.
push
(
`width:
${
width
}
px`
);
if
(
width
)
style
.
push
(
`width:
${
width
}
px`
);
...
...
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