diff --git a/src/index.js b/src/index.js
index 9be181d5..6dbd3221 100644
--- a/src/index.js
+++ b/src/index.js
@@ -150,148 +150,169 @@ function clearSelection() {
});
}
-function App() {
- const [draggingElement, setDraggingElement] = React.useState(null);
- const [elementType, setElementType] = React.useState("selection");
- const onKeyDown = React.useCallback(event => {
- if (event.key === "Backspace") {
- for (var i = elements.length - 1; i >= 0; --i) {
- if (elements[i].isSelected) {
- elements.splice(i, 1);
+class App extends React.Component {
+ componentDidMount() {
+ this.onKeyDown = event => {
+ if (event.key === "Backspace") {
+ for (var i = elements.length - 1; i >= 0; --i) {
+ if (elements[i].isSelected) {
+ elements.splice(i, 1);
+ }
}
+ drawScene();
+ event.preventDefault();
+ } else if (
+ event.key === "ArrowLeft" ||
+ event.key === "ArrowRight" ||
+ event.key === "ArrowUp" ||
+ event.key === "ArrowDown"
+ ) {
+ const step = event.shiftKey ? 5 : 1;
+ elements.forEach(element => {
+ if (element.isSelected) {
+ if (event.key === "ArrowLeft") element.x -= step;
+ else if (event.key === "ArrowRight") element.x += step;
+ else if (event.key === "ArrowUp") element.y -= step;
+ else if (event.key === "ArrowDown") element.y += step;
+ }
+ });
+ drawScene();
+ event.preventDefault();
}
- drawScene();
- event.preventDefault();
- } else if (
- event.key === "ArrowLeft" ||
- event.key === "ArrowRight" ||
- event.key === "ArrowUp" ||
- event.key === "ArrowDown"
- ) {
- const step = event.shiftKey ? 5 : 1;
- elements.forEach(element => {
- if (element.isSelected) {
- if (event.key === "ArrowLeft") element.x -= step;
- else if (event.key === "ArrowRight") element.x += step;
- else if (event.key === "ArrowUp") element.y -= step;
- else if (event.key === "ArrowDown") element.y += step;
- }
- });
- drawScene();
- event.preventDefault();
- }
- }, []);
- React.useEffect(() => {
- document.addEventListener("keydown", onKeyDown, false);
- return () => {
- document.removeEventListener("keydown", onKeyDown, false);
};
- }, [onKeyDown]);
-
- function ElementOption({ type, children }) {
- return (
-
- {
- setElementType(type);
- clearSelection();
- drawScene();
- }}
- />
- {children}
-
- );
+ document.addEventListener("keydown", this.onKeyDown, false);
}
- return (
-
- {/* Can't use the
form because ElementOption is re-defined
+
+ componentWillUnmount() {
+ document.removeEventListener("keydown", this.onKeyDown, false);
+ }
+
+ constructor() {
+ super();
+ this.state = {
+ draggingElement: null,
+ elementType: "selection"
+ };
+ }
+
+ render() {
+ const ElementOption = ({ type, children }) => {
+ return (
+
+ {
+ this.setState({ elementType: type });
+ clearSelection();
+ drawScene();
+ }}
+ />
+ {children}
+
+ );
+ };
+
+ return (
+
+ {/* Can't use the form because ElementOption is re-defined
on every render, which would blow up and re-create the entire DOM tree,
which in addition to being inneficient, messes up with browser text
selection */}
- {ElementOption({ type: "rectangle", children: "Rectangle" })}
- {ElementOption({ type: "ellipse", children: "Ellipse" })}
- {ElementOption({ type: "arrow", children: "Arrow" })}
- {ElementOption({ type: "text", children: "Text" })}
- {ElementOption({ type: "selection", children: "Selection" })}
- {
- console.log("click");
- }}
- onMouseDown={e => {
- const x = e.clientX - e.target.offsetLeft;
- const y = e.clientY - e.target.offsetTop;
- const element = newElement(elementType, x, y);
+ {ElementOption({ type: "rectangle", children: "Rectangle" })}
+ {ElementOption({ type: "ellipse", children: "Ellipse" })}
+ {ElementOption({ type: "arrow", children: "Arrow" })}
+ {ElementOption({ type: "text", children: "Text" })}
+ {ElementOption({ type: "selection", children: "Selection" })}
+ {
+ const x = e.clientX - e.target.offsetLeft;
+ const y = e.clientY - e.target.offsetTop;
+ const element = newElement(this.state.elementType, x, y);
- if (elementType === "text") {
- const text = prompt("What text do you want?");
- if (text === null) {
- return;
+ if (this.state.elementType === "text") {
+ const text = prompt("What text do you want?");
+ if (text === null) {
+ return;
+ }
+ element.text = text;
+ element.font = "20px Virgil";
+ const font = context.font;
+ context.font = element.font;
+ element.measure = context.measureText(element.text);
+ context.font = font;
+ const height =
+ element.measure.actualBoundingBoxAscent +
+ element.measure.actualBoundingBoxDescent;
+ // Center the text
+ element.x -= element.measure.width / 2;
+ element.y -= element.measure.actualBoundingBoxAscent;
+ element.width = element.measure.width;
+ element.height = height;
}
- element.text = text;
- element.font = "20px Virgil";
- const font = context.font;
- context.font = element.font;
- element.measure = context.measureText(element.text);
- context.font = font;
- const height =
- element.measure.actualBoundingBoxAscent +
- element.measure.actualBoundingBoxDescent;
- // Center the text
- element.x -= element.measure.width / 2;
- element.y -= element.measure.actualBoundingBoxAscent;
- element.width = element.measure.width;
- element.height = height;
- }
- generateDraw(element);
- elements.push(element);
- if (elementType === "text") {
- setDraggingElement(null);
- element.isSelected = true;
- } else {
- setDraggingElement(element);
- }
- drawScene();
- }}
- onMouseUp={e => {
- if (draggingElement === null) {
- return;
- }
- if (elementType === "selection") {
- // Remove actual selection element
- elements.pop();
- setSelection(draggingElement);
- } else {
- draggingElement.isSelected = true;
- }
- setDraggingElement(null);
- setElementType("selection");
- drawScene();
- }}
- onMouseMove={e => {
- if (!draggingElement) return;
- let width = e.clientX - e.target.offsetLeft - draggingElement.x;
- let height = e.clientY - e.target.offsetTop - draggingElement.y;
- draggingElement.width = width;
- // Make a perfect square or circle when shift is enabled
- draggingElement.height = e.shiftKey ? width : height;
+ generateDraw(element);
+ elements.push(element);
+ if (this.state.elementType === "text") {
+ this.setState({ draggingElement: null });
+ element.isSelected = true;
+ } else {
+ this.setState({ draggingElement: element });
+ }
- generateDraw(draggingElement);
+ const onMouseMove = e => {
+ // It is very important to read this.state within each move event,
+ // otherwise we would read a stale one!
+ const draggingElement = this.state.draggingElement;
+ if (!draggingElement) return;
+ let width = e.clientX - e.target.offsetLeft - draggingElement.x;
+ let height = e.clientY - e.target.offsetTop - draggingElement.y;
+ draggingElement.width = width;
+ // Make a perfect square or circle when shift is enabled
+ draggingElement.height = e.shiftKey ? width : height;
- if (elementType === "selection") {
- setSelection(draggingElement);
- }
- drawScene();
- }}
- />
-
- );
+ generateDraw(draggingElement);
+
+ if (this.state.elementType === "selection") {
+ setSelection(draggingElement);
+ }
+ drawScene();
+ };
+
+ const onMouseUp = e => {
+ window.removeEventListener("mousemove", onMouseMove);
+ window.removeEventListener("mouseup", onMouseUp);
+
+ const draggingElement = this.state.draggingElement;
+ if (draggingElement === null) {
+ return;
+ }
+ if (this.state.elementType === "selection") {
+ // Remove actual selection element
+ elements.pop();
+ setSelection(draggingElement);
+ } else {
+ draggingElement.isSelected = true;
+ }
+ this.setState({ draggingElement: null });
+ this.setState({ elementType: "selection" });
+ drawScene();
+ };
+
+ window.addEventListener("mousemove", onMouseMove);
+ window.addEventListener("mouseup", onMouseUp);
+
+ drawScene();
+ }}
+ />
+
+ );
+ }
}
+
const rootElement = document.getElementById("root");
ReactDOM.render( , rootElement);
const canvas = document.getElementById("canvas");