base group
This commit is contained in:
@@ -21,7 +21,7 @@ type ObjectRendererProps = {
|
||||
isSelected: boolean;
|
||||
transformerRef: React.RefObject<Konva.Transformer | null>;
|
||||
layerRef: React.RefObject<Konva.Layer | null>;
|
||||
onSelect: (id: string, node: Konva.Node) => void;
|
||||
onSelect: (id: string, node: Konva.Node, e?: Konva.KonvaEventObject<MouseEvent>) => void;
|
||||
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
||||
draggable: boolean;
|
||||
onCellClick?: (tableId: string, cellId: string) => void;
|
||||
@@ -51,17 +51,18 @@ const ObjectRenderer = ({
|
||||
|
||||
// Apply caching for masked objects - REQUIRED for destination-in to work
|
||||
useEffect(() => {
|
||||
if (!groupRef.current || !shouldApplyMask) return;
|
||||
const groupNode = groupRef.current;
|
||||
if (!groupNode || !shouldApplyMask) return;
|
||||
|
||||
// Use double requestAnimationFrame to ensure all changes (including stroke) are rendered before caching
|
||||
let rafId1: number;
|
||||
const rafId2 = requestAnimationFrame(() => {
|
||||
rafId1 = requestAnimationFrame(() => {
|
||||
if (!groupRef.current) return;
|
||||
|
||||
|
||||
// Clear cache first
|
||||
groupRef.current.clearCache();
|
||||
|
||||
|
||||
// Apply cache - Konva will automatically calculate bounds
|
||||
groupRef.current.cache();
|
||||
groupRef.current.getLayer()?.batchDraw();
|
||||
@@ -71,7 +72,7 @@ const ObjectRenderer = ({
|
||||
return () => {
|
||||
cancelAnimationFrame(rafId2);
|
||||
if (rafId1) cancelAnimationFrame(rafId1);
|
||||
groupRef.current?.clearCache();
|
||||
groupNode?.clearCache();
|
||||
};
|
||||
}, [shouldApplyMask, isSelected, maskShape?.x, maskShape?.y, maskShape?.width, maskShape?.height, maskShape?.rotation, obj.x, obj.y, obj.width, obj.height, obj.rotation, obj.maskInvert]);
|
||||
|
||||
@@ -97,7 +98,7 @@ const ObjectRenderer = ({
|
||||
}
|
||||
|
||||
// برای objectهای با mask، یک obj جدید با موقعیت (0, 0) ایجاد کن
|
||||
const shapeObj = shouldApplyMask
|
||||
const shapeObj = shouldApplyMask
|
||||
? { ...obj, x: 0, y: 0 }
|
||||
: obj;
|
||||
|
||||
@@ -222,8 +223,8 @@ const ObjectRenderer = ({
|
||||
key={obj.id}
|
||||
obj={obj}
|
||||
selectedCellId={selectedCellId || null}
|
||||
onCellClick={(tableId, cellId, node) => {
|
||||
onSelect(tableId, node);
|
||||
onCellClick={(tableId, cellId, node, e) => {
|
||||
onSelect(tableId, node, e);
|
||||
onCellClick?.(tableId, cellId);
|
||||
}}
|
||||
onCellDblClick={(tableId, cellId, x, y, width, height, text) => {
|
||||
@@ -236,6 +237,52 @@ const ObjectRenderer = ({
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case "group":
|
||||
// Render group as a simple selection box
|
||||
// Objects inside group are rendered separately in EditorCanvas
|
||||
shapeElement = (
|
||||
<Group
|
||||
key={obj.id}
|
||||
id={obj.id}
|
||||
name="group-container"
|
||||
x={obj.x}
|
||||
y={obj.y}
|
||||
draggable={draggable}
|
||||
onClick={(e) => {
|
||||
const groupNode = e.currentTarget;
|
||||
if (groupNode) {
|
||||
onSelect(obj.id, groupNode, e);
|
||||
}
|
||||
}}
|
||||
onDragMove={(e) => {
|
||||
const node = e.target;
|
||||
onUpdate(obj.id, {
|
||||
x: node.x(),
|
||||
y: node.y(),
|
||||
});
|
||||
}}
|
||||
onDragEnd={(e) => {
|
||||
const node = e.target;
|
||||
onUpdate(obj.id, {
|
||||
x: node.x(),
|
||||
y: node.y(),
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Rect
|
||||
x={0}
|
||||
y={0}
|
||||
width={obj.width || 100}
|
||||
height={obj.height || 100}
|
||||
fill="transparent"
|
||||
stroke={isSelected ? "#3b82f6" : "#e5e7eb"}
|
||||
strokeWidth={isSelected ? 2 : 1}
|
||||
dash={[5, 5]}
|
||||
listening={true}
|
||||
/>
|
||||
</Group>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -250,7 +297,7 @@ const ObjectRenderer = ({
|
||||
// The mask layer has listening={false} to not interfere with selection
|
||||
// Default: destination-out (overlap مخفی میشود)
|
||||
const compositeOp = obj.maskInvert === false ? "destination-in" : "destination-out";
|
||||
|
||||
|
||||
const handleGroupDragEnd = (e: Konva.KonvaEventObject<DragEvent>) => {
|
||||
const node = e.target;
|
||||
onUpdate(obj.id, {
|
||||
@@ -323,18 +370,18 @@ const ObjectRenderer = ({
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Group
|
||||
<Group
|
||||
ref={groupRef}
|
||||
id={`masked-${obj.id}`}
|
||||
name="masked-group"
|
||||
x={obj.x}
|
||||
y={obj.y}
|
||||
draggable={draggable}
|
||||
onClick={() => {
|
||||
onClick={(e) => {
|
||||
if (groupRef.current) {
|
||||
onSelect(obj.id, groupRef.current);
|
||||
onSelect(obj.id, groupRef.current, e);
|
||||
}
|
||||
}}
|
||||
onDragEnd={handleGroupDragEnd}
|
||||
|
||||
Reference in New Issue
Block a user