+
{
+ e.preventDefault();
+ if (onCloseRequest) onCloseRequest();
+ }}
+ />
+ {children}
+
+ );
+}
diff --git a/src/index.tsx b/src/index.tsx
index 2cdc5465..f16a59cc 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -36,6 +36,7 @@ import { SHAPES, findShapeByKey, shapesShortcutKeys } from "./shapes";
import { createHistory } from "./history";
import "./styles.scss";
+import ContextMenu from "./components/ContextMenu";
const { elements } = createScene();
const { history } = createHistory();
@@ -147,8 +148,7 @@ class App extends React.Component<{}, AppState> {
this.forceUpdate();
event.preventDefault();
} else if (event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE) {
- deleteSelectedElements(elements);
- this.forceUpdate();
+ this.deleteSelectedElements();
event.preventDefault();
} else if (isArrowKey(event.key)) {
const step = event.shiftKey
@@ -307,6 +307,23 @@ class App extends React.Component<{}, AppState> {
this.setState({ currentItemBackgroundColor: color });
};
+ private copyToClipboard = () => {
+ if (navigator.clipboard) {
+ const text = JSON.stringify(
+ elements.filter(element => element.isSelected)
+ );
+ navigator.clipboard.writeText(text);
+ }
+ };
+
+ private pasteFromClipboard = (x?: number, y?: number) => {
+ if (navigator.clipboard) {
+ navigator.clipboard
+ .readText()
+ .then(text => this.addElementsFromPaste(text, x, y));
+ }
+ };
+
public render() {
const canvasWidth = window.innerWidth - CANVAS_WINDOW_OFFSET_LEFT;
const canvasHeight = window.innerHeight - CANVAS_WINDOW_OFFSET_TOP;
@@ -332,25 +349,7 @@ class App extends React.Component<{}, AppState> {
}}
onPaste={e => {
const paste = e.clipboardData.getData("text");
- let parsedElements;
- try {
- parsedElements = JSON.parse(paste);
- } catch (e) {}
- if (
- Array.isArray(parsedElements) &&
- parsedElements.length > 0 &&
- parsedElements[0].type // need to implement a better check here...
- ) {
- clearSelection(elements);
- parsedElements.forEach(parsedElement => {
- parsedElement.x = 10 - this.state.scrollX;
- parsedElement.y = 10 - this.state.scrollY;
- parsedElement.seed = randomSeed();
- generateDraw(parsedElement);
- elements.push(parsedElement);
- });
- this.forceUpdate();
- }
+ this.addElementsFromPaste(paste);
e.preventDefault();
}}
>
@@ -577,6 +576,54 @@ class App extends React.Component<{}, AppState> {
}
}
}}
+ onContextMenu={e => {
+ e.preventDefault();
+
+ const x =
+ e.clientX - CANVAS_WINDOW_OFFSET_LEFT - this.state.scrollX;
+ const y = e.clientY - CANVAS_WINDOW_OFFSET_TOP - this.state.scrollY;
+
+ const element = getElementAtPosition(elements, x, y);
+ if (!element) {
+ ContextMenu.push({
+ options: [
+ navigator.clipboard && {
+ label: "Paste",
+ action: () => this.pasteFromClipboard(x, y)
+ }
+ ],
+ top: e.clientY,
+ left: e.clientX
+ });
+ return;
+ }
+
+ if (!element.isSelected) {
+ clearSelection(elements);
+ element.isSelected = true;
+ this.forceUpdate();
+ }
+
+ ContextMenu.push({
+ options: [
+ navigator.clipboard && {
+ label: "Copy",
+ action: this.copyToClipboard
+ },
+ navigator.clipboard && {
+ label: "Paste",
+ action: () => this.pasteFromClipboard(x, y)
+ },
+ { label: "Delete", action: this.deleteSelectedElements },
+ { label: "Move Forward", action: this.moveOneRight },
+ { label: "Send to Front", action: this.moveAllRight },
+ { label: "Move Backwards", action: this.moveOneLeft },
+ { label: "Send to Back", action: this.moveAllLeft }
+ ],
+ top: e.clientY,
+ left: e.clientX
+ });
+ }}
onMouseDown={e => {
if (lastMouseUp !== null) {
// Unfortunately, sometimes we don't get a mouseup after a mousedown,
@@ -942,6 +989,40 @@ class App extends React.Component<{}, AppState> {
}));
};
+ private addElementsFromPaste = (paste: string, x?: number, y?: number) => {
+ let parsedElements;
+ try {
+ parsedElements = JSON.parse(paste);
+ } catch (e) {}
+ if (
+ Array.isArray(parsedElements) &&
+ parsedElements.length > 0 &&
+ parsedElements[0].type // need to implement a better check here...
+ ) {
+ clearSelection(elements);
+
+ let dx: number;
+ let dy: number;
+ if (x) {
+ let minX = Math.min(...parsedElements.map(element => element.x));
+ dx = x - minX;
+ }
+ if (y) {
+ let minY = Math.min(...parsedElements.map(element => element.y));
+ dy = y - minY;
+ }
+
+ parsedElements.forEach(parsedElement => {
+ parsedElement.x = dx ? parsedElement.x + dx : 10 - this.state.scrollX;
+ parsedElement.y = dy ? parsedElement.y + dy : 10 - this.state.scrollY;
+ parsedElement.seed = randomSeed();
+ generateDraw(parsedElement);
+ elements.push(parsedElement);
+ });
+ this.forceUpdate();
+ }
+ };
+
componentDidUpdate() {
renderScene(elements, rc, canvas, {
scrollX: this.state.scrollX,
diff --git a/src/styles.scss b/src/styles.scss
index 43f157f1..969174ea 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -149,7 +149,8 @@ button {
border-color: #d6d4d4;
}
- &:active, &.active {
+ &:active,
+ &.active {
background-color: #bdbebc;
border-color: #bdbebc;
}