Skip to content

Commit f4a5d47

Browse files
Merge branch 'vnext' into feature/e-add-tableComment
2 parents d189dcf + c4bd05e commit f4a5d47

File tree

15 files changed

+219
-1
lines changed

15 files changed

+219
-1
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export const CopyIcon = () => {
2+
return (
3+
<svg
4+
xmlns="http://www.w3.org/2000/svg"
5+
width="1.2em"
6+
height="1.2em"
7+
viewBox="0 0 256 256"
8+
>
9+
<path
10+
fill="currentColor"
11+
d="M216 32H88a8 8 0 0 0-8 8v40H40a8 8 0 0 0-8 8v128a8 8 0 0 0 8 8h128a8 8 0 0 0 8-8v-40h40a8 8 0 0 0 8-8V40a8 8 0 0 0-8-8m-56 176H48V96h112Zm48-48h-32V88a8 8 0 0 0-8-8H96V48h112Z"
12+
/>
13+
</svg>
14+
);
15+
};

src/common/components/icons/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ export * from './add-folder.component';
2323
export * from './down-icon';
2424
export * from './up-icon.component';
2525
export * from './remove-icon.component';
26+
export * from './copy-icon.component';
27+
export * from './paste-icon.component';
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export const PasteIcon = () => {
2+
return (
3+
<svg
4+
xmlns="http://www.w3.org/2000/svg"
5+
width="1.2em"
6+
height="1.2em"
7+
viewBox="0 0 256 256"
8+
>
9+
<path
10+
fill="currentColor"
11+
d="M200 32h-36.26a47.92 47.92 0 0 0-71.48 0H56a16 16 0 0 0-16 16v168a16 16 0 0 0 16 16h144a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16m-72 0a32 32 0 0 1 32 32H96a32 32 0 0 1 32-32m72 184H56V48h26.75A47.9 47.9 0 0 0 80 64v8a8 8 0 0 0 8 8h80a8 8 0 0 0 8-8v-8a47.9 47.9 0 0 0-2.75-16H200Z"
12+
/>
13+
</svg>
14+
);
15+
};

src/core/providers/canvas-schema/canvas-schema-vlatest.model.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,8 @@ export interface CanvasSchemaContextVm {
9898
updateFullRelation: (relation: RelationVm) => void;
9999
deleteSelectedItem: (selectedElementId: GUID) => void;
100100
switchIsPristine: (isPristine: boolean) => void;
101+
duplicateSelectedTable: () => void;
102+
copySelectedTable: () => void;
103+
pasteTable: () => void;
104+
hasClipboardContent: boolean;
101105
}

src/core/providers/canvas-schema/canvas-schema.provider.tsx

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import { produce } from 'immer';
33
import { CanvasSchemaContext } from './canvas-schema.context';
44
import {
@@ -178,6 +178,75 @@ export const CanvasSchemaProvider: React.FC<Props> = props => {
178178
}));
179179
};
180180

181+
const duplicateSelectedTable = () => {
182+
setSchema(prevSchema => {
183+
if (!prevSchema.selectedElementId) return prevSchema;
184+
185+
const selectedTable = prevSchema.tables.find(
186+
table => table.id === prevSchema.selectedElementId
187+
);
188+
189+
if (!selectedTable) return prevSchema;
190+
191+
// Create duplicate with new IDs
192+
const duplicateTable: TableVm = {
193+
...selectedTable,
194+
id: crypto.randomUUID(), // Generate new ID
195+
x: selectedTable.x + 50, // Offset position
196+
y: selectedTable.y + 50,
197+
fields: selectedTable.fields.map(field => ({
198+
...field,
199+
id: crypto.randomUUID(), // New IDs for fields
200+
})),
201+
};
202+
203+
return {
204+
...prevSchema,
205+
tables: [...prevSchema.tables, duplicateTable],
206+
isPristine: false,
207+
};
208+
});
209+
};
210+
211+
const [clipboardTable, setClipboardTable] = useState<TableVm | null>(null);
212+
const [pasteOffset, setPasteOffset] = useState({ x: 0, y: 0 });
213+
214+
const copySelectedTable = () => {
215+
const selectedTable = canvasSchema.tables.find(
216+
table => table.id === canvasSchema.selectedElementId
217+
);
218+
if (selectedTable) {
219+
setClipboardTable(selectedTable);
220+
setPasteOffset({ x: 0, y: 0 }); // Reset offset on copy
221+
}
222+
};
223+
224+
const pasteTable = () => {
225+
if (clipboardTable) {
226+
// Increment offset
227+
const newOffset = { x: pasteOffset.x + 50, y: pasteOffset.y + 50 };
228+
229+
const newTable: TableVm = {
230+
...clipboardTable,
231+
id: crypto.randomUUID(),
232+
x: clipboardTable.x + newOffset.x,
233+
y: clipboardTable.y + newOffset.y,
234+
fields: clipboardTable.fields.map(field => ({
235+
...field,
236+
id: crypto.randomUUID(),
237+
})),
238+
};
239+
240+
setSchema(prev => ({
241+
...prev,
242+
tables: [...prev.tables, newTable],
243+
isPristine: false,
244+
}));
245+
246+
setPasteOffset(newOffset);
247+
}
248+
};
249+
181250
return (
182251
<CanvasSchemaContext.Provider
183252
value={{
@@ -200,6 +269,10 @@ export const CanvasSchemaProvider: React.FC<Props> = props => {
200269
updateFullRelation,
201270
deleteSelectedItem,
202271
switchIsPristine: switchIsPristine,
272+
duplicateSelectedTable,
273+
copySelectedTable,
274+
pasteTable,
275+
hasClipboardContent: Boolean(clipboardTable),
203276
}}
204277
>
205278
{children}

src/pods/canvas/canvas.pod.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ export const CanvasPod: React.FC = () => {
4646
doRedo,
4747
deleteSelectedItem,
4848
loadSchema,
49+
duplicateSelectedTable,
50+
copySelectedTable,
51+
pasteTable,
4952
} = useCanvasSchemaContext();
5053
const {
5154
canvasViewSettings,
@@ -247,6 +250,24 @@ export const CanvasPod: React.FC = () => {
247250
deleteSelectedItem(canvasSchema.selectedElementId);
248251
}
249252
}
253+
254+
// Add Cmd/Ctrl+D for duplicate
255+
if ((e.metaKey || e.ctrlKey) && e.key === 'd') {
256+
e.preventDefault(); // Prevent browser default
257+
duplicateSelectedTable();
258+
}
259+
260+
// Add Cmd/Ctrl+C for copy
261+
if ((e.metaKey || e.ctrlKey) && e.key === 'c') {
262+
e.preventDefault(); // Prevent browser default
263+
copySelectedTable();
264+
}
265+
266+
// Add Cmd/Ctrl+V for paste
267+
if ((e.metaKey || e.ctrlKey) && e.key === 'v') {
268+
e.preventDefault(); // Prevent browser default
269+
pasteTable();
270+
}
250271
};
251272

252273
modalDialog.isOpen
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { useCanvasSchemaContext } from '@/core/providers/canvas-schema';
2+
import { CopyIcon } from '@/common/components/icons';
3+
import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button';
4+
import classes from '@/pods/toolbar/toolbar.pod.module.css';
5+
import { SHORTCUTS } from '../../shortcut/shortcut.const';
6+
7+
export const CopyButton = () => {
8+
const { canvasSchema, copySelectedTable } = useCanvasSchemaContext();
9+
10+
return (
11+
<ToolbarButton
12+
icon={<CopyIcon />}
13+
label="Copy"
14+
onClick={copySelectedTable}
15+
className={`${classes.button} hide-mobile`}
16+
disabled={!canvasSchema.selectedElementId}
17+
shortcutOptions={SHORTCUTS.copy}
18+
/>
19+
);
20+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './copy-button.component';
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { useCanvasSchemaContext } from '@/core/providers/canvas-schema';
2+
import { CopyIcon } from '@/common/components/icons';
3+
import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button';
4+
import classes from '@/pods/toolbar/toolbar.pod.module.css';
5+
import { SHORTCUTS } from '../../shortcut/shortcut.const';
6+
7+
export const DuplicateButton = () => {
8+
const { canvasSchema, duplicateSelectedTable } = useCanvasSchemaContext();
9+
10+
return (
11+
<ToolbarButton
12+
icon={<CopyIcon />}
13+
label="Duplicate Table"
14+
onClick={duplicateSelectedTable}
15+
className={`${classes.button} hide-mobile`}
16+
disabled={!canvasSchema.selectedElementId}
17+
shortcutOptions={SHORTCUTS.duplicate}
18+
/>
19+
);
20+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './duplicate-button.component';

0 commit comments

Comments
 (0)