costum shape

This commit is contained in:
hamid zarghami
2026-06-13 16:22:46 +03:30
parent e6fc300fba
commit f9113ef9c8
17 changed files with 610 additions and 3 deletions
@@ -4,6 +4,7 @@ import TextInstruction from "./instructions/TextInstruction";
import {
AlignmentSettings,
AudioSettings,
CustomShapeSettings,
EntranceAnimationSettings,
GridSettings,
LineSettings,
@@ -97,6 +98,10 @@ const ObjectSettings = ({ selectedObject, pageWidth, pageHeight, onUpdate, onDel
{roundedSelectedObject.type === "grid" && <GridSettings selectedObject={roundedSelectedObject} onUpdate={handleRoundedUpdate} />}
{roundedSelectedObject.type === "custom-shape" && (
<CustomShapeSettings selectedObject={roundedSelectedObject} onUpdate={handleRoundedUpdate} />
)}
<EntranceAnimationSettings selectedObject={roundedSelectedObject} onUpdate={handleRoundedUpdate} />
<TransformSettings selectedObject={roundedSelectedObject} onUpdate={handleRoundedUpdate} />
@@ -11,6 +11,7 @@ import {
ArrowInstruction,
StickerInstruction,
GridInstruction,
CustomShapeInstruction,
} from "./instructions";
type ToolInstructionsProps = {
@@ -42,6 +43,7 @@ const ToolInstructions = ({ tool }: ToolInstructionsProps) => {
arrow: <ArrowInstruction />,
sticker: <StickerInstruction />,
grid: <GridInstruction />,
"custom-shape": <CustomShapeInstruction />,
};
return simpleInstructions[tool] ?? null;
@@ -11,6 +11,7 @@ import {
Music,
Minus,
Link2,
Magicpen,
} from "iconsax-react";
import type { ToolType } from "../../store/editorStore";
@@ -26,6 +27,7 @@ const tools: Array<{ icon: (color: string, variant?: "Bold" | "Outline" | "Broke
{ icon: (color, variant) => <ArrowLeft size={20} color={color} variant={variant} />, tool: "arrow", label: "پیکان" },
{ icon: (color, variant) => <Minus size={20} color={color} variant={variant} />, tool: "line", label: "خط" },
{ icon: (color, variant) => <Sticker size={20} color={color} variant={variant} />, tool: "sticker", label: "استیکر" },
{ icon: (color, variant) => <Magicpen size={20} color={color} variant={variant} />, tool: "custom-shape", label: "شکل سفارشی" },
];
type ToolsBarProps = {
@@ -0,0 +1,13 @@
const CustomShapeInstruction = () => (
<div className="space-y-2 text-sm text-gray-600">
<p className="font-medium text-gray-700">رسم شکل سفارشی</p>
<ul className="space-y-1">
<li> روی کانوس کلیک کنید تا نقاط اضافه شود</li>
<li> روی نقطه اول کلیک کنید تا شکل بسته شود</li>
<li> دوبار کلیک کنید تا شکل تکمیل شود</li>
<li> Escape را بزنید تا لغو شود</li>
</ul>
</div>
);
export default CustomShapeInstruction;
@@ -12,3 +12,4 @@ export { default as LineInstruction } from "./LineInstruction";
export { default as ArrowInstruction } from "./ArrowInstruction";
export { default as StickerInstruction } from "./StickerInstruction";
export { default as GridInstruction } from "./GridInstruction";
export { default as CustomShapeInstruction } from "./CustomShapeInstruction";
@@ -0,0 +1,137 @@
import ColorPicker from "@/components/ColorPicker";
import Input from "@/components/Input";
import type { EditorObject } from "@/pages/editor/store/editorStore";
type CustomShapeSettingsProps = {
selectedObject: EditorObject;
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
};
/**
* Sidebar settings panel for a completed custom-shape polygon.
* Supports solid fill, gradient fill, stroke colour, and stroke width.
* Mirrors the visual and UX conventions of ShapeSettings.
*/
const CustomShapeSettings = ({ selectedObject, onUpdate }: CustomShapeSettingsProps) => {
const fill = selectedObject.fill ?? "#3b82f6";
const fillType = selectedObject.fillType ?? "solid";
const gradient = selectedObject.gradient ?? {
from: "#3b82f6",
to: "#1d4ed8",
angle: 135,
};
const stroke = selectedObject.stroke ?? "#1e40af";
const strokeWidth = selectedObject.strokeWidth ?? 2;
return (
<div className="space-y-4">
<ColorPicker
label="رنگ پر"
value={fill}
readOnly={fillType === "gradient"}
onChange={(value) => onUpdate(selectedObject.id, { fill: value })}
/>
<div className="space-y-2">
<label className="text-sm">نوع Fill</label>
<div className="flex gap-2">
<button
type="button"
onClick={() => onUpdate(selectedObject.id, { fillType: "solid" })}
className={`px-3 py-1.5 rounded-lg border text-xs ${
fillType === "solid"
? "border-gray-800 text-gray-900"
: "border-border text-gray-500"
}`}
>
رنگ ساده
</button>
<button
type="button"
onClick={() =>
onUpdate(selectedObject.id, {
fillType: "gradient",
gradient: selectedObject.gradient ?? gradient,
})
}
className={`px-3 py-1.5 rounded-lg border text-xs ${
fillType === "gradient"
? "border-gray-800 text-gray-900"
: "border-border text-gray-500"
}`}
>
گرادیانت
</button>
</div>
</div>
{fillType === "gradient" && (
<div className="space-y-3 border border-border rounded-xl p-3">
<ColorPicker
label="رنگ شروع"
value={gradient.from}
onChange={(value) =>
onUpdate(selectedObject.id, { gradient: { ...gradient, from: value } })
}
/>
<ColorPicker
label="رنگ پایان"
value={gradient.to}
onChange={(value) =>
onUpdate(selectedObject.id, { gradient: { ...gradient, to: value } })
}
/>
<div className="space-y-1">
<label className="text-sm">زاویه گرادیانت</label>
<div className="flex items-center gap-2">
<input
type="range"
min={0}
max={360}
value={gradient.angle}
onChange={(e) =>
onUpdate(selectedObject.id, {
gradient: { ...gradient, angle: Number(e.target.value) },
})
}
className="w-full"
/>
<input
type="number"
min={0}
max={360}
value={gradient.angle}
onChange={(e) =>
onUpdate(selectedObject.id, {
gradient: { ...gradient, angle: Number(e.target.value) || 0 },
})
}
className="w-16 h-9 rounded-lg border border-border px-2 text-xs"
/>
</div>
</div>
</div>
)}
<ColorPicker
label="رنگ خط"
value={stroke}
onChange={(value) => onUpdate(selectedObject.id, { stroke: value })}
/>
<Input
label="ضخامت خط"
type="number"
value={strokeWidth}
min={0}
onChange={(e) =>
onUpdate(selectedObject.id, {
strokeWidth: parseInt(e.target.value) || 0,
})
}
/>
</div>
);
};
export default CustomShapeSettings;
@@ -1,4 +1,5 @@
export { default as TextSettings } from "./TextSettings";
export { default as CustomShapeSettings } from "./CustomShapeSettings";
export { default as ShapeSettings } from "./ShapeSettings";
export { default as LineSettings } from "./LineSettings";
export { default as SizeSettings } from "./SizeSettings";