Commit 9ed36052 by Яков

выравнивание картинки, обтекание текста, ресайз

parent 2b0a2301
{ {
"name": "react-ag-qeditor", "name": "react-ag-qeditor",
"version": "1.0.85", "version": "1.0.86",
"description": "WYSIWYG html editor", "description": "WYSIWYG html editor",
"author": "atma", "author": "atma",
"license": "MIT", "license": "MIT",
......
...@@ -20,6 +20,7 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => { ...@@ -20,6 +20,7 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => {
aspectRatio: 1 aspectRatio: 1
}); });
// Добавляем прозрачный нулевой пробел после изображения
useEffect(() => { useEffect(() => {
if (!editor || !getPos) return; if (!editor || !getPos) return;
...@@ -29,21 +30,16 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => { ...@@ -29,21 +30,16 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => {
if (doc.nodeSize > pos && doc.nodeAt(pos)?.textContent !== '\u200B') { if (doc.nodeSize > pos && doc.nodeAt(pos)?.textContent !== '\u200B') {
editor.commands.insertContentAt(pos, { editor.commands.insertContentAt(pos, {
type: 'text', type: 'text',
text: '\u200B' text: '\u200B' // Невидимый нулевой пробел
}); });
} }
}, [editor, getPos]); }, [editor, getPos]);
// Инициализация размеров
useEffect(() => { useEffect(() => {
if (imgRef.current && !isInitialized.current) { if (imgRef.current && !isInitialized.current) {
if (node.attrs.width && node.attrs.height) { const width = node.attrs.width || imgRef.current.naturalWidth;
isInitialized.current = true; const height = node.attrs.height || imgRef.current.naturalHeight;
return;
}
const width = imgRef.current.naturalWidth;
const height = imgRef.current.naturalHeight;
updateAttributes({ updateAttributes({
width: Math.round(width), width: Math.round(width),
height: Math.round(height) height: Math.round(height)
...@@ -56,8 +52,8 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => { ...@@ -56,8 +52,8 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
const currentWidth = node.attrs.width; const currentWidth = node.attrs.width || imgRef.current.naturalWidth;
const currentHeight = node.attrs.height; const currentHeight = node.attrs.height || imgRef.current.naturalHeight;
resizeData.current = { resizeData.current = {
startWidth: currentWidth, startWidth: currentWidth,
...@@ -77,16 +73,20 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => { ...@@ -77,16 +73,20 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => {
let newWidth, newHeight; let newWidth, newHeight;
if (node.attrs.align === 'center') { if (node.attrs.align === 'center') {
// Особый случай для центрированного изображения
if (direction.includes('n') || direction.includes('s')) { if (direction.includes('n') || direction.includes('s')) {
// Только вертикальный ресайз с сохранением пропорций
const scale = direction.includes('s') ? 1 : -1; const scale = direction.includes('s') ? 1 : -1;
newHeight = Math.max(startHeight + deltaY * scale, MIN_WIDTH); newHeight = Math.max(startHeight + deltaY * scale, MIN_WIDTH);
newWidth = Math.round(newHeight * aspectRatio); newWidth = Math.round(newHeight * aspectRatio);
} else { } else {
// Горизонтальный ресайз с сохранением пропорций
const scale = direction.includes('e') ? 1 : -1; const scale = direction.includes('e') ? 1 : -1;
newWidth = Math.max(startWidth + deltaX * scale, MIN_WIDTH); newWidth = Math.max(startWidth + deltaX * scale, MIN_WIDTH);
newHeight = Math.round(newWidth / aspectRatio); newHeight = Math.round(newWidth / aspectRatio);
} }
} else { } else {
// Обычный ресайз для других выравниваний
if (direction.includes('e') || direction.includes('w')) { if (direction.includes('e') || direction.includes('w')) {
const scale = direction.includes('e') ? 1 : -1; const scale = direction.includes('e') ? 1 : -1;
newWidth = Math.max(startWidth + deltaX * scale, MIN_WIDTH); newWidth = Math.max(startWidth + deltaX * scale, MIN_WIDTH);
...@@ -123,6 +123,8 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => { ...@@ -123,6 +123,8 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => {
const getWrapperStyle = () => { const getWrapperStyle = () => {
const baseStyle = { const baseStyle = {
display: 'inline-block', display: 'inline-block',
lineHeight: 0,
margin: '0.5rem 0',
position: 'relative', position: 'relative',
outline: editing ? `1px dashed ${BORDER_COLOR}` : 'none', outline: editing ? `1px dashed ${BORDER_COLOR}` : 'none',
verticalAlign: 'top', verticalAlign: 'top',
...@@ -131,33 +133,39 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => { ...@@ -131,33 +133,39 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => {
switch(node.attrs.align) { switch(node.attrs.align) {
case 'left': case 'left':
return { return { ...baseStyle, float: 'left', marginRight: '1rem' };
...baseStyle,
float: 'left',
margin: '0 1rem 1rem 0'
};
case 'right': case 'right':
return { return { ...baseStyle, float: 'right', marginLeft: '1rem' };
...baseStyle,
float: 'right',
margin: '0 0 1rem 1rem'
};
case 'center': case 'center':
return { return {
...baseStyle, ...baseStyle,
display: 'block', display: 'block',
margin: '0 auto', marginLeft: 'auto',
float: 'none', marginRight: 'auto',
textAlign: 'center' textAlign: 'center'
}; };
case 'wrap-left':
return { ...baseStyle, float: 'left', margin: '0 1rem 1rem 0', shapeOutside: 'margin-box' };
case 'wrap-right':
return { ...baseStyle, float: 'right', margin: '0 0 1rem 1rem', shapeOutside: 'margin-box' };
default: default:
return baseStyle; return baseStyle;
} }
}; };
const getImageStyle = () => ({
width: node.attrs.width ? `${node.attrs.width}px` : 'auto',
height: node.attrs.height ? `${node.attrs.height}px` : 'auto',
maxWidth: '100%',
display: 'block',
cursor: 'default',
userSelect: 'none',
margin: node.attrs.align === 'center' ? '0 auto' : '0'
});
return ( return (
<NodeViewWrapper <NodeViewWrapper
as="span" as="div"
style={getWrapperStyle()} style={getWrapperStyle()}
ref={wrapperRef} ref={wrapperRef}
onClick={(e) => { onClick={(e) => {
...@@ -170,18 +178,9 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => { ...@@ -170,18 +178,9 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos }) => {
<img <img
{...node.attrs} {...node.attrs}
ref={imgRef} ref={imgRef}
style={{ style={getImageStyle()}
width: `${node.attrs.width}px`,
height: `${node.attrs.height}px`,
maxWidth: '100%',
display: 'block',
margin: '0 auto', // Центрируем изображение внутри wrapper'а
cursor: 'default',
userSelect: 'none'
}}
onLoad={() => { onLoad={() => {
if (imgRef.current && !isInitialized.current && if (imgRef.current && !isInitialized.current) {
(!node.attrs.width || !node.attrs.height)) {
const width = imgRef.current.naturalWidth; const width = imgRef.current.naturalWidth;
const height = imgRef.current.naturalHeight; const height = imgRef.current.naturalHeight;
updateAttributes({ updateAttributes({
...@@ -313,26 +312,27 @@ const ResizableImageExtension = TipTapImage.extend({ ...@@ -313,26 +312,27 @@ const ResizableImageExtension = TipTapImage.extend({
renderHTML({ HTMLAttributes }) { renderHTML({ HTMLAttributes }) {
const align = HTMLAttributes.align || 'left'; const align = HTMLAttributes.align || 'left';
const isWrap = ['wrap-left', 'wrap-right'].includes(align);
const floatDirection = isWrap ? align.split('-')[1] : ['left', 'right'].includes(align) ? align : 'none';
return ['span', { return ['span', {
'data-type': 'resizable-image', 'data-type': 'resizable-image',
'data-image-wrapper': true, 'data-image-wrapper': true,
style: ` style: `
display: ${align === 'center' ? 'block' : 'inline-block'}; display: ${align === 'center' ? 'block' : 'inline-block'};
float: ${['left', 'right'].includes(align) ? align : 'none'}; float: ${floatDirection};
margin: ${align === 'left' ? '0 1rem 1rem 0' : margin: ${align === 'left' ? '0 1rem 1rem 0' :
align === 'right' ? '0 0 1rem 1rem' : align === 'right' ? '0 0 1rem 1rem' :
align === 'center' ? '0 auto' : '0'}; align === 'wrap-left' ? '0 1rem 1rem 0' :
text-align: ${align === 'center' ? 'center' : 'left'}; align === 'wrap-right' ? '0 0 1rem 1rem' :
align === 'center' ? '0.5rem auto' : '0'};
shape-outside: ${isWrap ? 'margin-box' : 'none'};
vertical-align: top; vertical-align: top;
position: relative; position: relative;
z-index: 1; z-index: 1;
`, `,
'data-align': align 'data-align': align
}, ['img', { }, ['img', HTMLAttributes]];
...HTMLAttributes,
style: `width:${HTMLAttributes.width}px;height:${HTMLAttributes.height}px;max-width:100%;display:block;margin:0 auto;`
}]];
}, },
addNodeView() { addNodeView() {
...@@ -342,7 +342,7 @@ const ResizableImageExtension = TipTapImage.extend({ ...@@ -342,7 +342,7 @@ const ResizableImageExtension = TipTapImage.extend({
inline: true, inline: true,
group: 'inline', group: 'inline',
draggable: true, draggable: true,
selectable: false selectable: false // Важно отключить выделение изображения
}); });
export default ResizableImageExtension; export default ResizableImageExtension;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment