Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#140 - Added Copy/Paste Button #241

Merged
merged 4 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions public/icons/copy.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/icons/paste.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions src/common/components/icons/copy-icon.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const CopyIcon = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 256 256"
>
<path
fill="currentColor"
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"
/>
</svg>
);
};
2 changes: 2 additions & 0 deletions src/common/components/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ export * from './send-to-back-icon.component';
export * from './linkedin-icon.component';
export * from './x-icon.component';
export * from './quickmock-logo.component';
export * from './copy-icon.component';
export * from './paste-icon.component';
15 changes: 15 additions & 0 deletions src/common/components/icons/paste-icon.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const PasteIcon = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 256 256"
>
<path
fill="currentColor"
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"
/>
</svg>
);
};
5 changes: 4 additions & 1 deletion src/core/providers/canvas/canvas.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export interface CanvasContextModel {
scale: number;
clearCanvas: () => void;
setScale: React.Dispatch<React.SetStateAction<number>>;
pasteShape: (shape: ShapeModel) => void;
addNewShape: (
type: ShapeType,
x: number,
Expand All @@ -54,6 +53,10 @@ export interface CanvasContextModel {
canRedo: () => boolean;
doUndo: () => void;
doRedo: () => void;
canCopy: boolean;
canPaste: boolean;
copyShapeToClipboard: () => void;
pasteShapeFromClipboard: () => void;
}

export interface DocumentModel {
Expand Down
27 changes: 17 additions & 10 deletions src/core/providers/canvas/canvas.provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { createDefaultDocumentModel, DocumentModel } from './canvas.model';
import { v4 as uuidv4 } from 'uuid';
import Konva from 'konva';
import { removeShapeFromList } from './canvas.business';
import { useClipboard } from './use-clipboard.hook';

interface Props {
children: React.ReactNode;
Expand Down Expand Up @@ -37,6 +38,18 @@ export const CanvasProvider: React.FC<Props> = props => {

const selectionInfo = useSelection(document, setDocument);

const pasteShape = (shape: ShapeModel) => {
shape.id = uuidv4();

setDocument(prevDocument => ({
...prevDocument,
shapes: [...prevDocument.shapes, shape],
}));
};

const { copyShapeToClipboard, pasteShapeFromClipboard, canCopy, canPaste } =
useClipboard(pasteShape, document.shapes, selectionInfo);

const clearCanvas = () => {
setDocument({ shapes: [] });
};
Expand All @@ -51,15 +64,6 @@ export const CanvasProvider: React.FC<Props> = props => {
}));
};

const pasteShape = (shape: ShapeModel) => {
shape.id = uuidv4();

setDocument(prevDocument => ({
...prevDocument,
shapes: [...prevDocument.shapes, shape],
}));
};

// TODO: instenad of x,y use Coord and reduce the number of arguments
const addNewShape = (
type: ShapeType,
Expand Down Expand Up @@ -130,13 +134,16 @@ export const CanvasProvider: React.FC<Props> = props => {
clearCanvas,
selectionInfo,
addNewShape,
pasteShape,
updateShapeSizeAndPosition,
updateShapePosition,
canUndo,
canRedo,
doUndo,
doRedo,
canCopy,
canPaste,
copyShapeToClipboard,
pasteShapeFromClipboard,
stageRef,
deleteSelectedShape,
}}
Expand Down
50 changes: 50 additions & 0 deletions src/core/providers/canvas/use-clipboard.hook.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useMemo, useRef, useState } from 'react';
import { ShapeModel } from '@/core/model';
import {
adjustShapePosition,
cloneShape,
findShapeById,
validateShape,
} from '../../../pods/canvas/clipboard.utils';

export const useClipboard = (
pasteShape: (shape: ShapeModel) => void,
shapes: ShapeModel[],
selectionInfo: { selectedShapeId: string | null }
) => {
const [clipboardShape, setClipboardShape] = useState<ShapeModel | null>(null);
const clipboardShapeRef = useRef<ShapeModel | null>(null);
const copyCount = useRef(1);

const copyShapeToClipboard = () => {
const selectedShape = findShapeById(
selectionInfo.selectedShapeId ?? '',
shapes
);
if (selectedShape) {
clipboardShapeRef.current = cloneShape(selectedShape);
setClipboardShape(clipboardShapeRef.current);
copyCount.current = 1;
}
};

const pasteShapeFromClipboard = () => {
if (clipboardShapeRef.current) {
const newShape: ShapeModel = cloneShape(clipboardShapeRef.current);
validateShape(newShape);
adjustShapePosition(newShape, copyCount.current);
pasteShape(newShape);
copyCount.current++;
}
};

const canCopy: boolean = useMemo(() => {
return !!selectionInfo.selectedShapeId;
}, [selectionInfo.selectedShapeId]);

const canPaste: boolean = useMemo(() => {
return clipboardShapeRef.current !== null;
}, [clipboardShape]);

return { copyShapeToClipboard, pasteShapeFromClipboard, canCopy, canPaste };
};
26 changes: 4 additions & 22 deletions src/pods/canvas/canvas.pod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { useDropShape } from './use-drop-shape.hook';
import { useMonitorShape } from './use-monitor-shape.hook';
import classes from './canvas.pod.module.css';
import { EditableComponent } from '@/common/components/inline-edit';
import { useClipboard } from './use-clipboard.hook';
import { useSnapIn } from './use-snapin.hook';
import { ShapeType } from '@/core/model';
import { useDropImageFromDesktop } from './use-drop-image-from-desktop';
Expand All @@ -25,6 +24,10 @@ export const CanvasPod = () => {
updateShapeSizeAndPosition,
updateShapePosition,
stageRef,
canCopy,
canPaste,
copyShapeToClipboard,
pasteShapeFromClipboard,
} = useCanvasContext();

const {
Expand Down Expand Up @@ -88,27 +91,6 @@ export const CanvasPod = () => {
updateShapePosition(id, { x, y });
};

const { copyShape, pasteShapeFromClipboard } = useClipboard();

useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
const isCtrlOrCmdPressed = e.ctrlKey || e.metaKey;

if (isCtrlOrCmdPressed && e.key === 'c') {
copyShape();
}
if (isCtrlOrCmdPressed && e.key === 'v') {
pasteShapeFromClipboard();
}
};

window.addEventListener('keydown', handleKeyDown);

return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [selectedShapeId]);

{
/* TODO: add other animation for isDraggerOver */
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ export const renderBreadcrumb = (
const { handleSelected, shapeRefs, handleDragEnd, handleTransform } =
shapeRenderedProps;

console.log(shapeRefs.current[shape.id]);

return (
<BreadcrumbShape
id={shape.id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ export const renderHeading1 = (
const { handleSelected, shapeRefs, handleDragEnd, handleTransform } =
shapeRenderedProps;

console.log(shapeRefs.current[shape.id]);

return (
<Heading1Shape
id={shape.id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ export const renderHeading2 = (
const { handleSelected, shapeRefs, handleDragEnd, handleTransform } =
shapeRenderedProps;

console.log(shapeRefs.current[shape.id]);

return (
<Heading2Shape
id={shape.id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ export const renderHeading3 = (
const { handleSelected, shapeRefs, handleDragEnd, handleTransform } =
shapeRenderedProps;

console.log(shapeRefs.current[shape.id]);

return (
<Heading3Shape
id={shape.id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ export const renderNormaltext = (
const { handleSelected, shapeRefs, handleDragEnd, handleTransform } =
shapeRenderedProps;

console.log(shapeRefs.current[shape.id]);

return (
<NormaltextShape
id={shape.id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ export const renderParagraph = (
const { handleSelected, shapeRefs, handleDragEnd, handleTransform } =
shapeRenderedProps;

console.log(shapeRefs.current[shape.id]);

return (
<ParagraphShape
id={shape.id}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ export const renderSmalltext = (
const { handleSelected, shapeRefs, handleDragEnd, handleTransform } =
shapeRenderedProps;

console.log(shapeRefs.current[shape.id]);

return (
<SmalltextShape
id={shape.id}
Expand Down
35 changes: 0 additions & 35 deletions src/pods/canvas/use-clipboard.hook.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { CopyIcon, PasteIcon } from '@/common/components/icons';
import { ToolbarButton } from '../toolbar-button';
import classes from '@/pods/toolbar/toolbar.pod.module.css';
import { useCanvasContext } from '@/core/providers';
import { SHORTCUTS } from '../../shortcut/shortcut.const';

export const CopyButton = () => {
const { canCopy, canPaste, copyShapeToClipboard, pasteShapeFromClipboard } =
useCanvasContext();

const handleCopyClick = () => {
if (canCopy) {
copyShapeToClipboard();
}
};

const handlePasteClick = () => {
if (canPaste) {
pasteShapeFromClipboard();
}
};

return (
<div>
<ul className={classes.buttonGroup}>
<li>
<ToolbarButton
icon={<CopyIcon />}
label="Copy"
onClick={handleCopyClick}
className={classes.button}
disabled={!canCopy}
shortcutOptions={SHORTCUTS.copy}
/>
</li>
<li>
<ToolbarButton
icon={<PasteIcon />}
label="Paste"
onClick={handlePasteClick}
className={classes.button}
disabled={!canPaste} // Disable the button if the clipboard is not filled
shortcutOptions={SHORTCUTS.paste}
/>
</li>
</ul>
</div>
);
};
1 change: 1 addition & 0 deletions src/pods/toolbar/components/copy-paste-button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './copy-paste-button';
12 changes: 12 additions & 0 deletions src/pods/toolbar/shortcut/shortcut.const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,16 @@ export const SHORTCUTS: Shortcut = {
targetKey: ['Backspace'],
targetKeyLabel: 'Backspace',
},
copy: {
description: 'Copy',
id: 'copy-button-shortcut',
targetKey: ['c'],
targetKeyLabel: 'Ctrl + C',
},
paste: {
description: 'Paste',
id: 'paste-button-shortcut',
targetKey: ['v'],
targetKeyLabel: 'Ctrl + V',
},
};
Loading
Loading