can edit text + fix font weight - remove character space

This commit is contained in:
hamid zarghami
2025-11-26 15:45:00 +03:30
parent aacf209894
commit 735355822a
19 changed files with 342 additions and 160 deletions
@@ -28,7 +28,7 @@ const AbstractShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, dr
outerRadius={radius}
fill={obj.fill}
stroke={isSelected ? "#3b82f6" : obj.stroke}
strokeWidth={isSelected ? 3 : obj.strokeWidth || 2}
strokeWidth={isSelected ? 3 : (obj.strokeWidth ?? 0)}
rotation={obj.rotation || 0}
draggable={draggable}
onClick={() => {
@@ -20,7 +20,7 @@ const ArrowShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, dragg
y={obj.y}
points={[0, 0, (obj.width || 0) - obj.x, (obj.height || 0) - obj.y]}
stroke={isSelected ? "#3b82f6" : obj.stroke || "#000000"}
strokeWidth={isSelected ? 3 : obj.strokeWidth || 2}
strokeWidth={isSelected ? 3 : (obj.strokeWidth ?? 0)}
fill={isSelected ? "#3b82f6" : obj.stroke || "#000000"}
rotation={obj.rotation || 0}
draggable={draggable}
@@ -21,7 +21,7 @@ const CircleShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, drag
radius={(obj.width || 50) / 2}
fill={obj.fill}
stroke={isSelected ? "#3b82f6" : obj.stroke}
strokeWidth={isSelected ? 3 : obj.strokeWidth || 2}
strokeWidth={isSelected ? 3 : (obj.strokeWidth ?? 0)}
rotation={obj.rotation || 0}
draggable={draggable}
onClick={() => {
@@ -20,7 +20,7 @@ const LineShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, dragga
y={obj.y}
points={[0, 0, (obj.width || 0) - obj.x, (obj.height || 0) - obj.y]}
stroke={isSelected ? "#3b82f6" : obj.stroke || "#000000"}
strokeWidth={isSelected ? 3 : obj.strokeWidth || 2}
strokeWidth={isSelected ? 3 : (obj.strokeWidth ?? 0)}
rotation={obj.rotation || 0}
draggable={draggable}
onClick={() => {
@@ -22,7 +22,7 @@ const RectangleShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, d
height={obj.height || 100}
fill={obj.fill}
stroke={isSelected ? "#3b82f6" : obj.stroke}
strokeWidth={isSelected ? 3 : obj.strokeWidth || 2}
strokeWidth={isSelected ? 3 : (obj.strokeWidth ?? 0)}
rotation={obj.rotation || 0}
draggable={draggable}
onClick={() => {
+53 -32
View File
@@ -4,33 +4,33 @@ import Konva from "konva";
import type { TextShapeProps } from "./types";
const getFontFamily = (fontFamily?: string): string => {
const fontMap: Record<string, string> = {
"1": "irancell",
"2": "irancell",
};
return fontMap[fontFamily || "1"] || "irancell";
const fontMap: Record<string, string> = {
"1": "irancell",
"2": "irancell",
};
return fontMap[fontFamily || "1"] || "irancell";
};
const getFontWeight = (fontWeight?: string): string | number => {
const weightMap: Record<string, string | number> = {
bold: "600",
normal: "300",
"300": "300",
};
return weightMap[fontWeight || "normal"] || "300";
const getFontWeight = (fontWeight?: string): string => {
const weightMap: Record<string, string> = {
bold: "bold",
normal: "normal",
"300": "300",
};
return weightMap[fontWeight || "normal"] || "normal";
};
const getColorWithOpacity = (fill?: string, opacity?: number): string => {
if (!fill) return "#000000";
if (opacity === undefined || opacity === 100) return fill;
if (!fill) return "#000000";
if (opacity === undefined || opacity === 100) return fill;
const hex = fill.replace("#", "");
const r = parseInt(hex.substring(0, 2), 16);
const g = parseInt(hex.substring(2, 4), 16);
const b = parseInt(hex.substring(4, 6), 16);
const alpha = opacity / 100;
const hex = fill.replace("#", "");
const r = parseInt(hex.substring(0, 2), 16);
const g = parseInt(hex.substring(2, 4), 16);
const b = parseInt(hex.substring(4, 6), 16);
const alpha = opacity / 100;
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
};
const TextShape = ({
@@ -46,10 +46,23 @@ const TextShape = ({
useEffect(() => {
if (isSelected && shapeRef.current && transformerRef.current) {
transformerRef.current.nodes([shapeRef.current]);
transformerRef.current.getLayer()?.batchDraw();
const textNode = shapeRef.current;
// تنظیم offset برای حذف فضای خالی
textNode.offsetX(0);
textNode.offsetY(0);
transformerRef.current.nodes([textNode]);
// به‌روزرسانی transformer بعد از یک تیک برای محاسبه دقیق bounds
setTimeout(() => {
if (transformerRef.current && shapeRef.current) {
transformerRef.current.forceUpdate();
transformerRef.current.getLayer()?.batchDraw();
}
}, 0);
}
}, [isSelected, transformerRef]);
}, [isSelected, transformerRef, obj.text, obj.width, obj.height, obj.fontSize, obj.fontFamily, obj.fontWeight]);
const fontFamily = useMemo(() => getFontFamily(obj.fontFamily), [obj.fontFamily]);
const fontWeight = useMemo(() => getFontWeight(obj.fontWeight), [obj.fontWeight]);
@@ -64,10 +77,11 @@ const TextShape = ({
fontSize={obj.fontSize || 24}
fill={fillColor}
fontFamily={fontFamily}
fontWeight={fontWeight}
letterSpacing={obj.letterSpacing}
fontStyle={fontWeight}
letterSpacing={obj.letterSpacing ?? 0}
width={obj.width}
height={obj.height}
align="right"
rotation={obj.rotation || 0}
draggable={draggable}
onClick={() => {
@@ -105,35 +119,42 @@ const TextShape = ({
const stage = textNode.getStage();
if (!stage) return;
const stageBox = stage.container().getBoundingClientRect();
const textPosition = textNode.absolutePosition();
const scaleX = stage.scaleX() || 1;
const scaleY = stage.scaleY() || 1;
const areaPosition = {
x: stage.container().offsetLeft + textPosition.x,
y: stage.container().offsetTop + textPosition.y,
x: stageBox.left + textPosition.x * scaleX,
y: stageBox.top + textPosition.y * scaleY,
};
const textarea = document.createElement("textarea");
document.body.appendChild(textarea);
textNode.hide();
layerRef.current?.draw();
textarea.value = textNode.text();
textarea.style.position = "absolute";
textarea.style.top = `${areaPosition.y}px`;
textarea.style.left = `${areaPosition.x}px`;
textarea.style.width = `${textNode.width()}px`;
textarea.style.fontSize = `${textNode.fontSize()}px`;
textarea.style.width = `${textNode.width() * scaleX}px`;
textarea.style.fontSize = `${textNode.fontSize() * scaleY}px`;
textarea.style.border = "none";
textarea.style.padding = "0px";
textarea.style.padding = "50px";
textarea.style.margin = "0px";
textarea.style.overflow = "hidden";
textarea.style.background = "transparent";
textarea.style.outline = "none";
textarea.style.resize = "none";
textarea.style.fontFamily = fontFamily;
textarea.style.fontWeight = String(fontWeight);
textarea.style.letterSpacing = `${obj.letterSpacing || 0}px`;
textarea.style.fontWeight = fontWeight === "bold" ? "bold" : "normal";
textarea.style.letterSpacing = `${obj.letterSpacing ?? 0}px`;
textarea.style.textAlign = textNode.align() || "right";
textarea.style.direction = "rtl";
textarea.focus();
const removeTextarea = () => {
document.body.removeChild(textarea);
textNode.show();
textNode.text(textarea.value);
onUpdate(obj.id, { text: textarea.value });
layerRef.current?.draw();
@@ -24,7 +24,7 @@ const TriangleShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, dr
radius={radius}
fill={obj.fill}
stroke={isSelected ? "#3b82f6" : obj.stroke}
strokeWidth={isSelected ? 3 : obj.strokeWidth || 2}
strokeWidth={isSelected ? 3 : (obj.strokeWidth ?? 0)}
rotation={obj.rotation || 0}
draggable={draggable}
onClick={() => {
@@ -30,7 +30,7 @@ const VideoShape = ({
height={obj.height || 300}
fill="#000000"
stroke={isSelected ? "#3b82f6" : "#666666"}
strokeWidth={isSelected ? 3 : 2}
strokeWidth={isSelected ? 3 : (obj.strokeWidth ?? 0)}
rotation={obj.rotation || 0}
draggable={draggable}
onClick={() => {