Commit bbaa6d76 by Яков

fix

parent d232d707
{ {
"name": "react-ag-qeditor", "name": "react-ag-qeditor",
"version": "1.0.97", "version": "1.0.98",
"description": "WYSIWYG html editor", "description": "WYSIWYG html editor",
"author": "atma", "author": "atma",
"license": "MIT", "license": "MIT",
......
...@@ -13,21 +13,78 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select ...@@ -13,21 +13,78 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
const isInitialized = useRef(false); const isInitialized = useRef(false);
const [isResizing, setIsResizing] = useState(false); const [isResizing, setIsResizing] = useState(false);
// Получаем ширину редактора для масштабирования изображений // Получаем текущую ширину редактора и доступное пространство
const getEditorWidth = () => { const getEditorDimensions = () => {
const editorElement = editor?.options?.element?.closest('.atma-editor-content'); const editorElement = editor?.options?.element?.closest('.atma-editor-content');
return editorElement ? editorElement.clientWidth : null; if (!editorElement) return { width: Infinity, availableSpace: Infinity };
const editorWidth = editorElement.clientWidth;
const imgElement = imgRef.current;
let availableSpace = editorWidth;
if (imgElement) {
const imgRect = imgElement.getBoundingClientRect();
const editorRect = editorElement.getBoundingClientRect();
if (node.attrs.align === 'center') {
const leftSpace = imgRect.left - editorRect.left;
const rightSpace = editorRect.right - imgRect.right;
availableSpace = Math.min(editorWidth, (leftSpace + rightSpace + imgRect.width));
console.log(leftSpace, rightSpace, availableSpace);
} else if (node.attrs.align === 'right') {
availableSpace = imgRect.left - editorRect.left + node.attrs.width;
} else if (node.attrs.align === 'left' || node.attrs.align === 'text') {
availableSpace = editorRect.right - imgRect.left;
}
}
return { width: editorWidth, availableSpace };
};
// Безопасное обновление атрибутов с учетом выравнивания и границ
const safeUpdateAttributes = (newAttrs) => {
const { width: editorWidth, availableSpace } = getEditorDimensions();
let { width, height, align } = { ...node.attrs, ...newAttrs };
const newAlign = newAttrs.align || align;
// При изменении выравнивания проверяем доступное пространство
if (newAlign && newAlign !== align) {
const maxWidth = availableSpace;
if (width > maxWidth) {
const ratio = maxWidth / width;
width = maxWidth;
height = Math.round(height * ratio);
}
} else {
// Для обычного обновления размеров
const maxWidth = availableSpace;
if (width > maxWidth) {
const ratio = maxWidth / width;
width = maxWidth;
height = Math.round(height * ratio);
}
}
// Проверяем минимальный размер
if (width < MIN_WIDTH) {
const ratio = MIN_WIDTH / width;
width = MIN_WIDTH;
height = Math.round(height * ratio);
}
updateAttributes({ width, height, ...newAttrs });
}; };
// Генерация уникального ID при создании // Инициализация изображения
useEffect(() => { useEffect(() => {
if (!node.attrs['data-node-id']) { if (!node.attrs['data-node-id']) {
updateAttributes({ safeUpdateAttributes({
'data-node-id': `img-${Date.now()}-${Math.random().toString(36).substr(2, 9)}` 'data-node-id': `img-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
}); });
} }
}, [node.attrs['data-node-id'], updateAttributes]); }, [node.attrs['data-node-id']]);
// Обработка кликов вне изображения
useEffect(() => { useEffect(() => {
const handleClickOutside = (event) => { const handleClickOutside = (event) => {
if (wrapperRef.current && !wrapperRef.current.contains(event.target) && selected) { if (wrapperRef.current && !wrapperRef.current.contains(event.target) && selected) {
...@@ -38,40 +95,22 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select ...@@ -38,40 +95,22 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
return () => document.removeEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside);
}, [selected, editor, getPos]); }, [selected, editor, getPos]);
// Загрузка и инициализация изображения
useEffect(() => { useEffect(() => {
if (!imgRef.current || isInitialized.current) return; if (!imgRef.current || isInitialized.current) return;
const initImageSize = () => { const initImageSize = () => {
try { try {
const editorWidth = getEditorWidth(); const { width: editorWidth } = getEditorDimensions();
const naturalWidth = imgRef.current.naturalWidth; const naturalWidth = imgRef.current.naturalWidth;
const naturalHeight = imgRef.current.naturalHeight; const naturalHeight = imgRef.current.naturalHeight;
let width = node.attrs.width || naturalWidth; safeUpdateAttributes({
let height = node.attrs.height || naturalHeight; width: naturalWidth,
height: naturalHeight,
// Масштабируем изображение, если оно шире редактора 'data-node-id': node.attrs['data-node-id'] || Math.random().toString(36).substr(2, 9)
if (editorWidth && width > editorWidth) { });
const ratio = editorWidth / width; isInitialized.current = true;
width = editorWidth;
height = Math.round(height * ratio);
}
// Проверяем минимальный размер
if (width < MIN_WIDTH) {
const ratio = MIN_WIDTH / width;
width = MIN_WIDTH;
height = Math.round(height * ratio);
}
if (width > 0 && height > 0) {
updateAttributes({
width: Math.round(width),
height: Math.round(height),
'data-node-id': node.attrs['data-node-id'] || Math.random().toString(36).substr(2, 9)
});
isInitialized.current = true;
}
} catch (error) { } catch (error) {
console.warn('Error initializing image size:', error); console.warn('Error initializing image size:', error);
} }
...@@ -86,8 +125,9 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select ...@@ -86,8 +125,9 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
return () => { return () => {
if (imgRef.current) imgRef.current.onload = null; if (imgRef.current) imgRef.current.onload = null;
}; };
}, [node.attrs.width, node.attrs.height, updateAttributes, node.attrs['data-node-id']]); }, [node.attrs.width, node.attrs.height, node.attrs['data-node-id']]);
// Обработка ресайза изображения
const handleResizeStart = (direction) => (e) => { const handleResizeStart = (direction) => (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
...@@ -100,36 +140,49 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select ...@@ -100,36 +140,49 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
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: editorWidth, availableSpace } = getEditorDimensions();
const onMouseMove = (e) => { const onMouseMove = (e) => {
const deltaX = e.clientX - startX; const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY; const deltaY = e.clientY - startY;
let newWidth, newHeight; let newWidth, newHeight;
const maxWidth = node.attrs.align === 'center' ? editorWidth : availableSpace;
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.min(Math.round(newHeight * aspectRatio), maxWidth);
newHeight = Math.round(newWidth / 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.min(
Math.max(startWidth + deltaX * scale, MIN_WIDTH),
maxWidth
);
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.min(
Math.max(startWidth + deltaX * scale, MIN_WIDTH),
maxWidth
);
newHeight = Math.round(newWidth / aspectRatio); newHeight = Math.round(newWidth / aspectRatio);
} else { } else {
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.min(
Math.round(newHeight * aspectRatio),
maxWidth
);
newHeight = Math.round(newWidth / aspectRatio);
} }
} }
updateAttributes({ width: newWidth, height: newHeight }); safeUpdateAttributes({ width: newWidth, height: newHeight });
}; };
const onMouseUp = () => { const onMouseUp = () => {
...@@ -144,12 +197,15 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select ...@@ -144,12 +197,15 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
window.addEventListener('mouseup', onMouseUp); window.addEventListener('mouseup', onMouseUp);
}; };
// Изменение выравнивания с автоматическим масштабированием
const handleAlign = (align) => { const handleAlign = (align) => {
updateAttributes({ align }); safeUpdateAttributes({ align });
safeUpdateAttributes({ align });
setShowAlignMenu(false); setShowAlignMenu(false);
editor.commands.focus(); editor.commands.focus();
}; };
// Стили для обертки изображения
const getWrapperStyle = () => { const getWrapperStyle = () => {
const baseStyle = { const baseStyle = {
display: 'inline-block', display: 'inline-block',
...@@ -160,7 +216,6 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select ...@@ -160,7 +216,6 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
margin: '0.5rem 0', margin: '0.5rem 0',
}; };
// Для выравнивания по центру
if (node.attrs.align === 'center') { if (node.attrs.align === 'center') {
return { return {
...baseStyle, ...baseStyle,
...@@ -173,7 +228,6 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select ...@@ -173,7 +228,6 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
}; };
} }
// Для других вариантов выравнивания
return { return {
...baseStyle, ...baseStyle,
...(node.attrs.align === 'left' && { ...(node.attrs.align === 'left' && {
...@@ -196,16 +250,17 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select ...@@ -196,16 +250,17 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
}; };
}; };
// Стили для самого изображения
const getImageStyle = () => ({ const getImageStyle = () => ({
width: node.attrs.width ? `${node.attrs.width}px` : 'auto', width: node.attrs.width ? `${node.attrs.width}px` : 'auto',
height: 'auto', // Автоматическая высота для сохранения пропорций height: 'auto',
maxWidth: '100%', maxWidth: '100%',
display: 'block', display: 'block',
cursor: 'default', cursor: 'default',
userSelect: 'none', userSelect: 'none',
margin: node.attrs.align === 'center' ? '0 auto' : '0', margin: node.attrs.align === 'center' ? '0 auto' : '0',
verticalAlign: node.attrs.align === 'text' ? 'middle' : 'top', verticalAlign: node.attrs.align === 'text' ? 'middle' : 'top',
objectFit: 'contain' // Сохраняем пропорции изображения objectFit: 'contain'
}); });
return ( return (
...@@ -227,30 +282,13 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select ...@@ -227,30 +282,13 @@ const ResizableImageTemplate = ({ node, updateAttributes, editor, getPos, select
style={getImageStyle()} style={getImageStyle()}
onLoad={() => { onLoad={() => {
if (imgRef.current && !isInitialized.current) { if (imgRef.current && !isInitialized.current) {
const editorWidth = getEditorWidth(); const { width: editorWidth } = getEditorDimensions();
const naturalWidth = imgRef.current.naturalWidth; const naturalWidth = imgRef.current.naturalWidth;
const naturalHeight = imgRef.current.naturalHeight; const naturalHeight = imgRef.current.naturalHeight;
let width = naturalWidth; safeUpdateAttributes({
let height = naturalHeight; width: naturalWidth,
height: naturalHeight,
// Масштабируем изображение, если оно шире редактора
if (editorWidth && width > editorWidth) {
const ratio = editorWidth / width;
width = editorWidth;
height = Math.round(height * ratio);
}
// Проверяем минимальный размер
if (width < MIN_WIDTH) {
const ratio = MIN_WIDTH / width;
width = MIN_WIDTH;
height = Math.round(height * ratio);
}
updateAttributes({
width: Math.round(width),
height: Math.round(height),
'data-node-id': node.attrs['data-node-id'] || Math.random().toString(36).substr(2, 9) 'data-node-id': node.attrs['data-node-id'] || Math.random().toString(36).substr(2, 9)
}); });
isInitialized.current = true; isInitialized.current = true;
......
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