fix build

This commit is contained in:
hamid zarghami
2026-01-05 14:24:45 +03:30
parent 34e0b882fa
commit 3a01267b55
13 changed files with 88 additions and 79 deletions
+10 -13
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef } from "react";
import { Stage, Layer, Transformer, Rect, Group, Text } from "react-konva"; import { Stage, Layer, Transformer, Rect, Group, Text } from "react-konva";
import Konva from "konva"; import Konva from "konva";
import { useEditorStore, type EditorObject } from "../store/editorStore"; import { useEditorStore, type EditorObject } from "../store/editorStore";
@@ -20,7 +20,6 @@ const EditorCanvas = () => {
selectedObjectId, selectedObjectId,
selectedObjectIds, selectedObjectIds,
setSelectedObjectId, setSelectedObjectId,
setSelectedObjectIds,
addToSelection, addToSelection,
removeFromSelection, removeFromSelection,
setSelectedTableId, setSelectedTableId,
@@ -50,7 +49,7 @@ const EditorCanvas = () => {
} }
}, [setStageRef, setLayerRef]); }, [setStageRef, setLayerRef]);
const handleSelect = (id: string, node: Konva.Node, e?: Konva.KonvaEventObject<MouseEvent>) => { const handleSelect = (id: string, _node: Konva.Node, e?: Konva.KonvaEventObject<MouseEvent>) => {
const obj = objects.find((o) => o.id === id); const obj = objects.find((o) => o.id === id);
const isMultiSelect = e?.evt?.metaKey || e?.evt?.ctrlKey || e?.evt?.shiftKey; const isMultiSelect = e?.evt?.metaKey || e?.evt?.ctrlKey || e?.evt?.shiftKey;
@@ -90,15 +89,15 @@ const EditorCanvas = () => {
} else if (!isMultiSelect) { } else if (!isMultiSelect) {
setSelectedTableId(null); setSelectedTableId(null);
} }
if (!isMultiSelect) { if (!isMultiSelect) {
setSelectedCellId(null); setSelectedCellId(null);
} }
}; };
const handleCellClick = (tableId: string, cellId: string, node?: Konva.Node, e?: Konva.KonvaEventObject<MouseEvent>) => { const handleCellClick = (tableId: string, cellId: string, e?: Konva.KonvaEventObject<MouseEvent>) => {
const isMultiSelect = e?.evt?.metaKey || e?.evt?.ctrlKey || e?.evt?.shiftKey; const isMultiSelect = e?.evt?.metaKey || e?.evt?.ctrlKey || e?.evt?.shiftKey;
if (isMultiSelect) { if (isMultiSelect) {
// انتخاب چندتایی // انتخاب چندتایی
if (selectedObjectIds.includes(tableId)) { if (selectedObjectIds.includes(tableId)) {
@@ -110,7 +109,7 @@ const EditorCanvas = () => {
setSelectedTableId(tableId); setSelectedTableId(tableId);
setSelectedObjectId(tableId); setSelectedObjectId(tableId);
} }
setSelectedCellId(cellId); setSelectedCellId(cellId);
}; };
@@ -181,7 +180,7 @@ const EditorCanvas = () => {
if (!layerRef.current || !transformerRef.current) return; if (!layerRef.current || !transformerRef.current) return;
const nodesToTransform: Konva.Node[] = []; const nodesToTransform: Konva.Node[] = [];
selectedObjectIds.forEach((objId) => { selectedObjectIds.forEach((objId) => {
const selectedObject = objects.find((obj) => obj.id === objId); const selectedObject = objects.find((obj) => obj.id === objId);
if (!selectedObject) return; if (!selectedObject) return;
@@ -243,8 +242,8 @@ const EditorCanvas = () => {
const scaledY = relativeY * scaleY; const scaledY = relativeY * scaleY;
// Find the Konva node for this member // Find the Konva node for this member
const memberNode = layerRef.current?.findOne(`#${member.id}`) || const memberNode = layerRef.current?.findOne(`#${member.id}`) ||
layerRef.current?.findOne(`#masked-${member.id}`); layerRef.current?.findOne(`#masked-${member.id}`);
if (memberNode) { if (memberNode) {
memberNode.x(newX + scaledX); memberNode.x(newX + scaledX);
memberNode.y(newY + scaledY); memberNode.y(newY + scaledY);
@@ -283,8 +282,6 @@ const EditorCanvas = () => {
const groupMembers = objects.filter((o) => o.groupId === selectedObject.id); const groupMembers = objects.filter((o) => o.groupId === selectedObject.id);
const oldGroupX = selectedObject.x || 0; const oldGroupX = selectedObject.x || 0;
const oldGroupY = selectedObject.y || 0; const oldGroupY = selectedObject.y || 0;
const oldGroupWidth = selectedObject.width || 100;
const oldGroupHeight = selectedObject.height || 100;
// محاسبه bounds جدید بر اساا member های update شده // محاسبه bounds جدید بر اساا member های update شده
const updatedMembers = groupMembers.map((member) => { const updatedMembers = groupMembers.map((member) => {
@@ -480,7 +477,7 @@ const EditorCanvas = () => {
.map((obj) => { .map((obj) => {
const groupObject = objects.find((o) => o.id === obj.groupId); const groupObject = objects.find((o) => o.id === obj.groupId);
if (!groupObject) return null; if (!groupObject) return null;
// Render with absolute position but wrapped in non-listening group // Render with absolute position but wrapped in non-listening group
return ( return (
<Group key={`group-member-${obj.id}`} listening={false}> <Group key={`group-member-${obj.id}`} listening={false}>
@@ -10,7 +10,7 @@ export const useDrawingHandlers = () => {
const [tempObject, setTempObject] = useState<EditorObject | null>(null); const [tempObject, setTempObject] = useState<EditorObject | null>(null);
const transformerRef = useRef<Konva.Transformer>(null); const transformerRef = useRef<Konva.Transformer>(null);
const { tool, addObject, updateObject, setSelectedObjectId, setSelectedObjectIds, selectedObjectId, selectedObjectIds, setTool, addTable, setSelectedTableId } = useEditorStore(); const { tool, addObject, updateObject, setSelectedObjectId, setSelectedObjectIds, selectedObjectIds, setTool, addTable, setSelectedTableId } = useEditorStore();
const { defaults } = useTextDefaultsStore(); const { defaults } = useTextDefaultsStore();
useEffect(() => { useEffect(() => {
@@ -3,7 +3,6 @@ import { useEditorStore } from "../../store/editorStore";
export const useKeyboardMovement = () => { export const useKeyboardMovement = () => {
const { const {
selectedObjectId,
selectedObjectIds, selectedObjectIds,
objects, objects,
updateObject, updateObject,
@@ -33,7 +32,7 @@ export const useKeyboardMovement = () => {
if (e.key === "Delete" || e.key === "Backspace") { if (e.key === "Delete" || e.key === "Backspace") {
if (selectedObjectIds.length > 0) { if (selectedObjectIds.length > 0) {
e.preventDefault(); e.preventDefault();
selectedObjectIds.forEach(id => deleteObject(id)); selectedObjectIds.forEach((id) => deleteObject(id));
return; return;
} }
} }
@@ -42,7 +41,9 @@ export const useKeyboardMovement = () => {
if ((e.metaKey || e.ctrlKey) && e.key === "g") { if ((e.metaKey || e.ctrlKey) && e.key === "g") {
e.preventDefault(); e.preventDefault();
if (selectedObjectIds.length === 1) { if (selectedObjectIds.length === 1) {
const selectedObject = objects.find((obj) => obj.id === selectedObjectIds[0]); const selectedObject = objects.find(
(obj) => obj.id === selectedObjectIds[0]
);
// اگر group انتخاب شده، ungroup کن // اگر group انتخاب شده، ungroup کن
if (selectedObject?.type === "group") { if (selectedObject?.type === "group") {
ungroupObjects(selectedObject.id); ungroupObjects(selectedObject.id);
@@ -116,5 +117,14 @@ export const useKeyboardMovement = () => {
return () => { return () => {
window.removeEventListener("keydown", handleKeyDown); window.removeEventListener("keydown", handleKeyDown);
}; };
}, [selectedObjectIds, objects, updateObject, deleteObject, tool, layerRef, groupObjects, ungroupObjects]); }, [
selectedObjectIds,
objects,
updateObject,
deleteObject,
tool,
layerRef,
groupObjects,
ungroupObjects,
]);
}; };
@@ -1,9 +1,9 @@
import { useEffect, useRef } from "react"; import { useRef } from "react";
import { Star } from "react-konva"; import { Star } from "react-konva";
import Konva from "konva"; import Konva from "konva";
import type { ShapeProps } from "./types"; import type { ShapeProps } from "./types";
const AbstractShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, draggable }: ShapeProps) => { const AbstractShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
const shapeRef = useRef<Konva.Star>(null); const shapeRef = useRef<Konva.Star>(null);
// Transformer is managed in EditorCanvas for multi-select support // Transformer is managed in EditorCanvas for multi-select support
@@ -27,13 +27,13 @@ const AbstractShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, dr
outerRadius={radius} outerRadius={radius}
fill={isMask ? "transparent" : obj.fill} fill={isMask ? "transparent" : obj.fill}
stroke={ stroke={
isSelected isSelected
? "#3b82f6" ? "#3b82f6"
: (isMask && showGuide) : (isMask && showGuide)
? "#ff6b6b" ? "#ff6b6b"
: (isMask && !showGuide) : (isMask && !showGuide)
? "transparent" ? "transparent"
: obj.stroke : obj.stroke
} }
strokeWidth={isSelected ? 3 : (isMask && showGuide ? 2 : (obj.strokeWidth ?? 0))} strokeWidth={isSelected ? 3 : (isMask && showGuide ? 2 : (obj.strokeWidth ?? 0))}
dash={isMask && showGuide ? [10, 5] : undefined} dash={isMask && showGuide ? [10, 5] : undefined}
@@ -1,9 +1,9 @@
import { useEffect, useRef } from "react"; import { useRef } from "react";
import { Arrow } from "react-konva"; import { Arrow } from "react-konva";
import Konva from "konva"; import Konva from "konva";
import type { ShapeProps } from "./types"; import type { ShapeProps } from "./types";
const ArrowShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, draggable }: ShapeProps) => { const ArrowShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
const shapeRef = useRef<Konva.Arrow>(null); const shapeRef = useRef<Konva.Arrow>(null);
// Transformer is managed in EditorCanvas for multi-select support // Transformer is managed in EditorCanvas for multi-select support
@@ -1,16 +1,16 @@
import { useEffect, useRef } from "react"; import { useRef } from "react";
import { Circle } from "react-konva"; import { Circle } from "react-konva";
import Konva from "konva"; import Konva from "konva";
import type { ShapeProps } from "./types"; import type { ShapeProps } from "./types";
const CircleShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, draggable }: ShapeProps) => { const CircleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
const shapeRef = useRef<Konva.Circle>(null); const shapeRef = useRef<Konva.Circle>(null);
// Transformer is managed in EditorCanvas for multi-select support // Transformer is managed in EditorCanvas for multi-select support
const isMask = obj.isMask || false; const isMask = obj.isMask || false;
const showGuide = obj.showMaskGuide !== false; const showGuide = obj.showMaskGuide !== false;
return ( return (
<Circle <Circle
ref={shapeRef} ref={shapeRef}
@@ -21,13 +21,13 @@ const CircleShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, drag
radius={(obj.width || 50) / 2} radius={(obj.width || 50) / 2}
fill={isMask ? "transparent" : obj.fill} fill={isMask ? "transparent" : obj.fill}
stroke={ stroke={
isSelected isSelected
? "#3b82f6" ? "#3b82f6"
: (isMask && showGuide) : (isMask && showGuide)
? "#ff6b6b" ? "#ff6b6b"
: (isMask && !showGuide) : (isMask && !showGuide)
? "transparent" ? "transparent"
: obj.stroke : obj.stroke
} }
strokeWidth={isSelected ? 3 : (isMask && showGuide ? 2 : (obj.strokeWidth ?? 0))} strokeWidth={isSelected ? 3 : (isMask && showGuide ? 2 : (obj.strokeWidth ?? 0))}
dash={isMask && showGuide ? [10, 5] : undefined} dash={isMask && showGuide ? [10, 5] : undefined}
@@ -1,4 +1,4 @@
import { useEffect, useRef } from "react"; import { useRef } from "react";
import { Image as KonvaImage } from "react-konva"; import { Image as KonvaImage } from "react-konva";
import Konva from "konva"; import Konva from "konva";
import useImage from "use-image"; import useImage from "use-image";
@@ -7,7 +7,6 @@ import type { ShapeProps } from "./types";
const ImageObject = ({ const ImageObject = ({
obj, obj,
isSelected, isSelected,
transformerRef,
onSelect, onSelect,
onUpdate, onUpdate,
draggable, draggable,
@@ -1,9 +1,9 @@
import { useEffect, useRef } from "react"; import { useRef } from "react";
import { Line } from "react-konva"; import { Line } from "react-konva";
import Konva from "konva"; import Konva from "konva";
import type { ShapeProps } from "./types"; import type { ShapeProps } from "./types";
const LineShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, draggable }: ShapeProps) => { const LineShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
const shapeRef = useRef<Konva.Line>(null); const shapeRef = useRef<Konva.Line>(null);
// Transformer is managed in EditorCanvas for multi-select support // Transformer is managed in EditorCanvas for multi-select support
@@ -1,4 +1,4 @@
import { useEffect, useRef, useMemo } from "react"; import { useRef, useMemo } from "react";
import { Text } from "react-konva"; import { Text } from "react-konva";
import Konva from "konva"; import Konva from "konva";
import type { ShapeProps } from "./types"; import type { ShapeProps } from "./types";
@@ -14,8 +14,6 @@ const getFontWeight = (fontWeight?: string): string => {
const LinkShape = ({ const LinkShape = ({
obj, obj,
isSelected,
transformerRef,
onSelect, onSelect,
onUpdate, onUpdate,
draggable, draggable,
@@ -1,9 +1,9 @@
import { useEffect, useRef } from "react"; import { useRef } from "react";
import { Rect } from "react-konva"; import { Rect } from "react-konva";
import Konva from "konva"; import Konva from "konva";
import type { ShapeProps } from "./types"; import type { ShapeProps } from "./types";
const RectangleShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, draggable }: ShapeProps) => { const RectangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
const shapeRef = useRef<Konva.Rect>(null); const shapeRef = useRef<Konva.Rect>(null);
// Transformer is managed in EditorCanvas for multi-select support // Transformer is managed in EditorCanvas for multi-select support
@@ -22,10 +22,10 @@ const RectangleShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, d
height={obj.height || 100} height={obj.height || 100}
fill={isMask ? "transparent" : obj.fill} fill={isMask ? "transparent" : obj.fill}
stroke={ stroke={
isSelected isSelected
? "#3b82f6" ? "#3b82f6"
: (isMask && showGuide) : (isMask && showGuide)
? "#ff6b6b" ? "#ff6b6b"
: (isMask && !showGuide) : (isMask && !showGuide)
? "transparent" ? "transparent"
: obj.stroke : obj.stroke
@@ -1,9 +1,9 @@
import { useEffect, useRef } from "react"; import { useRef } from "react";
import { RegularPolygon } from "react-konva"; import { RegularPolygon } from "react-konva";
import Konva from "konva"; import Konva from "konva";
import type { ShapeProps } from "./types"; import type { ShapeProps } from "./types";
const TriangleShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, draggable }: ShapeProps) => { const TriangleShape = ({ obj, isSelected, onSelect, onUpdate, draggable }: ShapeProps) => {
const shapeRef = useRef<Konva.RegularPolygon>(null); const shapeRef = useRef<Konva.RegularPolygon>(null);
// Transformer is managed in EditorCanvas for multi-select support // Transformer is managed in EditorCanvas for multi-select support
@@ -23,13 +23,13 @@ const TriangleShape = ({ obj, isSelected, transformerRef, onSelect, onUpdate, dr
radius={radius} radius={radius}
fill={isMask ? "transparent" : obj.fill} fill={isMask ? "transparent" : obj.fill}
stroke={ stroke={
isSelected isSelected
? "#3b82f6" ? "#3b82f6"
: (isMask && showGuide) : (isMask && showGuide)
? "#ff6b6b" ? "#ff6b6b"
: (isMask && !showGuide) : (isMask && !showGuide)
? "transparent" ? "transparent"
: obj.stroke : obj.stroke
} }
strokeWidth={isSelected ? 3 : (isMask && showGuide ? 2 : (obj.strokeWidth ?? 0))} strokeWidth={isSelected ? 3 : (isMask && showGuide ? 2 : (obj.strokeWidth ?? 0))}
dash={isMask && showGuide ? [10, 5] : undefined} dash={isMask && showGuide ? [10, 5] : undefined}
@@ -8,7 +8,6 @@ import { createPortal } from "react-dom";
const VideoShape = ({ const VideoShape = ({
obj, obj,
isSelected, isSelected,
transformerRef,
onSelect, onSelect,
onUpdate, onUpdate,
draggable, draggable,
+23 -17
View File
@@ -307,34 +307,36 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
}, },
selectedObjectId: null, selectedObjectId: null,
setSelectedObjectId: (id) => { setSelectedObjectId: (id) => {
set({ set({
selectedObjectId: id, selectedObjectId: id,
selectedObjectIds: id ? [id] : [] selectedObjectIds: id ? [id] : [],
}); });
}, },
selectedObjectIds: [], selectedObjectIds: [],
setSelectedObjectIds: (ids) => { setSelectedObjectIds: (ids) => {
set({ set({
selectedObjectIds: ids, selectedObjectIds: ids,
selectedObjectId: ids.length > 0 ? ids[0] : null selectedObjectId: ids.length > 0 ? ids[0] : null,
}); });
}, },
addToSelection: (id) => { addToSelection: (id) => {
const state = get(); const state = get();
if (!state.selectedObjectIds.includes(id)) { if (!state.selectedObjectIds.includes(id)) {
const newIds = [...state.selectedObjectIds, id]; const newIds = [...state.selectedObjectIds, id];
set({ set({
selectedObjectIds: newIds, selectedObjectIds: newIds,
selectedObjectId: newIds[0] selectedObjectId: newIds[0],
}); });
} }
}, },
removeFromSelection: (id) => { removeFromSelection: (id) => {
const state = get(); const state = get();
const newIds = state.selectedObjectIds.filter(selectedId => selectedId !== id); const newIds = state.selectedObjectIds.filter(
set({ (selectedId) => selectedId !== id
);
set({
selectedObjectIds: newIds, selectedObjectIds: newIds,
selectedObjectId: newIds.length > 0 ? newIds[0] : null selectedObjectId: newIds.length > 0 ? newIds[0] : null,
}); });
}, },
selectedTableId: null, selectedTableId: null,
@@ -650,7 +652,9 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
if (!obj) return; if (!obj) return;
const newVisible = !(obj.visible ?? true); const newVisible = !(obj.visible ?? true);
const shouldDeselect = !newVisible && (state.selectedObjectId === id || state.selectedTableId === id); const shouldDeselect =
!newVisible &&
(state.selectedObjectId === id || state.selectedTableId === id);
if (state.currentPageId) { if (state.currentPageId) {
set((state) => ({ set((state) => ({
@@ -704,7 +708,9 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
oldGroupIdsToRemove.add(id); oldGroupIdsToRemove.add(id);
} else if (obj?.groupId) { } else if (obj?.groupId) {
// اگر این آبجکت در یک گروه است، همه آبجکت‌های آن گروه را اضافه کن // اگر این آبجکت در یک گروه است، همه آبجکت‌های آن گروه را اضافه کن
const oldGroupMembers = state.objects.filter((o) => o.groupId === obj.groupId); const oldGroupMembers = state.objects.filter(
(o) => o.groupId === obj.groupId
);
oldGroupMembers.forEach((member) => { oldGroupMembers.forEach((member) => {
if (member.type !== "group") { if (member.type !== "group") {
allObjectIdsToGroup.add(member.id); allObjectIdsToGroup.add(member.id);
@@ -721,10 +727,10 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
if (allObjectIdsToGroup.size < 2) return; if (allObjectIdsToGroup.size < 2) return;
const groupId = `group-${crypto.randomUUID()}`; const groupId = `group-${crypto.randomUUID()}`;
const objectsToGroup = state.objects.filter((obj) => const objectsToGroup = state.objects.filter(
allObjectIdsToGroup.has(obj.id) && obj.type !== "group" (obj) => allObjectIdsToGroup.has(obj.id) && obj.type !== "group"
); );
if (objectsToGroup.length === 0) return; if (objectsToGroup.length === 0) return;
// محاسبه bounds گروه با در نظر گرفتن rotation // محاسبه bounds گروه با در نظر گرفتن rotation
@@ -737,7 +743,7 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
// برای متن‌هایی که width و height ندارند، از مقادیر پیش‌فرض استفاده کن // برای متن‌هایی که width و height ندارند، از مقادیر پیش‌فرض استفاده کن
let w = obj.width || 0; let w = obj.width || 0;
let h = obj.height || 0; let h = obj.height || 0;
if (obj.type === "text") { if (obj.type === "text") {
// اگر متن width نداره، از fontSize و طول متن برای تخمین استفاده کن // اگر متن width نداره، از fontSize و طول متن برای تخمین استفاده کن
if (!w) { if (!w) {
@@ -750,7 +756,7 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
h = obj.fontSize || 24; h = obj.fontSize || 24;
} }
} }
const cx = (obj.x || 0) + w / 2; const cx = (obj.x || 0) + w / 2;
const cy = (obj.y || 0) + h / 2; const cy = (obj.y || 0) + h / 2;
const rot = ((obj.rotation || 0) * Math.PI) / 180; const rot = ((obj.rotation || 0) * Math.PI) / 180;
@@ -853,7 +859,7 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
selectedObjectIds: [], selectedObjectIds: [],
})); }));
}, },
getGroupObjects: (groupId) => { getGroupObjects: (groupId: string) => {
const state = get(); const state = get();
return state.objects.filter((obj) => obj.groupId === groupId); return state.objects.filter((obj) => obj.groupId === groupId);
}, },