copy and paste
This commit is contained in:
@@ -7,6 +7,8 @@ export const useKeyboardMovement = () => {
|
|||||||
objects,
|
objects,
|
||||||
updateObject,
|
updateObject,
|
||||||
deleteObject,
|
deleteObject,
|
||||||
|
copySelectedObjects,
|
||||||
|
pasteCopiedObjects,
|
||||||
tool,
|
tool,
|
||||||
layerRef,
|
layerRef,
|
||||||
groupObjects,
|
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) {
|
if (selectedObjectIds.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -122,6 +138,8 @@ export const useKeyboardMovement = () => {
|
|||||||
objects,
|
objects,
|
||||||
updateObject,
|
updateObject,
|
||||||
deleteObject,
|
deleteObject,
|
||||||
|
copySelectedObjects,
|
||||||
|
pasteCopiedObjects,
|
||||||
tool,
|
tool,
|
||||||
layerRef,
|
layerRef,
|
||||||
groupObjects,
|
groupObjects,
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ type EditorStoreType = {
|
|||||||
updateObject: (id: string, updates: Partial<EditorObject>) => void;
|
updateObject: (id: string, updates: Partial<EditorObject>) => void;
|
||||||
deleteObject: (id: string) => void;
|
deleteObject: (id: string) => void;
|
||||||
duplicateObject: (id: string) => void;
|
duplicateObject: (id: string) => void;
|
||||||
|
copySelectedObjects: () => void;
|
||||||
|
pasteCopiedObjects: () => void;
|
||||||
reorderObjects: (draggedId: string, targetId: string) => void;
|
reorderObjects: (draggedId: string, targetId: string) => void;
|
||||||
selectedObjectId: string | null;
|
selectedObjectId: string | null;
|
||||||
setSelectedObjectId: (id: string | null) => void;
|
setSelectedObjectId: (id: string | null) => void;
|
||||||
@@ -104,10 +106,50 @@ type EditorStoreType = {
|
|||||||
groupObjects: (objectIds: string[]) => void;
|
groupObjects: (objectIds: string[]) => void;
|
||||||
ungroupObjects: (groupId: string) => void;
|
ungroupObjects: (groupId: string) => void;
|
||||||
loadPages: (pages: Page[]) => void;
|
loadPages: (pages: Page[]) => void;
|
||||||
|
clipboardObjects: EditorObject[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useEditorStore = create<EditorStoreType>((set, get) => {
|
export const useEditorStore = create<EditorStoreType>((set, get) => {
|
||||||
const initialPage = createInitialPage("جلد");
|
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 {
|
return {
|
||||||
tool: "select",
|
tool: "select",
|
||||||
setTool: (tool) =>
|
setTool: (tool) =>
|
||||||
@@ -117,6 +159,7 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
|
|||||||
})),
|
})),
|
||||||
objects: [],
|
objects: [],
|
||||||
guides: [],
|
guides: [],
|
||||||
|
clipboardObjects: [],
|
||||||
addObject: (object) => {
|
addObject: (object) => {
|
||||||
const state = get();
|
const state = get();
|
||||||
if (state.currentPageId) {
|
if (state.currentPageId) {
|
||||||
@@ -206,29 +249,7 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
|
|||||||
const objectToDuplicate = state.objects.find((obj) => obj.id === id);
|
const objectToDuplicate = state.objects.find((obj) => obj.id === id);
|
||||||
if (!objectToDuplicate) return;
|
if (!objectToDuplicate) return;
|
||||||
|
|
||||||
const duplicatedObject: EditorObject = {
|
const duplicatedObject = cloneObjectsForPaste([objectToDuplicate])[0];
|
||||||
...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()}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (state.currentPageId) {
|
if (state.currentPageId) {
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
@@ -247,6 +268,57 @@ export const useEditorStore = create<EditorStoreType>((set, get) => {
|
|||||||
selectedObjectId: duplicatedObject.id,
|
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) => {
|
reorderObjects: (draggedId, targetId) => {
|
||||||
if (draggedId === targetId) return;
|
if (draggedId === targetId) return;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user