99 lines
3.2 KiB
TypeScript
99 lines
3.2 KiB
TypeScript
import Select from "@/components/Select";
|
|
import Input from "@/components/Input";
|
|
import type { EditorObject } from "../../../store/editorStore";
|
|
import { useEditorStore } from "../../../store/editorStore";
|
|
import { ENTRANCE_ANIMATION_SELECT_ITEMS } from "../constants/entranceAnimationOptions";
|
|
import {
|
|
DEFAULT_ENTRANCE_DURATION_MS,
|
|
DEFAULT_ENTRANCE_DELAY_MS,
|
|
MAX_ENTRANCE_DELAY_MS,
|
|
} from "@/shared/entranceAnimation";
|
|
|
|
type Props = {
|
|
selectedObject: EditorObject;
|
|
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
|
|
};
|
|
|
|
/** اعضای گروه (نه خود باکس group) برای تنظیم انیمیشن ورود */
|
|
function getAnimationTargets(
|
|
selectedObject: EditorObject,
|
|
objects: EditorObject[],
|
|
): EditorObject[] {
|
|
if (selectedObject.type === "group") {
|
|
return objects.filter(
|
|
(o) => o.groupId === selectedObject.id && o.type !== "group",
|
|
);
|
|
}
|
|
return [selectedObject];
|
|
}
|
|
|
|
const EntranceAnimationSettings = ({ selectedObject, onUpdate }: Props) => {
|
|
const objects = useEditorStore((s) => s.objects);
|
|
const targets = getAnimationTargets(selectedObject, objects);
|
|
const primary = targets[0] ?? selectedObject;
|
|
|
|
const anim = primary.entranceAnimation ?? "none";
|
|
|
|
const duration = primary.entranceDurationMs ?? DEFAULT_ENTRANCE_DURATION_MS;
|
|
const delayMs = primary.entranceDelayMs ?? DEFAULT_ENTRANCE_DELAY_MS;
|
|
|
|
const applyToTargets = (updates: Partial<EditorObject>) => {
|
|
const ids =
|
|
targets.length > 0 ? targets.map((t) => t.id) : [selectedObject.id];
|
|
ids.forEach((id) => onUpdate(id, updates));
|
|
};
|
|
|
|
return (
|
|
<div className="pt-4 flex flex-col gap-4 border-t border-border">
|
|
<Select
|
|
label="انیمیشن هنگام نمایش"
|
|
items={ENTRANCE_ANIMATION_SELECT_ITEMS}
|
|
value={anim}
|
|
onChange={(e) => {
|
|
const v = e.target.value as EditorObject["entranceAnimation"];
|
|
applyToTargets({
|
|
entranceAnimation: v === "none" ? undefined : v,
|
|
...(v === "none" || !v
|
|
? { entranceDurationMs: undefined, entranceDelayMs: undefined }
|
|
: {}),
|
|
});
|
|
}}
|
|
/>
|
|
{anim !== "none" && (
|
|
<>
|
|
<Input
|
|
label="مدت انیمیشن (میلیثانیه)"
|
|
type="number"
|
|
min={100}
|
|
max={4000}
|
|
value={duration}
|
|
onChange={(e) => {
|
|
const n = parseInt(e.target.value, 10);
|
|
if (Number.isNaN(n)) return;
|
|
applyToTargets({
|
|
entranceDurationMs: Math.min(4000, Math.max(100, n)),
|
|
});
|
|
}}
|
|
/>
|
|
<Input
|
|
label="تأخیر قبل از شروع (میلیثانیه)"
|
|
type="number"
|
|
min={0}
|
|
max={MAX_ENTRANCE_DELAY_MS}
|
|
value={delayMs}
|
|
onChange={(e) => {
|
|
const n = parseInt(e.target.value, 10);
|
|
if (Number.isNaN(n)) return;
|
|
const clamped = Math.min(MAX_ENTRANCE_DELAY_MS, Math.max(0, n));
|
|
applyToTargets({
|
|
entranceDelayMs: clamped === 0 ? undefined : clamped,
|
|
});
|
|
}}
|
|
/>
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default EntranceAnimationSettings; |