add konva js to project + create some components with ai
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import { Trash } from "iconsax-react";
|
||||
import type { EditorObject } from "../../store/editorStore";
|
||||
import {
|
||||
TextSettings,
|
||||
ShapeSettings,
|
||||
LineSettings,
|
||||
SizeSettings,
|
||||
LinkSettings,
|
||||
VideoSettings,
|
||||
TransformSettings,
|
||||
} from "./settings";
|
||||
|
||||
type ObjectSettingsProps = {
|
||||
selectedObject: EditorObject;
|
||||
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
||||
onDelete: (id: string) => void;
|
||||
};
|
||||
|
||||
const ObjectSettings = ({ selectedObject, onUpdate, onDelete }: ObjectSettingsProps) => {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between pb-4 border-b border-border">
|
||||
<h3 className="text-sm font-medium">تنظیمات شیء</h3>
|
||||
<button
|
||||
onClick={() => onDelete(selectedObject.id)}
|
||||
className="p-2 hover:bg-red-50 rounded-lg transition-colors text-red-500"
|
||||
>
|
||||
<Trash size={20} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{selectedObject.type === "text" && <TextSettings selectedObject={selectedObject} onUpdate={onUpdate} />}
|
||||
|
||||
{(selectedObject.type === "rectangle" || selectedObject.type === "circle") && (
|
||||
<ShapeSettings selectedObject={selectedObject} onUpdate={onUpdate} />
|
||||
)}
|
||||
|
||||
{(selectedObject.type === "line" || selectedObject.type === "arrow") && (
|
||||
<LineSettings selectedObject={selectedObject} onUpdate={onUpdate} />
|
||||
)}
|
||||
|
||||
{selectedObject.type === "image" && (
|
||||
<SizeSettings selectedObject={selectedObject} onUpdate={onUpdate} />
|
||||
)}
|
||||
|
||||
{selectedObject.type === "link" && (
|
||||
<LinkSettings selectedObject={selectedObject} onUpdate={onUpdate} />
|
||||
)}
|
||||
|
||||
{selectedObject.type === "video" && (
|
||||
<VideoSettings selectedObject={selectedObject} onUpdate={onUpdate} />
|
||||
)}
|
||||
|
||||
{(selectedObject.type === "document" || selectedObject.type === "sticker") && (
|
||||
<SizeSettings
|
||||
selectedObject={selectedObject}
|
||||
onUpdate={onUpdate}
|
||||
defaultWidth={selectedObject.type === "document" ? 200 : 100}
|
||||
defaultHeight={selectedObject.type === "document" ? 250 : 100}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TransformSettings selectedObject={selectedObject} onUpdate={onUpdate} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ObjectSettings;
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { ToolType } from "../../store/editorStore";
|
||||
import { ImageUpload, DocumentUpload, VideoInput, LinkInput, SimpleInstruction } from "./instructions";
|
||||
|
||||
type ToolInstructionsProps = {
|
||||
tool: ToolType;
|
||||
onImageUpload: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
};
|
||||
|
||||
const ToolInstructions = ({ tool, onImageUpload }: ToolInstructionsProps) => {
|
||||
if (tool === "image") {
|
||||
return <ImageUpload onImageUpload={onImageUpload} />;
|
||||
}
|
||||
|
||||
if (tool === "document") {
|
||||
return <DocumentUpload />;
|
||||
}
|
||||
|
||||
if (tool === "video") {
|
||||
return <VideoInput />;
|
||||
}
|
||||
|
||||
if (tool === "link") {
|
||||
return <LinkInput />;
|
||||
}
|
||||
|
||||
return <SimpleInstruction tool={tool} />;
|
||||
};
|
||||
|
||||
export default ToolInstructions;
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { clx } from "@/helpers/utils";
|
||||
import {
|
||||
DocumentUpload,
|
||||
Element,
|
||||
Gallery,
|
||||
Grid8,
|
||||
Link,
|
||||
MouseSquare,
|
||||
Shapes,
|
||||
Sticker,
|
||||
Text,
|
||||
VideoSquare,
|
||||
Minus,
|
||||
} from "iconsax-react";
|
||||
import type { ToolType } from "../../store/editorStore";
|
||||
|
||||
const tools: Array<{ icon: (color: string, variant?: "Bold" | "Outline" | "Broken" | "Bulk" | "Linear" | "TwoTone") => React.ReactNode; tool: ToolType; label: string }> = [
|
||||
{ icon: (color, variant) => <MouseSquare size={20} color={color} variant={variant} />, tool: "select", label: "انتخاب" },
|
||||
{ icon: (color, variant) => <Shapes size={20} color={color} variant={variant} />, tool: "rectangle", label: "مستطیل" },
|
||||
{ icon: (color, variant) => <Shapes size={20} color={color} variant={variant} />, tool: "circle", label: "دایره" },
|
||||
{ icon: (color, variant) => <Text size={20} color={color} variant={variant} />, tool: "text", label: "متن" },
|
||||
{ icon: (color, variant) => <Minus size={20} color={color} variant={variant} />, tool: "line", label: "خط" },
|
||||
{ icon: (color, variant) => <Grid8 size={20} color={color} variant={variant} />, tool: "grid", label: "گرید" },
|
||||
{ icon: (color, variant) => <Sticker size={20} color={color} variant={variant} />, tool: "sticker", label: "استیکر" },
|
||||
{ icon: (color, variant) => <DocumentUpload size={20} color={color} variant={variant} />, tool: "document", label: "سند" },
|
||||
{ icon: (color, variant) => <Element size={20} color={color} variant={variant} />, tool: "arrow", label: "پیکان" },
|
||||
{ icon: (color, variant) => <Gallery size={20} color={color} variant={variant} />, tool: "image", label: "تصویر" },
|
||||
{ icon: (color, variant) => <VideoSquare size={20} color={color} variant={variant} />, tool: "video", label: "ویدیو" },
|
||||
{ icon: (color, variant) => <Link size={20} color={color} variant={variant} />, tool: "link", label: "لینک" },
|
||||
];
|
||||
|
||||
type ToolsBarProps = {
|
||||
tool: ToolType;
|
||||
onToolClick: (tool: ToolType) => void;
|
||||
};
|
||||
|
||||
const ToolsBar = ({ tool, onToolClick }: ToolsBarProps) => {
|
||||
return (
|
||||
<div className="flex h-full max-w-[72px] flex-1 flex-col items-center gap-4 overflow-y-auto border-l border-border py-2">
|
||||
{tools.map((item, index) => {
|
||||
const isActive = tool === item.tool;
|
||||
const iconColor = "black";
|
||||
const iconVariant = isActive ? "Bold" : undefined;
|
||||
return (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => onToolClick(item.tool)}
|
||||
className={clx(
|
||||
"min-h-10 w-10 flex items-center justify-center rounded-lg transition-colors text-black",
|
||||
// isActive
|
||||
// ? "bg-black text-white"
|
||||
// : "hover:bg-gray-100 text-black",
|
||||
)}
|
||||
title={item.label}
|
||||
>
|
||||
{item.icon(iconColor, iconVariant)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToolsBar;
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export { default as ToolsBar } from "./ToolsBar";
|
||||
export { default as ObjectSettings } from "./ObjectSettings";
|
||||
export { default as ToolInstructions } from "./ToolInstructions";
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { DocumentUpload } from "iconsax-react";
|
||||
import { useEditorStore, type ToolType } from "../../../store/editorStore";
|
||||
import Button from "@/components/Button";
|
||||
|
||||
const DocumentUploadComponent = () => {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm mb-2">آپلود سند</label>
|
||||
<input
|
||||
type="file"
|
||||
accept=".pdf,.doc,.docx"
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
const fileUrl = event.target?.result as string;
|
||||
const newDocument = {
|
||||
id: `document-${Date.now()}`,
|
||||
type: "document" as ToolType,
|
||||
x: 100,
|
||||
y: 100,
|
||||
width: 200,
|
||||
height: 250,
|
||||
imageUrl: fileUrl,
|
||||
};
|
||||
const { addObject } = useEditorStore.getState();
|
||||
addObject(newDocument);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
}}
|
||||
className="hidden"
|
||||
id="document-upload"
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => document.getElementById("document-upload")?.click()}
|
||||
className="w-full"
|
||||
>
|
||||
<DocumentUpload size={20} />
|
||||
انتخاب سند
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DocumentUploadComponent;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Gallery } from "iconsax-react";
|
||||
import Button from "@/components/Button";
|
||||
|
||||
type ImageUploadProps = {
|
||||
onImageUpload: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
};
|
||||
|
||||
const ImageUpload = ({ onImageUpload }: ImageUploadProps) => {
|
||||
return (
|
||||
<div>
|
||||
<label className="block text-sm mb-2">آپلود تصویر</label>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={onImageUpload}
|
||||
className="hidden"
|
||||
id="image-upload"
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => document.getElementById("image-upload")?.click()}
|
||||
className="w-full"
|
||||
>
|
||||
<Gallery size={20} />
|
||||
انتخاب تصویر
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImageUpload;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useEditorStore, type ToolType } from "../../../store/editorStore";
|
||||
import Input from "@/components/Input";
|
||||
|
||||
const LinkInput = () => {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Input
|
||||
label="آدرس لینک"
|
||||
placeholder="https://example.com"
|
||||
onEnter={() => {
|
||||
const input = document.querySelector('input[placeholder="https://example.com"]') as HTMLInputElement;
|
||||
if (input?.value) {
|
||||
const newLink = {
|
||||
id: `link-${Date.now()}`,
|
||||
type: "link" as ToolType,
|
||||
x: 100,
|
||||
y: 100,
|
||||
text: input.value,
|
||||
linkUrl: input.value,
|
||||
fontSize: 24,
|
||||
fill: "#0000ff",
|
||||
};
|
||||
const { addObject } = useEditorStore.getState();
|
||||
addObject(newLink);
|
||||
input.value = "";
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm text-gray-600">
|
||||
<p>آدرس لینک را وارد کنید و Enter بزنید</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LinkInput;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { ToolType } from "../../../store/editorStore";
|
||||
|
||||
type SimpleInstructionProps = {
|
||||
tool: ToolType;
|
||||
};
|
||||
|
||||
const SimpleInstruction = ({ tool }: SimpleInstructionProps) => {
|
||||
const instructions: Record<ToolType, string> = {
|
||||
select: "برای انتخاب و جابجایی اشیاء، روی آنها کلیک کنید",
|
||||
rectangle: "برای رسم مستطیل، روی کانوس کلیک کرده و بکشید",
|
||||
circle: "برای رسم دایره، روی کانوس کلیک کرده و بکشید",
|
||||
text: "برای افزودن متن، روی کانوس کلیک کنید",
|
||||
line: "برای رسم خط، روی کانوس کلیک کرده و بکشید",
|
||||
arrow: "برای رسم پیکان، روی کانوس کلیک کرده و بکشید",
|
||||
sticker: "برای افزودن استیکر، روی کانوس کلیک کنید",
|
||||
grid: "برای افزودن گرید، روی کانوس کلیک کنید",
|
||||
image: "",
|
||||
document: "",
|
||||
video: "",
|
||||
link: "",
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="text-sm text-gray-600">
|
||||
<p>{instructions[tool]}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SimpleInstruction;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useEditorStore, type ToolType } from "../../../store/editorStore";
|
||||
import Input from "@/components/Input";
|
||||
|
||||
const VideoInput = () => {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Input
|
||||
label="آدرس ویدیو"
|
||||
placeholder="https://example.com/video.mp4"
|
||||
onEnter={() => {
|
||||
const input = document.querySelector('input[placeholder="https://example.com/video.mp4"]') as HTMLInputElement;
|
||||
if (input?.value) {
|
||||
const newVideo = {
|
||||
id: `video-${Date.now()}`,
|
||||
type: "video" as ToolType,
|
||||
x: 100,
|
||||
y: 100,
|
||||
width: 400,
|
||||
height: 300,
|
||||
videoUrl: input.value,
|
||||
};
|
||||
const { addObject } = useEditorStore.getState();
|
||||
addObject(newVideo);
|
||||
input.value = "";
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-sm text-gray-600">
|
||||
<p>آدرس ویدیو را وارد کنید</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default VideoInput;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export { default as ImageUpload } from "./ImageUpload";
|
||||
export { default as DocumentUpload } from "./DocumentUpload";
|
||||
export { default as VideoInput } from "./VideoInput";
|
||||
export { default as LinkInput } from "./LinkInput";
|
||||
export { default as SimpleInstruction } from "./SimpleInstruction";
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { EditorObject } from "../../../store/editorStore";
|
||||
import Input from "@/components/Input";
|
||||
|
||||
type LineSettingsProps = {
|
||||
selectedObject: EditorObject;
|
||||
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
||||
};
|
||||
|
||||
const LineSettings = ({ selectedObject, onUpdate }: LineSettingsProps) => {
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
label="رنگ خط"
|
||||
type="color"
|
||||
value={selectedObject.stroke || "#000000"}
|
||||
onChange={(e) => onUpdate(selectedObject.id, { stroke: e.target.value })}
|
||||
/>
|
||||
<Input
|
||||
label="ضخامت خط"
|
||||
type="number"
|
||||
value={selectedObject.strokeWidth || 2}
|
||||
onChange={(e) =>
|
||||
onUpdate(selectedObject.id, {
|
||||
strokeWidth: parseInt(e.target.value) || 2,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LineSettings;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import type { EditorObject } from "../../../store/editorStore";
|
||||
import Input from "@/components/Input";
|
||||
|
||||
type LinkSettingsProps = {
|
||||
selectedObject: EditorObject;
|
||||
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
||||
};
|
||||
|
||||
const LinkSettings = ({ selectedObject, onUpdate }: LinkSettingsProps) => {
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
label="آدرس لینک"
|
||||
value={selectedObject.linkUrl || ""}
|
||||
onChange={(e) => onUpdate(selectedObject.id, { linkUrl: e.target.value })}
|
||||
placeholder="https://example.com"
|
||||
/>
|
||||
<Input
|
||||
label="متن لینک"
|
||||
value={selectedObject.text || ""}
|
||||
onChange={(e) => onUpdate(selectedObject.id, { text: e.target.value })}
|
||||
/>
|
||||
<Input
|
||||
label="اندازه فونت"
|
||||
type="number"
|
||||
value={selectedObject.fontSize || 24}
|
||||
onChange={(e) =>
|
||||
onUpdate(selectedObject.id, {
|
||||
fontSize: parseInt(e.target.value) || 24,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="رنگ متن"
|
||||
type="color"
|
||||
value={selectedObject.fill || "#0000ff"}
|
||||
onChange={(e) => onUpdate(selectedObject.id, { fill: e.target.value })}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LinkSettings;
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import type { EditorObject } from "../../../store/editorStore";
|
||||
import Input from "@/components/Input";
|
||||
|
||||
type ShapeSettingsProps = {
|
||||
selectedObject: EditorObject;
|
||||
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
||||
};
|
||||
|
||||
const ShapeSettings = ({ selectedObject, onUpdate }: ShapeSettingsProps) => {
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
label="عرض"
|
||||
type="number"
|
||||
value={selectedObject.width || 100}
|
||||
onChange={(e) =>
|
||||
onUpdate(selectedObject.id, {
|
||||
width: parseInt(e.target.value) || 100,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="ارتفاع"
|
||||
type="number"
|
||||
value={selectedObject.height || 100}
|
||||
onChange={(e) =>
|
||||
onUpdate(selectedObject.id, {
|
||||
height: parseInt(e.target.value) || 100,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="رنگ پسزمینه"
|
||||
type="color"
|
||||
value={selectedObject.fill || "#3b82f6"}
|
||||
onChange={(e) => onUpdate(selectedObject.id, { fill: e.target.value })}
|
||||
/>
|
||||
<Input
|
||||
label="رنگ خط"
|
||||
type="color"
|
||||
value={selectedObject.stroke || "#1e40af"}
|
||||
onChange={(e) => onUpdate(selectedObject.id, { stroke: e.target.value })}
|
||||
/>
|
||||
<Input
|
||||
label="ضخامت خط"
|
||||
type="number"
|
||||
value={selectedObject.strokeWidth || 2}
|
||||
onChange={(e) =>
|
||||
onUpdate(selectedObject.id, {
|
||||
strokeWidth: parseInt(e.target.value) || 2,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShapeSettings;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import type { EditorObject } from "../../../store/editorStore";
|
||||
import Input from "@/components/Input";
|
||||
|
||||
type SizeSettingsProps = {
|
||||
selectedObject: EditorObject;
|
||||
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
||||
defaultWidth?: number;
|
||||
defaultHeight?: number;
|
||||
};
|
||||
|
||||
const SizeSettings = ({ selectedObject, onUpdate, defaultWidth = 100, defaultHeight = 100 }: SizeSettingsProps) => {
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
label="عرض"
|
||||
type="number"
|
||||
value={selectedObject.width || defaultWidth}
|
||||
onChange={(e) =>
|
||||
onUpdate(selectedObject.id, {
|
||||
width: parseInt(e.target.value) || defaultWidth,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="ارتفاع"
|
||||
type="number"
|
||||
value={selectedObject.height || defaultHeight}
|
||||
onChange={(e) =>
|
||||
onUpdate(selectedObject.id, {
|
||||
height: parseInt(e.target.value) || defaultHeight,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SizeSettings;
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { EditorObject } from "../../../store/editorStore";
|
||||
import Input from "@/components/Input";
|
||||
|
||||
type TextSettingsProps = {
|
||||
selectedObject: EditorObject;
|
||||
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
||||
};
|
||||
|
||||
const TextSettings = ({ selectedObject, onUpdate }: TextSettingsProps) => {
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
label="متن"
|
||||
value={selectedObject.text || ""}
|
||||
onChange={(e) => onUpdate(selectedObject.id, { text: e.target.value })}
|
||||
/>
|
||||
<Input
|
||||
label="اندازه فونت"
|
||||
type="number"
|
||||
value={selectedObject.fontSize || 24}
|
||||
onChange={(e) =>
|
||||
onUpdate(selectedObject.id, {
|
||||
fontSize: parseInt(e.target.value) || 24,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="رنگ متن"
|
||||
type="color"
|
||||
value={selectedObject.fill || "#000000"}
|
||||
onChange={(e) => onUpdate(selectedObject.id, { fill: e.target.value })}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextSettings;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import type { EditorObject } from "../../../store/editorStore";
|
||||
import Input from "@/components/Input";
|
||||
|
||||
type TransformSettingsProps = {
|
||||
selectedObject: EditorObject;
|
||||
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
||||
};
|
||||
|
||||
const TransformSettings = ({ selectedObject, onUpdate }: TransformSettingsProps) => {
|
||||
return (
|
||||
<div className="pt-4 border-t border-border">
|
||||
<Input
|
||||
label="موقعیت X"
|
||||
type="number"
|
||||
value={Math.round(selectedObject.x)}
|
||||
onChange={(e) =>
|
||||
onUpdate(selectedObject.id, {
|
||||
x: parseInt(e.target.value) || 0,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="موقعیت Y"
|
||||
type="number"
|
||||
value={Math.round(selectedObject.y)}
|
||||
onChange={(e) =>
|
||||
onUpdate(selectedObject.id, {
|
||||
y: parseInt(e.target.value) || 0,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
label="چرخش (درجه)"
|
||||
type="number"
|
||||
value={selectedObject.rotation || 0}
|
||||
onChange={(e) =>
|
||||
onUpdate(selectedObject.id, {
|
||||
rotation: parseInt(e.target.value) || 0,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TransformSettings;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { EditorObject } from "../../../store/editorStore";
|
||||
import Input from "@/components/Input";
|
||||
import SizeSettings from "./SizeSettings";
|
||||
|
||||
type VideoSettingsProps = {
|
||||
selectedObject: EditorObject;
|
||||
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
||||
};
|
||||
|
||||
const VideoSettings = ({ selectedObject, onUpdate }: VideoSettingsProps) => {
|
||||
return (
|
||||
<>
|
||||
<Input
|
||||
label="آدرس ویدیو"
|
||||
value={selectedObject.videoUrl || ""}
|
||||
onChange={(e) => onUpdate(selectedObject.id, { videoUrl: e.target.value })}
|
||||
placeholder="https://example.com/video.mp4"
|
||||
/>
|
||||
<SizeSettings selectedObject={selectedObject} onUpdate={onUpdate} defaultWidth={400} defaultHeight={300} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default VideoSettings;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export { default as TextSettings } from "./TextSettings";
|
||||
export { default as ShapeSettings } from "./ShapeSettings";
|
||||
export { default as LineSettings } from "./LineSettings";
|
||||
export { default as SizeSettings } from "./SizeSettings";
|
||||
export { default as LinkSettings } from "./LinkSettings";
|
||||
export { default as VideoSettings } from "./VideoSettings";
|
||||
export { default as TransformSettings } from "./TransformSettings";
|
||||
|
||||
Reference in New Issue
Block a user