edit text

This commit is contained in:
hamid zarghami
2026-01-06 14:50:48 +03:30
parent 2890603c29
commit 4f2f49a12f
+128 -2
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef, useMemo } from "react";
import { useEffect, useRef, useMemo, useState } from "react";
import { Text, Group } from "react-konva";
import Konva from "konva";
import type { TextShapeProps } from "./types";
@@ -41,6 +41,7 @@ const TextShape = ({
}: TextShapeProps) => {
const shapeRef = useRef<Konva.Text>(null);
const groupRef = useRef<Konva.Group>(null);
const [isEditing, setIsEditing] = useState(false);
// Transformer is managed in EditorCanvas for multi-select support
// Set offset for text node
@@ -73,6 +74,130 @@ const TextShape = ({
const fontWeight = useMemo(() => getFontWeight(obj.fontWeight), [obj.fontWeight]);
const fillColor = useMemo(() => getColorWithOpacity(obj.fill, obj.opacity), [obj.fill, obj.opacity]);
const handleDblClick = () => {
if (!shapeRef.current || !groupRef.current) return;
setIsEditing(true);
const textNode = shapeRef.current;
const groupNode = groupRef.current;
const stage = textNode.getStage();
if (!stage) return;
// مخفی کردن متن اصلی در حین ویرایش
textNode.hide();
textNode.getLayer()?.batchDraw();
// گرفتن موقعیت مطلق text node روی صفحه با استفاده از getClientRect
// که همه transformها (scale, rotation, position) را در نظر می‌گیرد
const stageBox = stage.container().getBoundingClientRect();
const box = textNode.getClientRect();
// محاسبه موقعیت textarea روی صفحه
const areaPosition = {
x: stageBox.left + box.x,
y: stageBox.top + box.y,
};
// ایجاد textarea
const textarea = document.createElement("textarea");
document.body.appendChild(textarea);
// تنظیم مقدار
textarea.value = textNode.text();
// استایل‌دهی دقیق
const stageScale = stage.scaleX();
Object.assign(textarea.style, {
position: "absolute",
top: `${areaPosition.y}px`,
left: `${areaPosition.x}px`,
width: `${box.width}px`,
minHeight: `${box.height}px`,
fontSize: `${(obj.fontSize || 24) * stageScale}px`,
fontFamily: fontFamily,
fontWeight: fontWeight,
color: obj.fill || "#000000",
letterSpacing: `${(obj.letterSpacing || 0) * stageScale}px`,
lineHeight: textNode.lineHeight().toString(),
textAlign: "right",
direction: "rtl",
padding: "0",
margin: "0",
border: "none",
outline: "2px solid #3b82f6",
background: "transparent",
resize: "none",
overflow: "auto",
zIndex: "10000",
transformOrigin: "left top",
whiteSpace: "pre-wrap",
wordWrap: "break-word",
});
// اعمال rotation
const rotation = groupNode.rotation();
if (rotation) {
textarea.style.transform = `rotate(${rotation}deg)`;
// تنظیم transform origin برای rotation صحیح
textarea.style.transformOrigin = "left top";
}
// Focus و Select
textarea.focus();
// Select all text after a small delay
setTimeout(() => {
textarea.select();
}, 0);
const removeTextarea = () => {
if (textarea.parentNode) {
textarea.parentNode.removeChild(textarea);
}
window.removeEventListener("click", handleOutsideClick);
setIsEditing(false);
textNode.show();
textNode.getLayer()?.batchDraw();
};
const saveText = () => {
const newText = textarea.value;
if (newText !== textNode.text()) {
onUpdate(obj.id, { text: newText });
}
removeTextarea();
};
const handleOutsideClick = (e: MouseEvent) => {
if (e.target !== textarea) {
saveText();
}
};
textarea.addEventListener("keydown", (e) => {
// فقط با Escape خارج بشیم بدون ذخیره
if (e.key === "Escape") {
removeTextarea();
e.preventDefault();
}
// Enter عادی برای خط جدید
// Ctrl+Enter یا Cmd+Enter برای ذخیره و خروج
else if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
saveText();
e.preventDefault();
}
});
textarea.addEventListener("blur", () => {
saveText();
});
setTimeout(() => {
window.addEventListener("click", handleOutsideClick);
}, 10);
};
return (
<Group
ref={groupRef}
@@ -83,12 +208,13 @@ const TextShape = ({
width={obj.width}
height={obj.height}
rotation={obj.rotation || 0}
draggable={draggable}
draggable={draggable && !isEditing}
onClick={(e) => {
if (groupRef.current) {
onSelect(obj.id, groupRef.current, e);
}
}}
onDblClick={handleDblClick}
onDragEnd={(e) => {
const node = e.target;
onUpdate(obj.id, {