diff --git a/src/index.tsx b/src/index.tsx index a2ec4ae2..e6492a68 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -10,7 +10,7 @@ type ExcaliburTextElement = ExcaliburElement & { type: "text"; font: string; text: string; - measure: TextMetrics; + actualBoundingBoxAscent: number; }; var elements = Array.of(); @@ -205,7 +205,7 @@ function generateDraw(element: ExcaliburElement) { context.fillText( element.text, element.x, - element.y + element.measure.actualBoundingBoxAscent + element.y + element.actualBoundingBoxAscent ); context.font = font; }; @@ -256,6 +256,14 @@ function clearSelection() { }); } +function deleteSelectedElements() { + for (var i = elements.length - 1; i >= 0; --i) { + if (elements[i].isSelected) { + elements.splice(i, 1); + } + } +} + type AppState = { draggingElement: ExcaliburElement | null; elementType: string; @@ -286,11 +294,7 @@ class App extends React.Component<{}, AppState> { event.key === "Backspace" && (event.target as HTMLElement).nodeName !== "INPUT" ) { - for (var i = elements.length - 1; i >= 0; --i) { - if (elements[i].isSelected) { - elements.splice(i, 1); - } - } + deleteSelectedElements(); drawScene(); event.preventDefault(); } else if ( @@ -382,7 +386,46 @@ class App extends React.Component<{}, AppState> { /> px) -
+
{ + e.clipboardData.setData( + "text/plain", + JSON.stringify(elements.filter(element => element.isSelected)) + ); + deleteSelectedElements(); + drawScene(); + e.preventDefault(); + }} + onCopy={e => { + e.clipboardData.setData( + "text/plain", + JSON.stringify(elements.filter(element => element.isSelected)) + ); + e.preventDefault(); + }} + 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(); + parsedElements.forEach(parsedElement => { + parsedElement.x += 10; + parsedElement.y += 10; + generateDraw(parsedElement); + elements.push(parsedElement); + }); + drawScene(); + } + e.preventDefault(); + }} + > {this.renderOption({ type: "rectangle", children: "Rectangle" })} {this.renderOption({ type: "ellipse", children: "Ellipse" })} {this.renderOption({ type: "arrow", children: "Arrow" })} @@ -431,15 +474,19 @@ class App extends React.Component<{}, AppState> { element.font = "20px Virgil"; const font = context.font; context.font = element.font; - element.measure = context.measureText(element.text); + const { + actualBoundingBoxAscent, + actualBoundingBoxDescent, + width + } = context.measureText(element.text); + element.actualBoundingBoxAscent = actualBoundingBoxAscent; context.font = font; const height = - element.measure.actualBoundingBoxAscent + - element.measure.actualBoundingBoxDescent; + actualBoundingBoxAscent + actualBoundingBoxDescent; // Center the text - element.x -= element.measure.width / 2; - element.y -= element.measure.actualBoundingBoxAscent; - element.width = element.measure.width; + element.x -= width / 2; + element.y -= actualBoundingBoxAscent; + element.width = width; element.height = height; }