|
|
|
|
@ -10,7 +10,8 @@ import {
@@ -10,7 +10,8 @@ import {
|
|
|
|
|
duplicateElement, |
|
|
|
|
resizeTest, |
|
|
|
|
isTextElement, |
|
|
|
|
textWysiwyg |
|
|
|
|
textWysiwyg, |
|
|
|
|
getElementAbsoluteCoords |
|
|
|
|
} from "./element"; |
|
|
|
|
import { |
|
|
|
|
clearSelection, |
|
|
|
|
@ -115,6 +116,7 @@ export class App extends React.Component<{}, AppState> {
@@ -115,6 +116,7 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
|
|
|
|
|
public componentDidMount() { |
|
|
|
|
document.addEventListener("keydown", this.onKeyDown, false); |
|
|
|
|
document.addEventListener("mousemove", this.getCurrentCursorPosition); |
|
|
|
|
window.addEventListener("resize", this.onResize, false); |
|
|
|
|
|
|
|
|
|
const savedState = restoreFromLocalStorage(elements); |
|
|
|
|
@ -125,6 +127,11 @@ export class App extends React.Component<{}, AppState> {
@@ -125,6 +127,11 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
|
|
|
|
|
public componentWillUnmount() { |
|
|
|
|
document.removeEventListener("keydown", this.onKeyDown, false); |
|
|
|
|
document.removeEventListener( |
|
|
|
|
"mousemove", |
|
|
|
|
this.getCurrentCursorPosition, |
|
|
|
|
false |
|
|
|
|
); |
|
|
|
|
window.removeEventListener("resize", this.onResize, false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -139,6 +146,8 @@ export class App extends React.Component<{}, AppState> {
@@ -139,6 +146,8 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
viewBackgroundColor: "#ffffff", |
|
|
|
|
scrollX: 0, |
|
|
|
|
scrollY: 0, |
|
|
|
|
cursorX: 0, |
|
|
|
|
cursorY: 0, |
|
|
|
|
name: DEFAULT_PROJECT_NAME |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
@ -146,6 +155,10 @@ export class App extends React.Component<{}, AppState> {
@@ -146,6 +155,10 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
this.forceUpdate(); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
private getCurrentCursorPosition = (e: MouseEvent) => { |
|
|
|
|
this.setState({ cursorX: e.x, cursorY: e.y }); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
private onKeyDown = (event: KeyboardEvent) => { |
|
|
|
|
if (isInputLike(event.target)) return; |
|
|
|
|
|
|
|
|
|
@ -263,7 +276,7 @@ export class App extends React.Component<{}, AppState> {
@@ -263,7 +276,7 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
element.fillStyle = pastedElement?.fillStyle; |
|
|
|
|
element.opacity = pastedElement?.opacity; |
|
|
|
|
element.roughness = pastedElement?.roughness; |
|
|
|
|
if(isTextElement(element)) { |
|
|
|
|
if (isTextElement(element)) { |
|
|
|
|
element.font = pastedElement?.font; |
|
|
|
|
this.redrawTextBoundingBox(element); |
|
|
|
|
} |
|
|
|
|
@ -331,11 +344,11 @@ export class App extends React.Component<{}, AppState> {
@@ -331,11 +344,11 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
private pasteFromClipboard = (x?: number, y?: number) => { |
|
|
|
|
private pasteFromClipboard = () => { |
|
|
|
|
if (navigator.clipboard) { |
|
|
|
|
navigator.clipboard |
|
|
|
|
.readText() |
|
|
|
|
.then(text => this.addElementsFromPaste(text, x, y)); |
|
|
|
|
.then(text => this.addElementsFromPaste(text)); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
@ -345,7 +358,7 @@ export class App extends React.Component<{}, AppState> {
@@ -345,7 +358,7 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
element.height = metrics.height; |
|
|
|
|
element.baseline = metrics.baseline; |
|
|
|
|
this.forceUpdate(); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
public render() { |
|
|
|
|
const canvasWidth = window.innerWidth - CANVAS_WINDOW_OFFSET_LEFT; |
|
|
|
|
@ -480,39 +493,45 @@ export class App extends React.Component<{}, AppState> {
@@ -480,39 +493,45 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
<h5>Font size</h5> |
|
|
|
|
<ButtonSelect |
|
|
|
|
options={[ |
|
|
|
|
{ value: 16, text: "Small" },
|
|
|
|
|
{ value: 16, text: "Small" }, |
|
|
|
|
{ value: 20, text: "Medium" }, |
|
|
|
|
{ value: 28, text: "Large" }, |
|
|
|
|
{ value: 36, text: "Very Large" } |
|
|
|
|
]} |
|
|
|
|
value={getSelectedAttribute( |
|
|
|
|
elements, |
|
|
|
|
element => isTextElement(element) && +element.font.split("px ")[0] |
|
|
|
|
element => |
|
|
|
|
isTextElement(element) && +element.font.split("px ")[0] |
|
|
|
|
)} |
|
|
|
|
onChange={value => |
|
|
|
|
this.changeProperty(element => { |
|
|
|
|
if(isTextElement(element)) { |
|
|
|
|
element.font = `${value}px ${element.font.split("px ")[1]}`; |
|
|
|
|
if (isTextElement(element)) { |
|
|
|
|
element.font = `${value}px ${ |
|
|
|
|
element.font.split("px ")[1] |
|
|
|
|
}`;
|
|
|
|
|
this.redrawTextBoundingBox(element); |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
/> |
|
|
|
|
<h5>Font familly</h5> |
|
|
|
|
<ButtonSelect
|
|
|
|
|
<ButtonSelect |
|
|
|
|
options={[ |
|
|
|
|
{value: "Virgil", text: "Virgil"}, |
|
|
|
|
{value: "Helvetica", text: "Helvetica"}, |
|
|
|
|
{value: "Courier", text: "Courier"}, |
|
|
|
|
{ value: "Virgil", text: "Virgil" }, |
|
|
|
|
{ value: "Helvetica", text: "Helvetica" }, |
|
|
|
|
{ value: "Courier", text: "Courier" } |
|
|
|
|
]} |
|
|
|
|
value={getSelectedAttribute( |
|
|
|
|
elements, |
|
|
|
|
element => isTextElement(element) && element.font.split("px ")[1] |
|
|
|
|
element => |
|
|
|
|
isTextElement(element) && element.font.split("px ")[1] |
|
|
|
|
)} |
|
|
|
|
onChange={value => |
|
|
|
|
this.changeProperty(element => { |
|
|
|
|
if(isTextElement(element)) { |
|
|
|
|
element.font = `${element.font.split("px ")[0]}px ${value}`; |
|
|
|
|
if (isTextElement(element)) { |
|
|
|
|
element.font = `${ |
|
|
|
|
element.font.split("px ")[0] |
|
|
|
|
}px ${value}`;
|
|
|
|
|
this.redrawTextBoundingBox(element); |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
@ -609,7 +628,7 @@ export class App extends React.Component<{}, AppState> {
@@ -609,7 +628,7 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
options: [ |
|
|
|
|
navigator.clipboard && { |
|
|
|
|
label: "Paste", |
|
|
|
|
action: () => this.pasteFromClipboard(x, y) |
|
|
|
|
action: () => this.pasteFromClipboard() |
|
|
|
|
} |
|
|
|
|
], |
|
|
|
|
top: e.clientY, |
|
|
|
|
@ -632,7 +651,7 @@ export class App extends React.Component<{}, AppState> {
@@ -632,7 +651,7 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
}, |
|
|
|
|
navigator.clipboard && { |
|
|
|
|
label: "Paste", |
|
|
|
|
action: () => this.pasteFromClipboard(x, y) |
|
|
|
|
action: () => this.pasteFromClipboard() |
|
|
|
|
}, |
|
|
|
|
{ label: "Copy Styles", action: this.copyStyles }, |
|
|
|
|
{ label: "Paste Styles", action: this.pasteStyles }, |
|
|
|
|
@ -1105,7 +1124,7 @@ export class App extends React.Component<{}, AppState> {
@@ -1105,7 +1124,7 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
})); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
private addElementsFromPaste = (paste: string, x?: number, y?: number) => { |
|
|
|
|
private addElementsFromPaste = (paste: string) => { |
|
|
|
|
let parsedElements; |
|
|
|
|
try { |
|
|
|
|
parsedElements = JSON.parse(paste); |
|
|
|
|
@ -1117,19 +1136,47 @@ export class App extends React.Component<{}, AppState> {
@@ -1117,19 +1136,47 @@ export class App extends React.Component<{}, AppState> {
|
|
|
|
|
) { |
|
|
|
|
clearSelection(elements); |
|
|
|
|
|
|
|
|
|
if (x == null) x = 10 - this.state.scrollX; |
|
|
|
|
if (y == null) y = 10 - this.state.scrollY; |
|
|
|
|
let subCanvasX1 = Infinity; |
|
|
|
|
let subCanvasX2 = 0; |
|
|
|
|
let subCanvasY1 = Infinity; |
|
|
|
|
let subCanvasY2 = 0; |
|
|
|
|
|
|
|
|
|
const minX = Math.min(...parsedElements.map(element => element.x)); |
|
|
|
|
const minY = Math.min(...parsedElements.map(element => element.y)); |
|
|
|
|
const dx = x - minX; |
|
|
|
|
const dy = y - minY; |
|
|
|
|
|
|
|
|
|
const distance = (x: number, y: number) => { |
|
|
|
|
return Math.abs(x > y ? x - y : y - x); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
parsedElements.forEach(parsedElement => { |
|
|
|
|
const [x1, y1, x2, y2] = getElementAbsoluteCoords(parsedElement); |
|
|
|
|
subCanvasX1 = Math.min(subCanvasX1, x1); |
|
|
|
|
subCanvasY1 = Math.min(subCanvasY1, y1); |
|
|
|
|
subCanvasX2 = Math.max(subCanvasX2, x2); |
|
|
|
|
subCanvasY2 = Math.max(subCanvasY2, y2); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
const elementsCenterX = distance(subCanvasX1, subCanvasX2) / 2; |
|
|
|
|
const elementsCenterY = distance(subCanvasY1, subCanvasY2) / 2; |
|
|
|
|
|
|
|
|
|
const dx = |
|
|
|
|
this.state.cursorX - |
|
|
|
|
this.state.scrollX - |
|
|
|
|
CANVAS_WINDOW_OFFSET_LEFT - |
|
|
|
|
elementsCenterX; |
|
|
|
|
const dy = |
|
|
|
|
this.state.cursorY - |
|
|
|
|
this.state.scrollY - |
|
|
|
|
CANVAS_WINDOW_OFFSET_TOP - |
|
|
|
|
elementsCenterY; |
|
|
|
|
|
|
|
|
|
parsedElements.forEach(parsedElement => { |
|
|
|
|
const duplicate = duplicateElement(parsedElement); |
|
|
|
|
duplicate.x += dx; |
|
|
|
|
duplicate.y += dy; |
|
|
|
|
duplicate.x += dx - minX; |
|
|
|
|
duplicate.y += dy - minY; |
|
|
|
|
elements.push(duplicate); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.forceUpdate(); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|