copy and paste

This commit is contained in:
hamid zarghami
2026-04-30 11:28:55 +03:30
parent 8c38d340a4
commit 25585f9e17
2 changed files with 113 additions and 23 deletions
@@ -7,6 +7,8 @@ export const useKeyboardMovement = () => {
objects,
updateObject,
deleteObject,
copySelectedObjects,
pasteCopiedObjects,
tool,
layerRef,
groupObjects,
@@ -57,6 +59,20 @@ export const useKeyboardMovement = () => {
}
}
if ((e.metaKey || e.ctrlKey) && e.key === "c") {
if (selectedObjectIds.length > 0) {
e.preventDefault();
copySelectedObjects();
}
return;
}
if ((e.metaKey || e.ctrlKey) && e.key === "v") {
e.preventDefault();
pasteCopiedObjects();
return;
}
if (selectedObjectIds.length === 0) {
return;
}
@@ -122,6 +138,8 @@ export const useKeyboardMovement = () => {
objects,
updateObject,
deleteObject,
copySelectedObjects,
pasteCopiedObjects,
tool,
layerRef,
groupObjects,
+95 -23
View File
@@ -38,6 +38,8 @@ type EditorStoreType = {
updateObject: (id: string, updates: Partial<EditorObject>) => void;
deleteObject: (id: string) => void;
duplicateObject: (id: string) => void;
copySelectedObjects: () => void;
pasteCopiedObjects: () => void;
reorderObjects: (draggedId: string, targetId: string) => void;
selectedObjectId: string | null;
setSelectedObjectId: (id: string | null) => void;
@@ -104,10 +106,50 @@ type EditorStoreType = {
groupObjects: (objectIds: string[]) => void;
ungroupObjects: (groupId: string) => void;
loadPages: (pages: Page[]) => void;
clipboardObjects: EditorObject[];
};
export const useEditorStore = create<EditorStoreType>((set, get) => {
const initialPage = createInitialPage("جلد");
const cloneObjectsForPaste = (
sourceObjects: EditorObject[],
offset = 20,
): EditorObject[] => {
if (sourceObjects.length === 0) return [];
const sourceIds = new Set(sourceObjects.map((obj) => obj.id));
const idMap = new Map<string, string>();
sourceObjects.forEach((obj) => {
idMap.set(obj.id, `${obj.id}-${crypto.randomUUID()}`);
});
return sourceObjects.map((objectToClone) => ({
...objectToClone,
id: idMap.get(objectToClone.id)!,
x: (objectToClone.x || 0) + offset,
y: (objectToClone.y || 0) + offset,
groupId:
objectToClone.groupId && sourceIds.has(objectToClone.groupId)
? idMap.get(objectToClone.groupId)
: undefined,
...(objectToClone.tableData && {
tableData: {
...objectToClone.tableData,
id: `table-${crypto.randomUUID()}`,
cells: Object.fromEntries(
Object.entries(objectToClone.tableData.cells).map(([key, cell]) => [
key,
{
...cell,
id: `cell-${crypto.randomUUID()}`,
},
]),
),
},
}),
}));
};
return {
tool: "select",
setTool: (tool) =>
@@ -117,6 +159,7 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
})),
objects: [],
guides: [],
clipboardObjects: [],
addObject: (object) => {
const state = get();
if (state.currentPageId) {
@@ -206,29 +249,7 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
const objectToDuplicate = state.objects.find((obj) => obj.id === id);
if (!objectToDuplicate) return;
const duplicatedObject: EditorObject = {
...objectToDuplicate,
id: `${objectToDuplicate.id}-${crypto.randomUUID()}`,
x: (objectToDuplicate.x || 0) + 20,
y: (objectToDuplicate.y || 0) + 20,
...(objectToDuplicate.tableData && {
tableData: {
...objectToDuplicate.tableData,
id: `table-${crypto.randomUUID()}`,
cells: Object.fromEntries(
Object.entries(objectToDuplicate.tableData.cells).map(
([key, cell]) => [
key,
{
...cell,
id: `cell-${crypto.randomUUID()}`,
},
],
),
),
},
}),
};
const duplicatedObject = cloneObjectsForPaste([objectToDuplicate])[0];
if (state.currentPageId) {
set((state) => ({
@@ -247,6 +268,57 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
selectedObjectId: duplicatedObject.id,
}));
},
copySelectedObjects: () => {
const state = get();
const selectedIds =
state.selectedObjectIds.length > 0
? state.selectedObjectIds
: state.selectedObjectId
? [state.selectedObjectId]
: [];
if (selectedIds.length === 0) return;
const selectedIdSet = new Set(selectedIds);
const groupsToCopy = new Set(
state.objects
.filter((obj) => selectedIdSet.has(obj.id) && obj.type === "group")
.map((obj) => obj.id),
);
const objectsToCopy = state.objects.filter(
(obj) => selectedIdSet.has(obj.id) || groupsToCopy.has(obj.groupId || ""),
);
if (objectsToCopy.length === 0) return;
set({ clipboardObjects: objectsToCopy });
},
pasteCopiedObjects: () => {
const state = get();
if (state.clipboardObjects.length === 0) return;
const pastedObjects = cloneObjectsForPaste(state.clipboardObjects);
const pastedIds = pastedObjects.map((obj) => obj.id);
if (state.currentPageId) {
set((state) => ({
objects: [...state.objects, ...pastedObjects],
pages: state.pages.map((p) =>
p.id === state.currentPageId
? { ...p, objects: [...p.objects, ...pastedObjects] }
: p,
),
selectedObjectId: pastedIds[0] ?? null,
selectedObjectIds: pastedIds,
}));
return;
}
set((state) => ({
objects: [...state.objects, ...pastedObjects],
selectedObjectId: pastedIds[0] ?? null,
selectedObjectIds: pastedIds,
}));
},
reorderObjects: (draggedId, targetId) => {
if (draggedId === targetId) return;