Files
dpage-editor/src/pages/editor/components/sidebar/settings/AlignmentSettings.tsx
T
2026-06-20 11:35:37 +03:30

136 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { ReactNode } from "react";
import {
AlignBottom,
AlignHorizontally,
AlignLeft,
AlignRight,
AlignTop,
AlignVertically,
} from "iconsax-react";
import { useEditorStore, type EditorObject } from "../../../store/editorStore";
import type { AlignKind } from "./alignmentUtils";
import {
deltasForAlign,
pageBounds,
unionBoundsOfAlignTargets,
computeGroupFrameUpdate,
updatesFromDeltas,
} from "./alignmentUtils";
type AlignmentSettingsProps = {
pageWidth: number;
pageHeight: number;
onUpdate: (id: string, updates: Partial<EditorObject>) => void;
};
type AlignButtonGroupProps = {
children: ReactNode;
};
type AlignButtonProps = {
title: string;
onClick: () => void;
children: ReactNode;
};
const AlignButtonGroup = ({ children }: AlignButtonGroupProps) => (
<div className="flex flex-1 overflow-hidden rounded-lg border border-[#E5E5E5] bg-[#F2F2F2] divide-x divide-[#E5E5E5]">
{children}
</div>
);
const AlignButton = ({ title, onClick, children }: AlignButtonProps) => (
<button
type="button"
title={title}
onClick={onClick}
className="flex flex-1 items-center justify-center py-2.5 text-[#333333] transition-colors hover:bg-black/4 active:bg-black/8"
>
{children}
</button>
);
const alignIconProps = { size: 20, color: "currentColor" as const, variant: "Linear" as const };
const AlignmentSettings = ({
pageWidth,
pageHeight,
onUpdate,
}: AlignmentSettingsProps) => {
const applyAlign = (kind: AlignKind) => {
useEditorStore.getState().commitObjectHistoryBeforeChange();
const { objects: objs, selectedObjectIds: ids, layerRef } = useEditorStore.getState();
const layer = layerRef?.current ?? null;
const initial = new Map<string, EditorObject>();
for (const id of ids) {
const o = objs.find((x) => x.id === id);
if (o && o.visible !== false) initial.set(id, { ...o });
}
if (initial.size === 0) return;
const initialList = [...initial.values()];
const useSelectionBounds = ids.length === 2;
const target =
useSelectionBounds && initialList.length === 2
? unionBoundsOfAlignTargets(initialList, objs, layer)
: pageBounds(pageWidth, pageHeight);
if (!target) return;
const alignedGroupIds = new Set<string>();
for (const id of initial.keys()) {
const obj = initial.get(id)!;
const { dx, dy } = deltasForAlign(obj, kind, target, objs, layer);
const updates = updatesFromDeltas(obj, dx, dy);
if (Object.keys(updates).length === 0) continue;
onUpdate(id, updates);
if (obj.type === "group") {
alignedGroupIds.add(id);
}
}
const { updateObject } = useEditorStore.getState();
const afterObjects = useEditorStore.getState().objects;
for (const gid of alignedGroupIds) {
const patch = computeGroupFrameUpdate(gid, afterObjects, layer);
if (patch) updateObject(gid, patch);
}
};
return (
<div className="space-y-3">
<h4 className="text-[13px] text-[#7A7A7A]">ترازبندی</h4>
<div className="flex gap-2">
<AlignButtonGroup>
<AlignButton title="تراز راست" onClick={() => applyAlign("right")}>
<AlignRight {...alignIconProps} />
</AlignButton>
<AlignButton title="تراز افقی وسط" onClick={() => applyAlign("centerH")}>
<AlignHorizontally {...alignIconProps} />
</AlignButton>
<AlignButton title="تراز چپ" onClick={() => applyAlign("left")}>
<AlignLeft {...alignIconProps} />
</AlignButton>
</AlignButtonGroup>
<AlignButtonGroup>
<AlignButton title="تراز بالا" onClick={() => applyAlign("top")}>
<AlignTop {...alignIconProps} />
</AlignButton>
<AlignButton title="تراز عمودی وسط" onClick={() => applyAlign("centerV")}>
<AlignVertically {...alignIconProps} />
</AlignButton>
<AlignButton title="تراز پایین" onClick={() => applyAlign("bottom")}>
<AlignBottom {...alignIconProps} />
</AlignButton>
</AlignButtonGroup>
</div>
</div>
);
};
export default AlignmentSettings;