can shape edit + when add shape is default selected
This commit is contained in:
@@ -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, selectedObjectId } = useEditorStore();
|
const { tool, addObject, updateObject, setSelectedObjectId, selectedObjectId, setTool } = useEditorStore();
|
||||||
const { defaults } = useTextDefaultsStore();
|
const { defaults } = useTextDefaultsStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -71,6 +71,7 @@ export const useDrawingHandlers = () => {
|
|||||||
};
|
};
|
||||||
addObject(newText);
|
addObject(newText);
|
||||||
setSelectedObjectId(newText.id);
|
setSelectedObjectId(newText.id);
|
||||||
|
setTool("select");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -104,8 +105,13 @@ export const useDrawingHandlers = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleStageMouseUp = () => {
|
const handleStageMouseUp = () => {
|
||||||
|
const lastObjectId = tempObject?.id;
|
||||||
setIsDrawing(false);
|
setIsDrawing(false);
|
||||||
setTempObject(null);
|
setTempObject(null);
|
||||||
|
if (lastObjectId) {
|
||||||
|
setSelectedObjectId(lastObjectId);
|
||||||
|
setTool("select");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEditorStore, type ToolType } from "../../store/editorStore";
|
import { useEditorStore, type ToolType } from "@/pages/editor/store/editorStore";
|
||||||
import {
|
import {
|
||||||
ImageUpload,
|
ImageUpload,
|
||||||
DocumentUpload,
|
DocumentUpload,
|
||||||
@@ -19,7 +19,7 @@ type ToolInstructionsProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ToolInstructions = ({ tool }: ToolInstructionsProps) => {
|
const ToolInstructions = ({ tool }: ToolInstructionsProps) => {
|
||||||
const { addObject } = useEditorStore();
|
const { addObject, setSelectedObjectId, setTool } = useEditorStore();
|
||||||
|
|
||||||
const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
@@ -39,6 +39,8 @@ const ToolInstructions = ({ tool }: ToolInstructionsProps) => {
|
|||||||
imageUrl,
|
imageUrl,
|
||||||
};
|
};
|
||||||
addObject(newImage);
|
addObject(newImage);
|
||||||
|
setSelectedObjectId(newImage.id);
|
||||||
|
setTool("select");
|
||||||
};
|
};
|
||||||
img.src = imageUrl;
|
img.src = imageUrl;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DocumentUpload } from "iconsax-react";
|
import { DocumentUpload } from "iconsax-react";
|
||||||
import { useEditorStore, type ToolType } from "../../../store/editorStore";
|
import { useEditorStore, type ToolType } from "@/pages/editor/store/editorStore";
|
||||||
import Button from "@/components/Button";
|
import Button from "@/components/Button";
|
||||||
|
|
||||||
const DocumentUploadComponent = () => {
|
const DocumentUploadComponent = () => {
|
||||||
@@ -25,8 +25,10 @@ const DocumentUploadComponent = () => {
|
|||||||
height: 250,
|
height: 250,
|
||||||
imageUrl: fileUrl,
|
imageUrl: fileUrl,
|
||||||
};
|
};
|
||||||
const { addObject } = useEditorStore.getState();
|
const { addObject, setSelectedObjectId, setTool } = useEditorStore.getState();
|
||||||
addObject(newDocument);
|
addObject(newDocument);
|
||||||
|
setSelectedObjectId(newDocument.id);
|
||||||
|
setTool("select");
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEditorStore, type ToolType } from "../../../store/editorStore";
|
import { useEditorStore, type ToolType } from "@/pages/editor/store/editorStore";
|
||||||
import Input from "@/components/Input";
|
import Input from "@/components/Input";
|
||||||
|
|
||||||
const LinkInput = () => {
|
const LinkInput = () => {
|
||||||
@@ -21,8 +21,10 @@ const LinkInput = () => {
|
|||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fill: "#0000ff",
|
fill: "#0000ff",
|
||||||
};
|
};
|
||||||
const { addObject } = useEditorStore.getState();
|
const { addObject, setSelectedObjectId, setTool } = useEditorStore.getState();
|
||||||
addObject(newLink);
|
addObject(newLink);
|
||||||
|
setSelectedObjectId(newLink.id);
|
||||||
|
setTool("select");
|
||||||
input.value = "";
|
input.value = "";
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEditorStore, type ToolType } from "../../../store/editorStore";
|
import { useEditorStore, type ToolType } from "@/pages/editor/store/editorStore";
|
||||||
import Input from "@/components/Input";
|
import Input from "@/components/Input";
|
||||||
|
|
||||||
const VideoInput = () => {
|
const VideoInput = () => {
|
||||||
@@ -20,8 +20,10 @@ const VideoInput = () => {
|
|||||||
height: 300,
|
height: 300,
|
||||||
videoUrl: input.value,
|
videoUrl: input.value,
|
||||||
};
|
};
|
||||||
const { addObject } = useEditorStore.getState();
|
const { addObject, setSelectedObjectId, setTool } = useEditorStore.getState();
|
||||||
addObject(newVideo);
|
addObject(newVideo);
|
||||||
|
setSelectedObjectId(newVideo.id);
|
||||||
|
setTool("select");
|
||||||
input.value = "";
|
input.value = "";
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,57 +1,130 @@
|
|||||||
import type { EditorObject } from "../../../store/editorStore";
|
import { useEffect, useState } from "react";
|
||||||
|
import Button from "@/components/Button";
|
||||||
import Input from "@/components/Input";
|
import Input from "@/components/Input";
|
||||||
|
import type { EditorObject } from "@/pages/editor/store/editorStore";
|
||||||
|
|
||||||
type ShapeSettingsProps = {
|
type ShapeSettingsProps = {
|
||||||
selectedObject: EditorObject;
|
selectedObject: EditorObject;
|
||||||
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ShapeFormState = {
|
||||||
|
width: string;
|
||||||
|
height: string;
|
||||||
|
fill: string;
|
||||||
|
stroke: string;
|
||||||
|
strokeWidth: string;
|
||||||
|
};
|
||||||
|
|
||||||
const ShapeSettings = ({ selectedObject, onUpdate }: ShapeSettingsProps) => {
|
const ShapeSettings = ({ selectedObject, onUpdate }: ShapeSettingsProps) => {
|
||||||
|
const baseWidth = selectedObject.width ?? 100;
|
||||||
|
const baseHeight = selectedObject.height ?? 100;
|
||||||
|
const baseFill = selectedObject.fill ?? "#3b82f6";
|
||||||
|
const baseStroke = selectedObject.stroke ?? "#1e40af";
|
||||||
|
const baseStrokeWidth = selectedObject.strokeWidth ?? 2;
|
||||||
|
|
||||||
|
const [formState, setFormState] = useState<ShapeFormState>({
|
||||||
|
width: String(baseWidth),
|
||||||
|
height: String(baseHeight),
|
||||||
|
fill: baseFill,
|
||||||
|
stroke: baseStroke,
|
||||||
|
strokeWidth: String(baseStrokeWidth),
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFormState({
|
||||||
|
width: String(baseWidth),
|
||||||
|
height: String(baseHeight),
|
||||||
|
fill: baseFill,
|
||||||
|
stroke: baseStroke,
|
||||||
|
strokeWidth: String(baseStrokeWidth),
|
||||||
|
});
|
||||||
|
}, [baseWidth, baseHeight, baseFill, baseStroke, baseStrokeWidth]);
|
||||||
|
|
||||||
|
const parseNumber = (value: string, fallback: number) => {
|
||||||
|
const parsed = parseInt(value, 10);
|
||||||
|
return Number.isNaN(parsed) ? fallback : parsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
onUpdate(selectedObject.id, {
|
||||||
|
width: parseNumber(formState.width, baseWidth),
|
||||||
|
height: parseNumber(formState.height, baseHeight),
|
||||||
|
fill: formState.fill || baseFill,
|
||||||
|
stroke: formState.stroke || baseStroke,
|
||||||
|
strokeWidth: parseNumber(formState.strokeWidth, baseStrokeWidth),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const isDirty =
|
||||||
|
formState.width !== String(baseWidth) ||
|
||||||
|
formState.height !== String(baseHeight) ||
|
||||||
|
formState.fill !== baseFill ||
|
||||||
|
formState.stroke !== baseStroke ||
|
||||||
|
formState.strokeWidth !== String(baseStrokeWidth);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="space-y-4">
|
||||||
<Input
|
<Input
|
||||||
label="عرض"
|
label="عرض"
|
||||||
type="number"
|
type="number"
|
||||||
value={selectedObject.width || 100}
|
value={formState.width}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
onUpdate(selectedObject.id, {
|
setFormState((prev) => ({
|
||||||
width: parseInt(e.target.value) || 100,
|
...prev,
|
||||||
})
|
width: e.target.value,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label="ارتفاع"
|
label="ارتفاع"
|
||||||
type="number"
|
type="number"
|
||||||
value={selectedObject.height || 100}
|
value={formState.height}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
onUpdate(selectedObject.id, {
|
setFormState((prev) => ({
|
||||||
height: parseInt(e.target.value) || 100,
|
...prev,
|
||||||
})
|
height: e.target.value,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label="رنگ پسزمینه"
|
label="رنگ پسزمینه"
|
||||||
type="color"
|
type="color"
|
||||||
value={selectedObject.fill || "#3b82f6"}
|
value={formState.fill}
|
||||||
onChange={(e) => onUpdate(selectedObject.id, { fill: e.target.value })}
|
onChange={(e) =>
|
||||||
|
setFormState((prev) => ({
|
||||||
|
...prev,
|
||||||
|
fill: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label="رنگ خط"
|
label="رنگ خط"
|
||||||
type="color"
|
type="color"
|
||||||
value={selectedObject.stroke || "#1e40af"}
|
value={formState.stroke}
|
||||||
onChange={(e) => onUpdate(selectedObject.id, { stroke: e.target.value })}
|
onChange={(e) =>
|
||||||
|
setFormState((prev) => ({
|
||||||
|
...prev,
|
||||||
|
stroke: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
label="ضخامت خط"
|
label="ضخامت خط"
|
||||||
type="number"
|
type="number"
|
||||||
value={selectedObject.strokeWidth || 2}
|
value={formState.strokeWidth}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
onUpdate(selectedObject.id, {
|
setFormState((prev) => ({
|
||||||
strokeWidth: parseInt(e.target.value) || 2,
|
...prev,
|
||||||
})
|
strokeWidth: e.target.value,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
min={1}
|
||||||
/>
|
/>
|
||||||
</>
|
<Button type="button" onClick={handleSave} disabled={!isDirty} className="w-full">
|
||||||
|
ذخیره تغییرات
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,11 @@ type EditorStoreType = {
|
|||||||
|
|
||||||
export const useEditorStore = create<EditorStoreType>((set) => ({
|
export const useEditorStore = create<EditorStoreType>((set) => ({
|
||||||
tool: "select",
|
tool: "select",
|
||||||
setTool: (tool) => set({ tool }),
|
setTool: (tool) =>
|
||||||
|
set((state) => ({
|
||||||
|
tool,
|
||||||
|
selectedObjectId: tool === "select" ? state.selectedObjectId : null,
|
||||||
|
})),
|
||||||
objects: [],
|
objects: [],
|
||||||
addObject: (object) =>
|
addObject: (object) =>
|
||||||
set((state) => ({ objects: [...state.objects, object] })),
|
set((state) => ({ objects: [...state.objects, object] })),
|
||||||
|
|||||||
Reference in New Issue
Block a user